#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>

#include <string>
#include <fstream>

#ifndef WIN32
#include <unistd.h>
#endif

#ifdef OS_UNIX
#include <sched.h>
#endif

#include <dboxpath.h>
#include <lock.h>
#include <logf.h>

//
// Locking Services
// 
// The types/name combinations used should be unique
//
// Todo: This locking does not work over NFS
//       This locking probably does not work with Win32
//       Automatic stale lock detection has a race condition
//

static void mklockname(std::string &filename,
  const char *type, const char *name, const char *boxname)
 {
  char tmpname[TEXTLEN], *tptr;
  char boxdir[DIRLEN];

  mkboxdir(boxdir, boxname);

  strmaxcpy(tmpname, name, TEXTLEN-1);
  
  for(tptr=tmpname; tptr[0]!=0; tptr++)
    if(tptr[0]==DIRCHAR[0])
      tptr[0]='_';

  getboxlockdir(filename, boxdir);
  filename+=type;
  filename+='.';
  filename+=tmpname;
 }

bool stale_lock(const std::string &filename)
 {
  std::string line1, line2, line3;
  char ch;
  std::ifstream infile(filename.c_str());

  infile >> line1;
  infile >> ch; // read newline
  infile >> line2;
  infile >> ch; // read newline
  infile >> line3;
  infile >> ch; // read newline

  logf.printf("stale_lock", "warning: stale lock %s detected: %s@%s module %s",
    filename.c_str(), line2.c_str(), line1.c_str(), line3.c_str());

  char tempstr[TEXTLEN*2];
  gethostname(tempstr, TEXTLEN-1);

  if(line1==tempstr) // local machine?
   {
    if(kill(atoi(line2.c_str()), 0)!=0 && errno==ESRCH) // process dead
     {
      logf.printf("stale_lock", "%s@%s dead, removing lock",
        line2.c_str(), line1.c_str());

      unlink(filename.c_str()); // race: another process might do the same
      return FALSE;
     }
    else // process not dead
     {
      logf.printf("stale_lock", "%s@%s is blocking resource %s",
        line2.c_str(), line1.c_str(), filename.c_str());
      return FALSE;
     }
   }
  else // remote machine
   {
    logf.printf("stale_lock", "warning: removing remote lock (unsafe)");
    unlink(filename.c_str());
    return FALSE;
   }  

  return TRUE;
 }

bool lock(const char *type, const char *name,
          const char *boxname, const char *module)
 {
  std::string filename;
  char tempstr[TEXTLEN*2];
  int handle, attempt=0;

  mklockname(filename, type, name, boxname);

  while(TRUE)
   {
    attempt++;

    handle=open(filename.c_str(), O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0600);

    if(handle<0) 
     {
      if(attempt>=2) 
        sleep(1);
      else
       {
        #ifdef WIN32
        sleep(1);
        #else
        #ifdef OS_WIN32
        sleep(1);
        #endif
        #endif
        sched_yield();
       }

      if(attempt>20)
       {
        if(stale_lock(filename)) return TRUE;
        attempt=0;
       }
     }
    else
      break;
   }

  gethostname(tempstr, TEXTLEN-1);
  write(handle, tempstr, strlen(tempstr));
  snprintf(tempstr, TEXTLEN, "\n%d\n", getpid());

  if(module!=NULL && module[0]!=0)
   {
    write(handle, module, strlen(module));
    strmaxcpy(tempstr, "\n", TEXTLEN-1);
    write(handle, tempstr, strlen(tempstr));
   }

  close(handle);

  return FALSE;
 }

bool unlock(const char *type, const char *name, const char *boxname)
 {
  std::string filename;
  mklockname(filename, type, name, boxname);
  unlink(filename.c_str());

  return FALSE;
 }



syntax highlighted by Code2HTML, v. 0.9.1