Index: trunk/zoo-project/zoo-kernel/service_internal.c
===================================================================
--- trunk/zoo-project/zoo-kernel/service_internal.c	(revision 889)
+++ trunk/zoo-project/zoo-kernel/service_internal.c	(revision 917)
@@ -2,5 +2,5 @@
  * Author : Gérald FENOY
  *
- * Copyright (c) 2009-2015 GeoLabs SARL
+ * Copyright (c) 2009-2018 GeoLabs SARL
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -31,14 +31,193 @@
 #include "service_internal.h"
 
-#ifndef TRUE
-#define TRUE 1
-#endif
-#ifndef FALSE
-#define FALSE -1
-#endif
-
+#ifdef WIN32
+// ref. https://docs.microsoft.com/en-us/windows/desktop/fileio/locking-and-unlocking-byte-ranges-in-files
+__inline int fcntl(int fd, int cmd, ...)
+{
+  va_list a;
+  va_start(a, cmd);
+  switch(cmd)
+    {
+    case F_SETLK:
+      {
+	HANDLE h = (HANDLE)_get_osfhandle(fd);
+	struct flock* l= va_arg(a, struct flock*);
+	OVERLAPPED sOverlapped;
+	sOverlapped.Offset = 0;
+	sOverlapped.OffsetHigh = 0;
+	switch(l->l_type)
+	  {
+	  case F_RDLCK:
+	    {
+	      if (!LockFileEx(h, LOCKFILE_FAIL_IMMEDIATELY, 0, l->l_len, 0, &sOverlapped)) 
+		{
+		  _set_errno(GetLastError() == ERROR_LOCK_VIOLATION ? EAGAIN : EBADF);
+		  return -1;
+		}
+	    }
+	    break;
+	  case F_WRLCK:
+	    {
+	      if (!LockFileEx(h, LOCKFILE_FAIL_IMMEDIATELY|LOCKFILE_EXCLUSIVE_LOCK, 0, l->l_len, 0, &sOverlapped))
+		{
+		  _set_errno(GetLastError() == ERROR_LOCK_VIOLATION ? EAGAIN : EBADF);
+		  return -1;
+		}
+	    }
+	    break;
+	  case F_UNLCK:
+	    {
+	      UnlockFileEx(h, 0, l->l_len, 0, &sOverlapped);
+	    }
+	    break;
+	  default:
+	    _set_errno(ENOTSUP);
+	    return -1;
+	  }
+      }
+      break;
+    default:
+      _set_errno(ENOTSUP);
+      return -1;
+    }
+  return 0;
+}
+#endif
 #define ERROR_MSG_MAX_LENGTH 1024
