Cover V04, I04
Article
Listing 1
Listing 2
Sidebar 1

jul95.tar


Listing 1: rlock--A distributed file locking utility

#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);
}