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: }