+
+/**
+ * Lock a file for read, write and upload.
+ * @param conf the main configuration maps
+ * @param filename the file to lock
+ * @param mode define access: 'r' for read, 'w' for write
+ * @return a new zooLock structure on sucess, NULL on failure 
+ */
+struct zooLock* lockFile(maps* conf,const char* filename,const char mode){
+  struct stat f_status;
+  int itn=0;
+  int s;
+  struct zooLock* myLock=(struct zooLock*)malloc(sizeof(struct flock)+sizeof(FILE*)+sizeof(char*));
+  int len=6;
+  char *myTemplate="%s.lock";
+  int res=-1;
+ retryLockFile:
+  myLock->filename=(char*)malloc((strlen(filename)+len)*sizeof(char));
+  sprintf(myLock->filename,myTemplate,filename);
+  s=stat(myLock->filename, &f_status);
+  if(s==0 && mode!='r'){
+    if(itn<ZOO_LOCK_MAX_RETRY){
+      itn++;
+#ifdef DEBUG
+      fprintf(stderr,"(%d) Wait for write lock on %s, tried %d times (sleep) ... \n",zGetpid(),myLock->filename,itn);
+      fflush(stderr);
+#endif
+      zSleep(5);
+      free(myLock->filename);
+      goto retryLockFile;
+    }else{
+      free(myLock->filename);
+      free(myLock);
+      return NULL;
+    }
+  }else{
+    char local_mode[3];
+    memset(local_mode,0,3);
+    if(mode=='w')
+      sprintf(local_mode,"%c+",mode);
+    else
+      sprintf(local_mode,"%c",mode);
+    myLock->lockfile=fopen(myLock->filename,local_mode);
+    char tmp[512];
+    sprintf(tmp,"%d",zGetpid());
+    if(myLock->lockfile==NULL){
+      myLock->lockfile=fopen(myLock->filename,"w+");
+      fwrite(tmp,sizeof(char),strlen(tmp),myLock->lockfile);
+      fflush(myLock->lockfile);
+      fclose(myLock->lockfile);
+      myLock->lockfile=fopen(myLock->filename,local_mode);
+    }/*else
+       fprintf(stderr,"%s %d %d\n",__FILE__,__LINE__,(myLock->lockfile==NULL));*/
+    if(mode!='r'){
+      fwrite(tmp,sizeof(char),strlen(tmp),myLock->lockfile);
+      fflush(myLock->lockfile);
+    }
+    int cnt=0;
+    if(mode=='r'){
+      myLock->lock.l_type = F_RDLCK;
+    }else
+      myLock->lock.l_type = F_WRLCK;
+    myLock->lock.l_whence = 0;
+    myLock->lock.l_start = 0;
+    myLock->lock.l_len = strlen(tmp)*sizeof(char);
+    while (true) {
+      if((res=fcntl(fileno(myLock->lockfile), F_SETLK, &(myLock->lock)))==-1 &&
+	 (errno==EAGAIN || errno==EACCES)){
+	  if(cnt >= ZOO_LOCK_MAX_RETRY){
+	    char message[51];	  
+	    sprintf(message,"Unable to get the lock after %d attempts.\n",cnt);
+	    setMapInMaps(conf,"lenv","message",message);
+	    fclose(myLock->lockfile);
+	    free(myLock->filename);
+	    free(myLock);
+	    return NULL;
+	  }
+#ifdef DEBUG
+	  fprintf(stderr,"(%d) Wait for lock on  %s, tried %d times ... \n",zGetpid(),myLock->filename,cnt);
+	  fflush(stderr);
+#endif
+	  zSleep(1);
+	  cnt++;
+	}else
+	   break;
+    }
+    if(res<0){
+      char *tmp;
+      if(errno==EBADF)
+	tmp="Either: the filedes argument is invalid; you requested a read lock but the filedes is not open for read access; or, you requested a write lock but the filedes is not open for write access.";
+      else
+	if(errno==EINVAL)
+	  tmp="Either the lockp argument doesn’t specify valid lock information, or the file associated with filedes doesn’t support locks.";
+	else
+	  tmp="The system has run out of file lock resources; there are already too many file locks in place.";
+#ifdef DEBUG
+      fprintf(stderr,"Unable to get the lock on %s due to the following error: %s\n",myLock->filename,tmp);
+#endif
+      return NULL;
+    }
+    return myLock;
+  }
+}
+
+/**
+ * Remove a lock.
+ * @param conf the main configuration maps
+ * @param s the zooLock structure
+ * @return 0 on success, -1 on failure.
+ */
+int unlockFile(maps* conf,struct zooLock* s){
+  int res=-1;
+  if(s!=NULL){
+    s->lock.l_type = F_UNLCK;
+    res=fcntl(fileno(s->lockfile), F_SETLK, &s->lock);
+    if(res==-1)
+      return res;
+    fclose(s->lockfile);
+#ifndef WIN32
+    // Check if there is any process locking a file and delete the lock if not.
+    s->lock.l_type = F_WRLCK;
+    if(fcntl(fileno(s->lockfile), F_GETLK, &s->lock)!=-1 && s->lock.l_type == F_UNLCK){
+#endif
+      zUnlink(s->filename);
+#ifndef WIN32
+    }
+#endif
+    free(s->filename);
+    free(s);
+  }
+  return res;
+}
+
 #ifndef RELY_ON_DB
-#include <dirent.h>
+#include "dirent.h"
 
 /**
@@ -148,6 +327,6 @@
 
     //FILE* f0 = fopen (fileName, "r");
-	// knut: open file in binary mode to avoid conversion of line endings (yielding extra bytes) on Windows platforms
-	FILE* f0 = fopen(fileName, "rb"); 
+    // knut: open file in binary mode to avoid conversion of line endings (yielding extra bytes) on Windows platforms
+    FILE* f0 = fopen(fileName, "rb"); 
     if(f0!=NULL){
       fseek (f0, 0, SEEK_END);
@@ -162,5 +341,4 @@
 	free(stat);
       }
-
       return tmps1;
     }
@@ -250,5 +428,5 @@
 		     * sizeof (char));
   sprintf (fbkpid, "%s/%s.status", r_inputs->value, usid->value);
-  unlink(fbkpid);
+  zUnlink(fbkpid);
   free(fbkpid);
 }
@@ -510,4 +688,5 @@
             }
         }
+	setMapInMaps(conf,"lenv","semaphore","Created");
     } else if (errno == EEXIST) { /* someone else got it first */
         int ready = 0;
@@ -529,5 +708,5 @@
 	      fprintf(stderr,"Retry to access the semaphore later ...\n");
 #endif
-	      zSleep(1);
+	      zSleep(1000);
             }
         }
@@ -540,4 +719,5 @@
 	  return -1;
         }
+	setMapInMaps(conf,"lenv","semaphore","Acquired");
     } else {
         return sem_id; /* error, check errno */
@@ -739,2 +919,29 @@
 }
 
+/**
+ * Check if file exists in specified folder
+ *
+ * @param dir the folder in which to search for file
+ * @param name the name of the file (not full path) 
+ * @return a character string with the full path [dir/name], or NULL if the file does not exist
+ *
+ * @attention Caller is responsible for applying free() to the returned pointer
+ */
+char* file_exists(const char* dir, const char* name) {
+	const char* d = (dir != NULL ? dir : ".");
+	if (name != NULL) {
+		size_t length = strlen(d) + strlen(name) + 2; // including file separator and \0 character
+		char* path = (char*)calloc(length, sizeof(char));
+		snprintf(path, length, "%s/%s", d, name);
+
+		struct stat buffer;
+		if (stat(path, &buffer) != 0) {
+			free(path);
+			path = NULL;
+		}
+		return path;
+	}
+	else {
+		return NULL;
+	}
+}
