1: /*
   2: ** Program to produce reliable locks for shell scripts.
   3: ** Algorithmn suggested by Peter Honeyman, January 1984,
   4: ** in connection with HoneyDanBer UUCP.
   5: **
   6: ** I tried extending this to handle shared locks in November 1987,
   7: ** and ran into to some fundamental problems:
   8: **
   9: **	Neither 4.3 BSD nor System V have an open(2) with locking,
  10: **	so that you can open a file and have it locked as soon as
  11: **	it's real; you have to make two system calls, and there's
  12: **	a race...
  13: **
  14: **	When removing dead process id's from a list in a file,
  15: **	you need to truncate the file (you don't want to create a
  16: **	new one; see above); unfortunately for the portability of
  17: **	this program, only 4.3 BSD has ftruncate(2).
  18: **
  19: ** Erik E. Fair <fair@ucbarpa.berkeley.edu>, November 8, 1987
  20: */
  21: 
  22: #include <stdio.h>
  23: #include <sys/types.h>
  24: #include <fcntl.h>          /* Needed on hpux */
  25: #include <sys/file.h>
  26: #include <errno.h>
  27: 
  28: #define LOCK_SET    0
  29: #define LOCK_FAIL   1
  30: 
  31: #define FAIL        (-1)
  32: 
  33: #define TRUE    1
  34: #define FALSE   0
  35: 
  36: #ifdef USG
  37: #define index   strchr
  38: #define rindex  strrchr
  39: #endif
  40: 
  41: int Debug = FALSE;
  42: char    *Pname;
  43: char    *USAGE = "%s: USAGE: shlock -f file -p pid [-d]\n";
  44: char    *errmsg();
  45: char    *xtmpfile();
  46: 
  47: #define dprintf if (Debug) printf
  48: 
  49: extern  int errno;
  50: extern  char    *rindex();
  51: extern  char    *strcpy();
  52: extern  char    *strcat();
  53: 
  54: main(ac, av)
  55: int ac;
  56: char    *av[];
  57: {
  58:     register int    x;
  59:     char    *file;
  60:     int pid;
  61: 
  62:     Pname = ((Pname = rindex(av[0], '/')) ? Pname + 1 : av[0]);
  63: 
  64:     for(x = 1; x < ac; x++) {
  65:         if (av[x][0] == '-') {
  66:             switch(av[x][1]) {
  67:             case 'd':
  68:                 Debug = TRUE;
  69:                 break;
  70:             case 'p':
  71:                 if (strlen(av[x]) > 2) {
  72:                     pid = atoi(&av[x][2]);
  73:                 } else {
  74:                     pid = atoi(av[++x]);
  75:                 }
  76:                 break;
  77:             case 'f':
  78:                 if (strlen(av[x]) > 2) {
  79:                     file = &av[x][2];
  80:                 } else {
  81:                     file = av[++x];
  82:                 }
  83:                 break;
  84:             default:
  85:                 fprintf(stderr, USAGE, Pname);
  86:                 exit(LOCK_FAIL);
  87:             }
  88:         }
  89:     }
  90:     if (pid == 0 || file == (char *)NULL) {
  91:         fprintf(stderr, USAGE, Pname);
  92:         exit(LOCK_FAIL);
  93:     }
  94: 
  95:     exit(mklock(file, pid) ? LOCK_SET : LOCK_FAIL);
  96: }
  97: 
  98: char *
  99: errmsg(n)
 100: register int    n;
 101: {
 102:     return(strerror(n));
 103: }
 104: 
 105: mklock(file, pid)
 106: char    *file;
 107: int pid;
 108: {
 109:     register char   *tmp;
 110:     register int    retcode = FALSE;
 111:     char    *e_unlk = "%s: unlink(%s): %s\n";
 112: 
 113:     dprintf("%s: trying lock <%s> for process %d\n", Pname, file, pid);
 114:     if ((tmp = xtmpfile(file, pid)) == (char *)NULL)
 115:         return(FALSE);
 116: 
 117: linkloop:
 118:     if (link(tmp, file) < 0) {
 119:         switch(errno) {
 120:         case EEXIST:
 121:             dprintf("%s: lock <%s> already exists\n", Pname, file);
 122:             if (cklock(file)) {
 123:                 dprintf("%s: extant lock is valid\n", Pname);
 124:                 break;
 125:             } else {
 126:                 dprintf("%s: lock is invalid, removing\n",
 127:                     Pname);
 128:                 if (unlink(file) < 0) {
 129:                     fprintf(stderr, e_unlk,
 130:                         Pname, file, errmsg(errno));
 131:                     break;
 132:                 }
 133:             }
 134:             /*
 135: 			** I hereby profane the god of structured programming,
 136: 			** Edsgar Dijkstra
 137: 			*/
 138:             goto linkloop;
 139:         default:
 140:             fprintf(stderr, "%s: link(%s, %s): %s\n",
 141:                 Pname, tmp, file, errmsg(errno));
 142:             break;
 143:         }
 144:     } else {
 145:         dprintf("%s: got lock <%s>\n", Pname, file);
 146:         retcode = TRUE;
 147:     }
 148:     if (unlink(tmp) < 0) {
 149:         fprintf(stderr, e_unlk, Pname, tmp, errmsg(errno));
 150:     }
 151:     return(retcode);
 152: }
 153: 
 154: /*
 155: ** Does the PID exist?
 156: ** Send null signal to find out.
 157: */
 158: p_exists(pid)
 159: int pid;
 160: {
 161:     dprintf("%s: process %d is ", Pname, pid);
 162:     if (pid <= 0) {
 163:         dprintf("invalid\n");
 164:         return(FALSE);
 165:     }
 166:     if (kill(pid, 0) < 0) {
 167:         switch(errno) {
 168:         case ESRCH:
 169:             dprintf("dead\n");
 170:             return(FALSE);  /* pid does not exist */
 171:         case EPERM:
 172:             dprintf("alive\n");
 173:             return(TRUE);   /* pid exists */
 174:         default:
 175:             dprintf("state unknown: %s\n", errmsg(errno));
 176:             return(TRUE);   /* be conservative */
 177:         }
 178:     }
 179:     dprintf("alive\n");
 180:     return(TRUE);   /* pid exists */
 181: }
 182: 
 183: /*
 184: ** Check the validity of an existing lock file.
 185: **
 186: **	Read the PID out of the lock
 187: **	Send a null signal to determine whether that PID still exists
 188: **	Existence (or not) determines the validity of the lock.
 189: **
 190: **	Two bigs wins to this algorithmn:
 191: **
 192: **	o	Locks do not survive crashes of either the system or the
 193: **			application by any appreciable period of time.
 194: **
 195: **	o	No clean up to do if the system or application crashes.
 196: **
 197: */
 198: 
 199: cklock(file)
 200: char    *file;
 201: {
 202:     register int    fd = open(file, O_RDONLY);
 203:     register int    len;
 204:     char    buf[BUFSIZ];
 205: 
 206:     dprintf("%s: checking extant lock <%s>\n", Pname, file);
 207:     if (fd < 0) {
 208:         fprintf(stderr,"%s: open(%s): %s\n", Pname, file, errmsg(errno));
 209:         return(TRUE);   /* might or might not; conservatism */
 210:     }
 211: 
 212:     if ((len = read(fd, buf, sizeof(buf))) <= 0) {
 213:         close(fd);
 214:         dprintf("%s: lock file format error\n", Pname);
 215:         return(FALSE);
 216:     }
 217:     close(fd);
 218:     buf[len + 1] = '\0';
 219:     return(p_exists(atoi(buf)));
 220: }
 221: 
 222: /*
 223: ** Create a temporary file, all ready to lock with.
 224: ** The file arg is so we get the filename right, if he
 225: ** gave us a full path, instead of using the current directory
 226: ** which might not be in the same filesystem.
 227: */
 228: char *
 229: xtmpfile(file, pid)
 230: char    *file;
 231: int pid;
 232: {
 233:     register int    fd;
 234:     register int    len;
 235:     char    *cp, buf[BUFSIZ];
 236:     static char tempname[BUFSIZ];
 237: 
 238:     sprintf(buf, "shlock%d", getpid());
 239:     if ((cp = rindex(strcpy(tempname, file), '/')) != (char *)NULL) {
 240:         *++cp = '\0';
 241:         (void) strcat(tempname, buf);
 242:     } else
 243:         (void) strcpy(tempname, buf);
 244:     dprintf("%s: temporary filename: %s\n", Pname, tempname);
 245: 
 246:     sprintf(buf, "%d\n", pid);
 247:     len = strlen(buf);
 248: openloop:
 249:     if ((fd = open(tempname, O_RDWR|O_CREAT|O_EXCL, 0644)) < 0) {
 250:         switch(errno) {
 251:         case EEXIST:
 252:             dprintf("%s: file %s exists already.\n",
 253:                 Pname, tempname);
 254:             if (unlink(tempname) < 0) {
 255:                 fprintf(stderr, "%s: unlink(%s): %s\n",
 256:                     Pname, tempname, errmsg(errno));
 257:                 return((char *)NULL);
 258:             }
 259:             /*
 260: 			** Further profanity
 261: 			*/
 262:             goto openloop;
 263:         default:
 264:             fprintf(stderr, "%s: open(%s): %s\n",
 265:                 Pname, tempname, errmsg(errno));
 266:             return((char *)NULL);
 267:         }
 268:     }
 269: 
 270:     /*
 271: 	** Write the PID into the temporary file before attempting to link
 272: 	** to the actual lock file. That way we have a valid lock the instant
 273: 	** the link succeeds.
 274: 	*/
 275:     if (write(fd, buf, len) < 0) {
 276:         fprintf(stderr, "%s: write(%s,%d): %s\n",
 277:             Pname, tempname, pid, errmsg(errno));
 278:         (void) close(fd);
 279:         return((char *)NULL);
 280:     }
 281:     (void) close(fd);
 282:     return(tempname);
 283: }

Defined functions

cklock defined in line 199; used 1 times
errmsg defined in line 98; used 14 times
main defined in line 54; never used
mklock defined in line 105; used 1 times
  • in line 95
p_exists defined in line 158; used 1 times
xtmpfile defined in line 228; used 2 times

Defined variables

Debug defined in line 41; used 2 times
Pname defined in line 42; used 23 times
USAGE defined in line 43; used 2 times

Defined macros

FAIL defined in line 31; never used
FALSE defined in line 34; used 6 times
LOCK_FAIL defined in line 29; used 3 times
LOCK_SET defined in line 28; used 1 times
  • in line 95
TRUE defined in line 33; used 6 times
dprintf defined in line 47; used 15 times
index defined in line 37; never used
rindex defined in line 38; used 3 times
Last modified: 1996-03-22
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 4182
Valid CSS Valid XHTML 1.0 Strict