1 /* $NetBSD: shlock.c,v 1.5 2000/10/11 14:46:18 is Exp $ */ 2 3 /* 4 ** Program to produce reliable locks for shell scripts. 5 ** Algorithm suggested by Peter Honeyman, January 1984, 6 ** in connection with HoneyDanBer UUCP. 7 ** 8 ** I tried extending this to handle shared locks in November 1987, 9 ** and ran into to some fundamental problems: 10 ** 11 ** Neither 4.3 BSD nor System V have an open(2) with locking, 12 ** so that you can open a file and have it locked as soon as 13 ** it's real; you have to make two system calls, and there's 14 ** a race... 15 ** 16 ** When removing dead process id's from a list in a file, 17 ** you need to truncate the file (you don't want to create a 18 ** new one; see above); unfortunately for the portability of 19 ** this program, only 4.3 BSD has ftruncate(2). 20 ** 21 ** Erik E. Fair <fair@ucbarpa.berkeley.edu>, November 8, 1987 22 ** 23 ** Extensions for UUCP style locks (i.e. pid is an int in the file, 24 ** rather than an ASCII string). Also fix long standing bug with 25 ** full file systems and temporary files. 26 ** 27 ** Erik E. Fair <fair@apple.com>, November 12, 1989 28 ** 29 ** ANSIfy the code somewhat to make gcc -Wall happy with the code. 30 ** Submit to NetBSD 31 ** 32 ** Erik E. Fair <fair@clock.org>, May 20, 1997 33 */ 34 35 #include <sys/types.h> 36 #include <sys/file.h> 37 #include <fcntl.h> /* Needed on hpux */ 38 #include <stdio.h> 39 #include <signal.h> 40 #include <errno.h> 41 #include <string.h> 42 #include <unistd.h> 43 #include <stdlib.h> 44 45 #define LOCK_SET 0 46 #define LOCK_FAIL 1 47 48 #define LOCK_GOOD 0 49 #define LOCK_BAD 1 50 51 #define FAIL (-1) 52 53 #define TRUE 1 54 #define FALSE 0 55 56 int Debug = FALSE; 57 char *Pname; 58 const char USAGE[] = "%s: USAGE: shlock -f file -p pid [-d][-u]\n"; 59 const char E_unlk[] = "%s: unlink(%s): %s\n"; 60 const char E_open[] = "%s: open(%s): %s\n"; 61 62 #define dprintf if (Debug) printf 63 64 /* 65 ** Prototypes to make the ANSI compilers happy 66 ** Didn't lint used to do type and argument checking? 67 ** (and wasn't that sufficient?) 68 */ 69 70 #ifdef __STDC__ 71 /* the following is in case you need to make the prototypes go away. */ 72 #define _P(x) x 73 74 char *xtmpfile _P((char *, pid_t, int)); 75 int p_exists _P((pid_t)); 76 int cklock _P((char *, int)); 77 int mklock _P((char *, pid_t, int)); 78 void bad_usage _P((void)); 79 int main _P((int, char **)); 80 #endif /* __STDC__ */ 81 82 /* 83 ** Create a temporary file, all ready to lock with. 84 ** The file arg is so we get the filename right, if he 85 ** gave us a full path, instead of using the current directory 86 ** which might not be in the same filesystem. 87 */ 88 char * 89 xtmpfile(file, pid, uucpstyle) 90 char *file; 91 pid_t pid; 92 int uucpstyle; 93 { 94 int fd; 95 int len; 96 char *cp, buf[BUFSIZ]; 97 static char tempname[BUFSIZ]; 98 99 sprintf(buf, "shlock%ld", (u_long)getpid()); 100 if ((cp = strrchr(strcpy(tempname, file), '/')) != (char *)NULL) { 101 *++cp = '\0'; 102 (void) strcat(tempname, buf); 103 } else 104 (void) strcpy(tempname, buf); 105 dprintf("%s: temporary filename: %s\n", Pname, tempname); 106 107 sprintf(buf, "%ld\n", (u_long)pid); 108 len = strlen(buf); 109 openloop: 110 if ((fd = open(tempname, O_RDWR|O_CREAT|O_EXCL, 0644)) < 0) { 111 switch(errno) { 112 case EEXIST: 113 dprintf("%s: file %s exists already.\n", 114 Pname, tempname); 115 if (unlink(tempname) < 0) { 116 fprintf(stderr, E_unlk, 117 Pname, tempname, strerror(errno)); 118 return((char *)NULL); 119 } 120 /* 121 ** Further profanity 122 */ 123 goto openloop; 124 default: 125 fprintf(stderr, E_open, 126 Pname, tempname, strerror(errno)); 127 return((char *)NULL); 128 } 129 } 130 131 /* 132 ** Write the PID into the temporary file before attempting to link 133 ** to the actual lock file. That way we have a valid lock the instant 134 ** the link succeeds. 135 */ 136 if (uucpstyle ? 137 (write(fd, &pid, sizeof(pid)) != sizeof(pid)) : 138 (write(fd, buf, len) < 0)) 139 { 140 fprintf(stderr, "%s: write(%s,%ld): %s\n", 141 Pname, tempname, (u_long)pid, strerror(errno)); 142 (void) close(fd); 143 if (unlink(tempname) < 0) { 144 fprintf(stderr, E_unlk, 145 Pname, tempname, strerror(errno)); 146 } 147 return((char *)NULL); 148 } 149 (void) close(fd); 150 return(tempname); 151 } 152 153 /* 154 ** Does the PID exist? 155 ** Send null signal to find out. 156 */ 157 int 158 p_exists(pid) 159 pid_t pid; 160 { 161 dprintf("%s: process %ld is ", Pname, (u_long)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", strerror(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 algorithm: 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 int 199 cklock(file, uucpstyle) 200 char *file; 201 int uucpstyle; 202 { 203 int fd = open(file, O_RDONLY); 204 ssize_t len; 205 pid_t pid; 206 char buf[BUFSIZ]; 207 208 dprintf("%s: checking extant lock <%s>\n", Pname, file); 209 if (fd < 0) { 210 if (errno != ENOENT) 211 fprintf(stderr, E_open, Pname, file, strerror(errno)); 212 return(TRUE); /* might or might not; conservatism */ 213 } 214 215 if (uucpstyle ? 216 ((len = read(fd, &pid, sizeof(pid))) != sizeof(pid)) : 217 ((len = read(fd, buf, sizeof(buf))) <= 0)) 218 { 219 close(fd); 220 dprintf("%s: lock file format error\n", Pname); 221 return(FALSE); 222 } 223 close(fd); 224 buf[len + 1] = '\0'; 225 return(p_exists(uucpstyle ? pid : atoi(buf))); 226 } 227 228 int 229 mklock(file, pid, uucpstyle) 230 char *file; 231 pid_t pid; 232 int uucpstyle; 233 { 234 char *tmp; 235 int retcode = FALSE; 236 237 dprintf("%s: trying lock <%s> for process %ld\n", Pname, file, 238 (u_long)pid); 239 if ((tmp = xtmpfile(file, pid, uucpstyle)) == (char *)NULL) 240 return(FALSE); 241 242 linkloop: 243 if (link(tmp, file) < 0) { 244 switch(errno) { 245 case EEXIST: 246 dprintf("%s: lock <%s> already exists\n", Pname, file); 247 if (cklock(file, uucpstyle)) { 248 dprintf("%s: extant lock is valid\n", Pname); 249 break; 250 } else { 251 dprintf("%s: lock is invalid, removing\n", 252 Pname); 253 if (unlink(file) < 0) { 254 fprintf(stderr, E_unlk, 255 Pname, file, strerror(errno)); 256 break; 257 } 258 } 259 /* 260 ** I hereby profane the god of structured programming, 261 ** Edsgar Dijkstra 262 */ 263 goto linkloop; 264 default: 265 fprintf(stderr, "%s: link(%s, %s): %s\n", 266 Pname, tmp, file, strerror(errno)); 267 break; 268 } 269 } else { 270 dprintf("%s: got lock <%s>\n", Pname, file); 271 retcode = TRUE; 272 } 273 if (unlink(tmp) < 0) { 274 fprintf(stderr, E_unlk, Pname, tmp, strerror(errno)); 275 } 276 return(retcode); 277 } 278 279 void 280 bad_usage() 281 { 282 fprintf(stderr, USAGE, Pname); 283 exit(LOCK_FAIL); 284 } 285 286 int 287 main(ac, av) 288 int ac; 289 char *av[]; 290 { 291 int x; 292 char *file = (char *)NULL; 293 pid_t pid = 0; 294 int uucpstyle = FALSE; /* indicating UUCP style locks */ 295 int only_check = TRUE; /* don't make a lock */ 296 297 Pname = ((Pname = strrchr(av[0], '/')) ? Pname + 1 : av[0]); 298 299 for(x = 1; x < ac; x++) { 300 if (av[x][0] == '-') { 301 switch(av[x][1]) { 302 case 'u': 303 uucpstyle = TRUE; 304 break; 305 case 'd': 306 Debug = TRUE; 307 break; 308 case 'p': 309 if (strlen(av[x]) > 2) { 310 pid = atoi(&av[x][2]); 311 } else { 312 if (++x >= ac) { 313 bad_usage(); 314 } 315 pid = atoi(av[x]); 316 } 317 only_check = FALSE; /* wants one */ 318 break; 319 case 'f': 320 if (strlen(av[x]) > 2) { 321 file = &av[x][2]; 322 } else { 323 if (++x >= ac) { 324 bad_usage(); 325 } 326 file = av[x]; 327 } 328 break; 329 default: 330 fprintf(stderr, USAGE, Pname); 331 exit(LOCK_FAIL); 332 } 333 } 334 } 335 336 if (file == (char *)NULL || (!only_check && pid <= 0)) { 337 bad_usage(); 338 } 339 340 if (only_check) { 341 exit(cklock(file, uucpstyle) ? LOCK_GOOD : LOCK_BAD); 342 } 343 344 exit(mklock(file, pid, uucpstyle) ? LOCK_SET : LOCK_FAIL); 345 } 346