Listing 1: rlock

#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/param.h>


/*
 * Quick and dirty basename for machines without such.
 */
char* basename(path)
char* path;
{
  int len;
  char* ptr;

  len=strlen(path);
  ptr=path+len-1;
  for (;ptr != path;ptr--)
    if (*ptr == '/')
    {
      return(ptr+1);
    }
  return(ptr);
}

/*
 * Quick and dirty dirname for machines without such.
 */
char* dirname(path)
char* path;
{
  static char* dot=".";
  int len;
  char* ptr;

  len=strlen(path);
  ptr=path+len-1;
  for (;ptr != path;ptr--)
    if (*ptr == '/')
    {
      *ptr='\0';
      return(path);
    }
  return (dot);
}

/*
 * Perform SCCS-like (z-file) file locking.
 *
 * Attempts to create a file with the parent process
 * id's pid in it.  If the file exists and pid exists,
 * it can wait on the file until unlocked or test
 * if -q.  If the file exists and pid does not, the
 * file is removed and new file is created with
 * current parent process id.
 *
 */

static int dbg=0;
static char* pgm;

/* Location of remote shell program */
#define RSHCMD                "/usr/bin/rsh"

/* Maximum length of hostnames */
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN        64
#endif

/* Maximum number of characters for ASCII pid */
#define PIDBUFFERSIZE         10

/* Current host */
static char hostname[MAXHOSTNAMELEN+1];
/* Locking host from lock file */
static char lockinghost[MAXHOSTNAMELEN+1];

#define DBGMSG(msg,parm) \
  { \
    if (dbg) \
      {\
        fprintf(stderr,"DBG %s: ",__FILE__);\
        fprintf(stderr,msg,parm);\
      }\
  }

static char pidbuffer[PIDBUFFERSIZE+1]="9876543210";

static void usage()
{
  /* creates a Z.filename for each filename listed */

  fprintf(stderr, "Usage: %s [-b|-z] [-p prefix]", pgm);
  fprintf(stderr, "[-q | -s seconds] filename...\n");
  exit(-1);
}

static void lockerror(name,msg)
char* name;
char* msg;
{
  fprintf(stderr, "%s: %s %s\n",pgm,name,msg);
  exit(-1);
}

static void writepid(fd, locktype)
int fd;
char locktype;
{
  pid_t ppid;

  ppid=getppid();
  DBGMSG("creating lock file for pid=[%d]\n",ppid);
  DBGMSG("creating lock file for hostname=[%s]\n",
    hostname);

  /* Write locktype to lock file if not -z */
  if (locktype != 'Z')
    write(fd,&locktype,1);

  if (locktype != 'A') /* Binary lock */
    write(fd,&ppid,sizeof(pid_t));
  else /* ASCII lock */
  {
    sprintf(pidbuffer,"%d",ppid);
    write(fd,pidbuffer,PIDBUFFERSIZE);
  }

  write(fd,hostname,MAXHOSTNAMELEN);
}

