xref: /minix3/usr.bin/shlock/shlock.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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