/************************************************************************
* secure exclusive creat/lock v1.6 1995/05/05 *
* (works even across NFS, which O_EXCL does *not*) *
* *
* Created 1990-1995, S.R. van den Berg, The Netherlands *
*
[email protected] *
*
[email protected] *
* *
* This file is donated to the public domain. *
* *
* Usage: int xcreat(const char*filename,mode_t mode) *
* *
* returns 0:success -1:lock busy *
* -2:parameter error or out of memory *
* *
* sets errno on failure *
* *
* To remove a `lockfile', simply unlink it. *
* *
************************************************************************/
#define HOSTNAMElen 9 /* significant characters for hostname */
/*#define NOuname /* uncomment if uname is not available */
/*#define NOstrpbrk /* uncomment if strpbrk is not available */
/*#define strchr(s,c) index(s,c) /* uncomment if strchr is not available */
#define const /* can be undefined for ANSI compilers */
static const char dirsep[]="/"; /* directory separators */
#include <unistd.h> /* open() close() link() unlink()
getpid() */
#include <fcntl.h> /* O_WRONLY O_CREAT O_EXCL */
#include <stdlib.h> /* malloc() free() */
#include <string.h> /* strncpy() strcat() strpbrk() */
#include <sys/stat.h> /* stat() struct stat */
#ifndef NOuname
#include <sys/utsname.h> /* uname() struct utsname */
#endif
#include <errno.h>
/************************************************************************
* Only edit below this line if you *think* you know what you are doing *
************************************************************************/
#ifndef O_SYNC
#define O_SYNC 0
#endif
#ifndef O_CREAT
#define copen(path,type,mode) creat(path,mode)
#else
#define copen(path,type,mode) open(path,type,mode)
#endif
#define log(string) /* should log string to stderr */
#define UNIQ_PREFIX '_'
#define charsSERIAL 4
#define UNIQnamelen (1+charsSERIAL+HOSTNAMElen+1)
#define bitsSERIAL (6*charsSERIAL)
#define maskSERIAL ((1L<<bitsSERIAL)-1)
#define rotbSERIAL 2
#define irotbSERIAL (1L<<bitsSERIAL-rotbSERIAL)
#define mrotbSERIAL ((maskSERIAL&irotbSERIAL-1)+irotbSERIAL)
extern errno;
#ifdef NOstrpbrk
char*strpbrk(st,del)const char*const st,*del;
{ const char*f=0,*t;
for(f=0;*del;)
if((t=strchr(st,*del++))&&(!f||t<f))
f=t;
return(char*)f;
}
#endif
static const char*hostname()
{ static char name[HOSTNAMElen+1];
#ifdef NOuname
gethostname(name,HOSTNAMElen+1);
#else
struct utsname names;
uname(&names);strncpy(name,names.nodename,HOSTNAMElen);
#endif
name[HOSTNAMElen]='\0';return name;
}
static ultoan(val,dest)unsigned long val;char*dest; /* convert to a number */
{ register i; /* within the set [0-9A-Za-z-_] */
do
{ i=val&0x3f;
*dest++=i+(i<10?'0':i<10+26?'A'-10:i<10+26+26?'a'-10-26:
i==10+26+26?'-'-10-26-26:'_'-10-26-27);
}
while(val>>=6);
*dest='\0';
}
static unique(full,p,mode)const char*const full;char*const p;const mode_t mode;
{ unsigned long retry=mrotbSERIAL;int i; /* create unique file name */
do
{ ultoan(maskSERIAL&(retry-=irotbSERIAL)+(long)getpid(),p+1);*p=UNIQ_PREFIX;
strcat(p,hostname());
}
while(0>(i=copen(full,O_WRONLY|O_CREAT|O_EXCL|O_SYNC,mode))&&errno==EEXIST&&
retry); /* casually check if it already exists (highly unlikely) */
if(i<0)
{ log("Error while writing to \"");log(full);log("\"\n");return 0;
}
close(i);return 1;
}
/* rename MUST fail if already existent */
static myrename(old,newn)const char*const old,*const newn;
{ int i,serrno;struct stat stbuf;
if(!link(old,newn))
{ unlink(old);
return 0;
}
serrno=errno;i=stat(old,&stbuf);unlink(old);errno=serrno;
return stbuf.st_nlink==2?i:-1;
}
/* an NFS secure exclusive file open */
xcreat(name,mode)char*name;const mode_t mode;
{ char*p,*q;int j= -2,i;
for(q=name;p=strpbrk(q,dirsep);q=p+1); /* find last DIRSEP */
if(!(p=malloc((i=q-name)+UNIQnamelen))) /* out of memory */
return j;
strncpy(p,name,i);
if(unique(p,p+i,mode))
j=myrename(p,name); /* try and rename it, fails if nonexclusive */
free(p);return j;
}