void main(argc, argv)
int argc;
char* argv[];
{
  int fd;
  int delay=2;
  int waitonlock=1;
  int errflg=0;
  int rc=0;
  int grc=0;
  int filenumber=0;
  int c;
  char locktype='A';

  pid_t ppid;

  char* debug;
  char* delaystr;
  char pathName[1024];
  char dirName[1024];
  char syscmd[1024];
  char prefix[80];

  extern char *optarg;
    extern int optind;

  pgm=argv[0];
  strcpy(prefix,"Z.");

  while ((c = getopt(argc, argv, "bzqs:p:")) != EOF)
  {
    switch (c)
    {
      case 'b':
        locktype='B';
        break;
      case 'z':
        locktype='Z';
        break;
      case 'q':
        waitonlock=0;
        break;
      case 's':
        delaystr = optarg;
        delay=atoi(delaystr);
        break;
      case 'p':
        strcpy(prefix,optarg);
        break;
      case '?':
        errflg++;
    }
  }

  if (errflg || argc < 2 )
    usage(argv[0]);

  if (gethostname(hostname,MAXHOSTNAMELEN))
    lockerror("gethostname,",
      "unable to obtain hostname");

  debug=getenv("LOCKDBG");
  if (debug)
    dbg=atoi(debug);

  for (;optind<argc;optind++)
  {
    filenumber++;
    /* construct Z.filename from argument */

    strcpy(dirName,argv[optind]);
    sprintf(pathName,"%s/%s%s",dirname(dirName),
      prefix,basename(argv[optind]));

    /*
     * loop continually until file lock established
     * unless !waitonlock
     */

    while (1)
    {
      rc=0;
      DBGMSG("try to create file %s\n", pathName);

      if ((fd=open(pathName,O_CREAT|O_EXCL|O_RDWR,
        0644)) == -1)
      {
        DBGMSG("file exists, try reading it\n","");

        /* file exists */
        if ((fd=open(pathName,O_RDONLY)) == -1)
          lockerror(pathName,"permission denied");
  
        /* read pid number */
        DBGMSG("reading pid\n","");

        /*
         * read locktype (A=ASCII, B=binary if not
         * -z option.
         */
        if (locktype != 'Z' && !read(fd,&locktype,1))
        {
          DBGMSG("reading locktype\n","");
          DBGMSG(pathName,"empty file?");
          if (unlink(pathName) == -1)
            lockerror(pathName,"permission denied");
          continue;
        }

        if (locktype != 'A') /* Binary type */
        {
          DBGMSG("locktype is Binary\n","");
          if (!read(fd,&ppid,sizeof(pid_t)))
          {
            DBGMSG(pathName,"empty file?");
            if (unlink(pathName) == -1)
              lockerror(pathName,"permission denied");
            continue;
          }
        }
        else /* ASCII type */
        {
          DBGMSG("locktype is ASCII\n","");
          if (!read(fd,pidbuffer,PIDBUFFERSIZE))
          {
            DBGMSG(pathName,"empty file?");
            if (unlink(pathName) == -1)
              lockerror(pathName,"permission denied");
            continue;
          }
          else
            ppid=atoi(pidbuffer);
        }

        DBGMSG("reading locking hostname\n","");

        if (!read(fd,lockinghost,MAXHOSTNAMELEN))
        {
          DBGMSG(pathName,"empty file?");
          if (unlink(pathName) == -1)
            lockerror(pathName,"permission denied");
          continue;
        }

        /* check for pid */

        DBGMSG("\tpid=[%d]\n",ppid);
        DBGMSG("\tlockinghost=[%s]\n",lockinghost);
  
        if (!strcmp(hostname,lockinghost) && dbg != 2)
        {
          /* determine if local process still active */
          if (kill(ppid,0) == -1)
          {
            if (errno == ESRCH)
            {
              /* remove file */
              DBGMSG("pid gone!, remove file!\n","");

              if (unlink(pathName) == -1)
                lockerror(pathName,
                  "permission denied");
              else
                rc=1;
            }
          }
          else
          {
            DBGMSG("\tprocess still active\n","");
          }
        }
        else
        {
          /* determine if remote process exists */
          DBGMSG("\tFOREIGN HOST\n","");
          sprintf(syscmd,
            "rc=`%s %s \"kill -0 %d 2>/dev/null
            echo \\\\$?\"`;exit $rc",
            RSHCMD, lockinghost, ppid);
          DBGMSG("\tissuing %s\n",syscmd);
          rc=system(syscmd);
          rc=rc>>8;
          DBGMSG("\tremote process status=[%d]\n",rc);
          if (rc)
          {
            /* remove file */
            DBGMSG("pid gone!, remove file!\n","");

            if (unlink(pathName) == -1)
              lockerror(pathName,"permission denied");
          }
          else
          {
            DBGMSG("\tprocess still active\n","");
          }
        }
          
        if (rc) /* We removed the lock! Immediately loop. */
        {
          close(fd);
          continue;
        }

        if (waitonlock)
        {
          /* sleep for delay */
          DBGMSG("sleep for %d seconds\n",delay);
          sleep(delay);
          close(fd);
        }
        else
        {
          if (!grc)
            grc=filenumber;
          break;
        }
      }
      else
      {
        /* file is new */
        writepid(fd,locktype);
        close(fd);
        break;
      }
    }
    close(fd);
  }
  exit(grc);
}

