1*0a6a1f1dSLionel Sambuc /* $NetBSD: shlock.c,v 1.13 2015/04/10 09:34:43 tron Exp $ */
2152a1565SThomas Cort
3152a1565SThomas Cort /*-
4152a1565SThomas Cort * Copyright (c) 2006 The NetBSD Foundation, Inc.
5152a1565SThomas Cort * All rights reserved.
6152a1565SThomas Cort *
7152a1565SThomas Cort * This code is derived from software contributed to The NetBSD Foundation
8152a1565SThomas Cort * by Erik E. Fair.
9152a1565SThomas Cort *
10152a1565SThomas Cort * Redistribution and use in source and binary forms, with or without
11152a1565SThomas Cort * modification, are permitted provided that the following conditions
12152a1565SThomas Cort * are met:
13152a1565SThomas Cort * 1. Redistributions of source code must retain the above copyright
14152a1565SThomas Cort * notice, this list of conditions and the following disclaimer.
15152a1565SThomas Cort * 2. Redistributions in binary form must reproduce the above copyright
16152a1565SThomas Cort * notice, this list of conditions and the following disclaimer in the
17152a1565SThomas Cort * documentation and/or other materials provided with the distribution.
18152a1565SThomas Cort *
19152a1565SThomas Cort * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20152a1565SThomas Cort * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21152a1565SThomas Cort * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22152a1565SThomas Cort * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23152a1565SThomas Cort * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24152a1565SThomas Cort * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25152a1565SThomas Cort * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26152a1565SThomas Cort * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27152a1565SThomas Cort * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28152a1565SThomas Cort * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29152a1565SThomas Cort * POSSIBILITY OF SUCH DAMAGE.
30152a1565SThomas Cort */
31152a1565SThomas Cort
32152a1565SThomas Cort /*
33152a1565SThomas Cort ** Program to produce reliable locks for shell scripts.
34152a1565SThomas Cort ** Algorithm suggested by Peter Honeyman, January 1984,
35152a1565SThomas Cort ** in connection with HoneyDanBer UUCP.
36152a1565SThomas Cort **
37152a1565SThomas Cort ** I tried extending this to handle shared locks in November 1987,
38152a1565SThomas Cort ** and ran into to some fundamental problems:
39152a1565SThomas Cort **
40152a1565SThomas Cort ** Neither 4.3 BSD nor System V have an open(2) with locking,
41152a1565SThomas Cort ** so that you can open a file and have it locked as soon as
42152a1565SThomas Cort ** it's real; you have to make two system calls, and there's
43152a1565SThomas Cort ** a race...
44152a1565SThomas Cort **
45152a1565SThomas Cort ** When removing dead process id's from a list in a file,
46152a1565SThomas Cort ** you need to truncate the file (you don't want to create a
47152a1565SThomas Cort ** new one; see above); unfortunately for the portability of
48152a1565SThomas Cort ** this program, only 4.3 BSD has ftruncate(2).
49152a1565SThomas Cort **
50152a1565SThomas Cort ** Erik E. Fair <fair@ucbarpa.berkeley.edu>, November 8, 1987
51152a1565SThomas Cort **
52152a1565SThomas Cort ** Extensions for UUCP style locks (i.e. pid is an int in the file,
53152a1565SThomas Cort ** rather than an ASCII string). Also fix long standing bug with
54152a1565SThomas Cort ** full file systems and temporary files.
55152a1565SThomas Cort **
56152a1565SThomas Cort ** Erik E. Fair <fair@apple.com>, November 12, 1989
57152a1565SThomas Cort **
58152a1565SThomas Cort ** ANSIfy the code somewhat to make gcc -Wall happy with the code.
59152a1565SThomas Cort ** Submit to NetBSD
60152a1565SThomas Cort **
61152a1565SThomas Cort ** Erik E. Fair <fair@clock.org>, May 20, 1997
62152a1565SThomas Cort */
63152a1565SThomas Cort
64152a1565SThomas Cort #include <sys/cdefs.h>
65152a1565SThomas Cort
66152a1565SThomas Cort #ifndef lint
67*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: shlock.c,v 1.13 2015/04/10 09:34:43 tron Exp $");
68152a1565SThomas Cort #endif
69152a1565SThomas Cort
70152a1565SThomas Cort #include <sys/types.h>
71152a1565SThomas Cort #include <sys/file.h>
72152a1565SThomas Cort #include <fcntl.h> /* Needed on hpux */
73152a1565SThomas Cort #include <stdio.h>
74152a1565SThomas Cort #include <signal.h>
75152a1565SThomas Cort #include <errno.h>
76152a1565SThomas Cort #include <string.h>
77152a1565SThomas Cort #include <unistd.h>
78152a1565SThomas Cort #include <stdlib.h>
79152a1565SThomas Cort
80152a1565SThomas Cort #define LOCK_SET 0
81152a1565SThomas Cort #define LOCK_FAIL 1
82152a1565SThomas Cort
83152a1565SThomas Cort #define LOCK_GOOD 0
84152a1565SThomas Cort #define LOCK_BAD 1
85152a1565SThomas Cort
86152a1565SThomas Cort #define FAIL (-1)
87152a1565SThomas Cort
88152a1565SThomas Cort #define TRUE 1
89152a1565SThomas Cort #define FALSE 0
90152a1565SThomas Cort
91152a1565SThomas Cort static int Debug = FALSE;
92152a1565SThomas Cort static char *Pname;
93152a1565SThomas Cort static const char USAGE[] = "%s: USAGE: %s [-du] [-p PID] -f file\n";
94152a1565SThomas Cort static const char E_unlk[] = "%s: unlink(%s): %s\n";
95152a1565SThomas Cort static const char E_open[] = "%s: open(%s): %s\n";
96152a1565SThomas Cort
97152a1565SThomas Cort #define dprintf if (Debug) printf
98152a1565SThomas Cort
99152a1565SThomas Cort /*
100152a1565SThomas Cort ** Prototypes to make the ANSI compilers happy
101152a1565SThomas Cort ** Didn't lint used to do type and argument checking?
102152a1565SThomas Cort ** (and wasn't that sufficient?)
103152a1565SThomas Cort */
104152a1565SThomas Cort
105152a1565SThomas Cort /* the following is in case you need to make the prototypes go away. */
106152a1565SThomas Cort static char *xtmpfile(char *, pid_t, int);
107152a1565SThomas Cort static int p_exists(pid_t);
108152a1565SThomas Cort static int cklock(char *, int);
109152a1565SThomas Cort static int mklock(char *, pid_t, int);
110152a1565SThomas Cort __dead static void bad_usage(void);
111152a1565SThomas Cort
112152a1565SThomas Cort /*
113152a1565SThomas Cort ** Create a temporary file, all ready to lock with.
114152a1565SThomas Cort ** The file arg is so we get the filename right, if he
115152a1565SThomas Cort ** gave us a full path, instead of using the current directory
116152a1565SThomas Cort ** which might not be in the same filesystem.
117152a1565SThomas Cort */
118152a1565SThomas Cort static char *
xtmpfile(char * file,pid_t pid,int uucpstyle)119152a1565SThomas Cort xtmpfile(char *file, pid_t pid, int uucpstyle)
120152a1565SThomas Cort {
121152a1565SThomas Cort int fd;
122152a1565SThomas Cort int len;
123152a1565SThomas Cort char *cp, buf[BUFSIZ];
124152a1565SThomas Cort static char tempname[BUFSIZ];
125152a1565SThomas Cort
126*0a6a1f1dSLionel Sambuc sprintf(buf, "shlock%ld", (long)getpid());
127152a1565SThomas Cort if ((cp = strrchr(strcpy(tempname, file), '/')) != NULL) {
128152a1565SThomas Cort *++cp = '\0';
129152a1565SThomas Cort (void) strcat(tempname, buf);
130152a1565SThomas Cort } else
131152a1565SThomas Cort (void) strcpy(tempname, buf);
132152a1565SThomas Cort dprintf("%s: temporary filename: %s\n", Pname, tempname);
133152a1565SThomas Cort
134*0a6a1f1dSLionel Sambuc sprintf(buf, "%ld\n", (long)pid);
135152a1565SThomas Cort len = strlen(buf);
136152a1565SThomas Cort openloop:
137152a1565SThomas Cort if ((fd = open(tempname, O_RDWR|O_CREAT|O_EXCL, 0644)) < 0) {
138152a1565SThomas Cort switch(errno) {
139152a1565SThomas Cort case EEXIST:
140152a1565SThomas Cort dprintf("%s: file %s exists already.\n",
141152a1565SThomas Cort Pname, tempname);
142152a1565SThomas Cort if (unlink(tempname) < 0) {
143152a1565SThomas Cort fprintf(stderr, E_unlk,
144152a1565SThomas Cort Pname, tempname, strerror(errno));
145152a1565SThomas Cort return (NULL);
146152a1565SThomas Cort }
147152a1565SThomas Cort /*
148152a1565SThomas Cort ** Further profanity
149152a1565SThomas Cort */
150152a1565SThomas Cort goto openloop;
151152a1565SThomas Cort default:
152152a1565SThomas Cort fprintf(stderr, E_open,
153152a1565SThomas Cort Pname, tempname, strerror(errno));
154152a1565SThomas Cort return (NULL);
155152a1565SThomas Cort }
156152a1565SThomas Cort }
157152a1565SThomas Cort
158152a1565SThomas Cort /*
159152a1565SThomas Cort ** Write the PID into the temporary file before attempting to link
160152a1565SThomas Cort ** to the actual lock file. That way we have a valid lock the instant
161152a1565SThomas Cort ** the link succeeds.
162152a1565SThomas Cort */
163152a1565SThomas Cort if (uucpstyle ?
164152a1565SThomas Cort (write(fd, &pid, sizeof(pid)) != sizeof(pid)) :
165152a1565SThomas Cort (write(fd, buf, len) < 0))
166152a1565SThomas Cort {
167152a1565SThomas Cort fprintf(stderr, "%s: write(%s,%ld): %s\n",
168*0a6a1f1dSLionel Sambuc Pname, tempname, (long)pid, strerror(errno));
169152a1565SThomas Cort (void) close(fd);
170152a1565SThomas Cort if (unlink(tempname) < 0) {
171152a1565SThomas Cort fprintf(stderr, E_unlk,
172152a1565SThomas Cort Pname, tempname, strerror(errno));
173152a1565SThomas Cort }
174152a1565SThomas Cort return (NULL);
175152a1565SThomas Cort }
176152a1565SThomas Cort (void) close(fd);
177152a1565SThomas Cort return(tempname);
178152a1565SThomas Cort }
179152a1565SThomas Cort
180152a1565SThomas Cort /*
181152a1565SThomas Cort ** Does the PID exist?
182152a1565SThomas Cort ** Send null signal to find out.
183152a1565SThomas Cort */
184152a1565SThomas Cort static int
p_exists(pid_t pid)185152a1565SThomas Cort p_exists(pid_t pid)
186152a1565SThomas Cort {
187*0a6a1f1dSLionel Sambuc dprintf("%s: process %ld is ", Pname, (long)pid);
188152a1565SThomas Cort if (pid <= 0) {
189152a1565SThomas Cort dprintf("invalid\n");
190152a1565SThomas Cort return(FALSE);
191152a1565SThomas Cort }
192152a1565SThomas Cort if (kill(pid, 0) < 0) {
193152a1565SThomas Cort switch(errno) {
194152a1565SThomas Cort case ESRCH:
195152a1565SThomas Cort dprintf("dead\n");
196152a1565SThomas Cort return(FALSE); /* pid does not exist */
197152a1565SThomas Cort case EPERM:
198152a1565SThomas Cort dprintf("alive\n");
199152a1565SThomas Cort return(TRUE); /* pid exists */
200152a1565SThomas Cort default:
201152a1565SThomas Cort dprintf("state unknown: %s\n", strerror(errno));
202152a1565SThomas Cort return(TRUE); /* be conservative */
203152a1565SThomas Cort }
204152a1565SThomas Cort }
205152a1565SThomas Cort dprintf("alive\n");
206152a1565SThomas Cort return(TRUE); /* pid exists */
207152a1565SThomas Cort }
208152a1565SThomas Cort
209152a1565SThomas Cort /*
210152a1565SThomas Cort ** Check the validity of an existing lock file.
211152a1565SThomas Cort **
212152a1565SThomas Cort ** Read the PID out of the lock
213152a1565SThomas Cort ** Send a null signal to determine whether that PID still exists
214152a1565SThomas Cort ** Existence (or not) determines the validity of the lock.
215152a1565SThomas Cort **
216152a1565SThomas Cort ** Two bigs wins to this algorithm:
217152a1565SThomas Cort **
218152a1565SThomas Cort ** o Locks do not survive crashes of either the system or the
219152a1565SThomas Cort ** application by any appreciable period of time.
220152a1565SThomas Cort **
221152a1565SThomas Cort ** o No clean up to do if the system or application crashes.
222152a1565SThomas Cort **
223152a1565SThomas Cort */
224152a1565SThomas Cort static int
cklock(char * file,int uucpstyle)225152a1565SThomas Cort cklock(char *file, int uucpstyle)
226152a1565SThomas Cort {
227152a1565SThomas Cort int fd = open(file, O_RDONLY);
228152a1565SThomas Cort ssize_t len;
229152a1565SThomas Cort pid_t pid;
230152a1565SThomas Cort char buf[BUFSIZ];
231152a1565SThomas Cort
232152a1565SThomas Cort dprintf("%s: checking extant lock <%s>\n", Pname, file);
233152a1565SThomas Cort if (fd < 0) {
234152a1565SThomas Cort if (errno != ENOENT)
235152a1565SThomas Cort fprintf(stderr, E_open, Pname, file, strerror(errno));
236152a1565SThomas Cort return(TRUE); /* might or might not; conservatism */
237152a1565SThomas Cort }
238152a1565SThomas Cort
239152a1565SThomas Cort if (uucpstyle ?
240152a1565SThomas Cort ((len = read(fd, &pid, sizeof(pid))) != sizeof(pid)) :
241152a1565SThomas Cort ((len = read(fd, buf, sizeof(buf))) <= 0))
242152a1565SThomas Cort {
243152a1565SThomas Cort close(fd);
244152a1565SThomas Cort dprintf("%s: lock file format error\n", Pname);
245152a1565SThomas Cort return(FALSE);
246152a1565SThomas Cort }
247152a1565SThomas Cort close(fd);
248152a1565SThomas Cort buf[len + 1] = '\0';
249152a1565SThomas Cort return(p_exists(uucpstyle ? pid : atoi(buf)));
250152a1565SThomas Cort }
251152a1565SThomas Cort
252152a1565SThomas Cort static int
mklock(char * file,pid_t pid,int uucpstyle)253152a1565SThomas Cort mklock(char *file, pid_t pid, int uucpstyle)
254152a1565SThomas Cort {
255152a1565SThomas Cort char *tmp;
256152a1565SThomas Cort int retcode = FALSE;
257152a1565SThomas Cort
258152a1565SThomas Cort dprintf("%s: trying lock <%s> for process %ld\n", Pname, file,
259*0a6a1f1dSLionel Sambuc (long)pid);
260152a1565SThomas Cort if ((tmp = xtmpfile(file, pid, uucpstyle)) == NULL)
261152a1565SThomas Cort return(FALSE);
262152a1565SThomas Cort
263152a1565SThomas Cort linkloop:
264152a1565SThomas Cort if (link(tmp, file) < 0) {
265152a1565SThomas Cort switch(errno) {
266152a1565SThomas Cort case EEXIST:
267152a1565SThomas Cort dprintf("%s: lock <%s> already exists\n", Pname, file);
268152a1565SThomas Cort if (cklock(file, uucpstyle)) {
269152a1565SThomas Cort dprintf("%s: extant lock is valid\n", Pname);
270152a1565SThomas Cort break;
271152a1565SThomas Cort } else {
272152a1565SThomas Cort dprintf("%s: lock is invalid, removing\n",
273152a1565SThomas Cort Pname);
274152a1565SThomas Cort if (unlink(file) < 0) {
275152a1565SThomas Cort fprintf(stderr, E_unlk,
276152a1565SThomas Cort Pname, file, strerror(errno));
277152a1565SThomas Cort break;
278152a1565SThomas Cort }
279152a1565SThomas Cort }
280152a1565SThomas Cort /*
281152a1565SThomas Cort ** I hereby profane the god of structured programming,
282152a1565SThomas Cort ** Edsgar Dijkstra
283152a1565SThomas Cort */
284152a1565SThomas Cort goto linkloop;
285152a1565SThomas Cort default:
286152a1565SThomas Cort fprintf(stderr, "%s: link(%s, %s): %s\n",
287152a1565SThomas Cort Pname, tmp, file, strerror(errno));
288152a1565SThomas Cort break;
289152a1565SThomas Cort }
290152a1565SThomas Cort } else {
291152a1565SThomas Cort dprintf("%s: got lock <%s>\n", Pname, file);
292152a1565SThomas Cort retcode = TRUE;
293152a1565SThomas Cort }
294152a1565SThomas Cort if (unlink(tmp) < 0) {
295152a1565SThomas Cort fprintf(stderr, E_unlk, Pname, tmp, strerror(errno));
296152a1565SThomas Cort }
297152a1565SThomas Cort return(retcode);
298152a1565SThomas Cort }
299152a1565SThomas Cort
300152a1565SThomas Cort static void
bad_usage(void)301152a1565SThomas Cort bad_usage(void)
302152a1565SThomas Cort {
303152a1565SThomas Cort fprintf(stderr, USAGE, Pname, Pname);
304152a1565SThomas Cort exit(LOCK_FAIL);
305152a1565SThomas Cort }
306152a1565SThomas Cort
307152a1565SThomas Cort int
main(int ac,char ** av)308152a1565SThomas Cort main(int ac, char **av)
309152a1565SThomas Cort {
310152a1565SThomas Cort int x;
311152a1565SThomas Cort char *file = NULL;
312152a1565SThomas Cort pid_t pid = 0;
313152a1565SThomas Cort int uucpstyle = FALSE; /* indicating UUCP style locks */
314152a1565SThomas Cort int only_check = TRUE; /* don't make a lock */
315152a1565SThomas Cort
316152a1565SThomas Cort Pname = ((Pname = strrchr(av[0], '/')) ? Pname + 1 : av[0]);
317152a1565SThomas Cort
318152a1565SThomas Cort for(x = 1; x < ac; x++) {
319152a1565SThomas Cort if (av[x][0] == '-') {
320152a1565SThomas Cort switch(av[x][1]) {
321152a1565SThomas Cort case 'u':
322152a1565SThomas Cort uucpstyle = TRUE;
323152a1565SThomas Cort break;
324152a1565SThomas Cort case 'd':
325152a1565SThomas Cort Debug = TRUE;
326152a1565SThomas Cort break;
327152a1565SThomas Cort case 'p':
328152a1565SThomas Cort if (strlen(av[x]) > 2) {
329152a1565SThomas Cort pid = atoi(&av[x][2]);
330152a1565SThomas Cort } else {
331152a1565SThomas Cort if (++x >= ac) {
332152a1565SThomas Cort bad_usage();
333152a1565SThomas Cort }
334152a1565SThomas Cort pid = atoi(av[x]);
335152a1565SThomas Cort }
336152a1565SThomas Cort only_check = FALSE; /* wants one */
337152a1565SThomas Cort break;
338152a1565SThomas Cort case 'f':
339152a1565SThomas Cort if (strlen(av[x]) > 2) {
340152a1565SThomas Cort file = &av[x][2];
341152a1565SThomas Cort } else {
342152a1565SThomas Cort if (++x >= ac) {
343152a1565SThomas Cort bad_usage();
344152a1565SThomas Cort }
345152a1565SThomas Cort file = av[x];
346152a1565SThomas Cort }
347152a1565SThomas Cort break;
348152a1565SThomas Cort default:
349152a1565SThomas Cort bad_usage();
350152a1565SThomas Cort }
351152a1565SThomas Cort }
352152a1565SThomas Cort }
353152a1565SThomas Cort
354152a1565SThomas Cort if (file == NULL || (!only_check && pid <= 0)) {
355152a1565SThomas Cort bad_usage();
356152a1565SThomas Cort }
357152a1565SThomas Cort
358152a1565SThomas Cort if (only_check) {
359152a1565SThomas Cort exit(cklock(file, uucpstyle) ? LOCK_GOOD : LOCK_BAD);
360152a1565SThomas Cort }
361152a1565SThomas Cort
362152a1565SThomas Cort exit(mklock(file, pid, uucpstyle) ? LOCK_SET : LOCK_FAIL);
363152a1565SThomas Cort }
364