xref: /netbsd-src/lib/libutil/pidlock.c (revision e4d7c2e329d54c97e0c0bd3016bbe74f550c3d5e)
1 /*	$NetBSD: pidlock.c,v 1.8 1999/09/20 04:48:08 lukem Exp $ */
2 
3 /*
4  * Copyright 1996, 1997 by Curt Sampson <cjs@netbsd.org>.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
13  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
16  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22  * SUCH DAMAGE.
23  */
24 
25 #include <sys/cdefs.h>
26 #if defined(LIBC_SCCS) && !defined(lint)
27 __RCSID("$NetBSD: pidlock.c,v 1.8 1999/09/20 04:48:08 lukem Exp $");
28 #endif /* LIBC_SCCS and not lint */
29 
30 #include <sys/param.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 
34 #include <assert.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <signal.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <util.h>
43 
44 /*
45  * Create a lockfile. Return 0 when locked, -1 on error.
46  */
47 int
48 pidlock(lockfile, flags, locker, info)
49 	const char *lockfile;
50 	int flags;
51 	pid_t *locker;
52 	const char *info;
53 {
54 	char	tempfile[MAXPATHLEN];
55 	char	hostname[MAXHOSTNAMELEN + 1];
56 	pid_t	pid2 = -1;
57 	struct	stat st;
58 	int	err;
59 	int	f;
60 	char	s[256];
61 	char	*p;
62 
63 	_DIAGASSERT(lockfile != NULL);
64 	/* locker may be NULL */
65 	/* info may be NULL */
66 
67 
68 	if (gethostname(hostname, sizeof(hostname)))
69 		return -1;
70 	hostname[sizeof(hostname) - 1] = '\0';
71 
72 	/*
73 	 * Build a path to the temporary file.
74 	 * We use the path with the PID and hostname appended.
75 	 * XXX This is not thread safe.
76 	 */
77 	if (snprintf(tempfile, sizeof(tempfile), "%s.%d.%s", lockfile,
78 	    (int) getpid(), hostname) >= sizeof(tempfile))  {
79 		errno = ENAMETOOLONG;
80 		return -1;
81 	}
82 
83 	/* Open it, write pid, hostname, info. */
84 	if ( (f = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1 )  {
85 		err = errno;
86 		unlink(tempfile);
87 		errno = err;
88 		return -1;
89 	}
90 	snprintf(s, sizeof(s), "%10d\n", getpid());	/* pid */
91 	if (write(f, s, 11) != 11)  {
92 		err = errno;
93 		close(f);
94 		unlink(tempfile);
95 		errno = err;
96 		return -1;
97 	}
98 	if ((flags & PIDLOCK_USEHOSTNAME))  {		/* hostname */
99 		if ((write(f, hostname, strlen(hostname)) != strlen(hostname))
100 		    || (write(f, "\n", 1) != 1))  {
101 			err = errno;
102 			close(f);
103 			unlink(tempfile);
104 			errno = err;
105 			return -1;
106 		}
107 	}
108 	if (info)  {					/* info */
109 		if (!(flags & PIDLOCK_USEHOSTNAME))  {
110 			/* write blank line because there's no hostname */
111 			if (write(f, "\n", 1) != 1)  {
112 				err = errno;
113 				close(f);
114 				unlink(tempfile);
115 				errno = err;
116 				return -1;
117 			}
118 		}
119 		if (write(f, info, strlen(info)) != strlen(info) ||
120 		    (write(f, "\n", 1) != 1))  {
121 			err = errno;
122 			close(f);
123 			unlink(tempfile);
124 			errno = err;
125 			return -1;
126 		}
127 	}
128 	close(f);
129 
130 	/* Hard link the temporary file to the real lock file. */
131 	/* This is an atomic operation. */
132 lockfailed:
133 	while (link(tempfile, lockfile) != 0)  {
134 		if (errno != EEXIST)  {
135 			err = errno;
136 			unlink(tempfile);
137 			errno = err;
138 			return -1;
139 		}
140 		/* Find out who has this lockfile. */
141 		if ((f = open(lockfile, O_RDONLY, 0)) != 0)  {
142 			read(f, s, 11);
143 			pid2 = atoi(s);
144 			read(f, s, sizeof(s)-2);
145 			s[sizeof(s)-1] = '\0';
146 			if ((p=strchr(s, '\n')) != NULL)
147 				*p = '\0';
148 			close(f);
149 
150 			if (!((flags & PIDLOCK_USEHOSTNAME) &&
151 			    strcmp(s, hostname)))  {
152 				if ((kill(pid2, 0) != 0) && (errno == ESRCH))  {
153 					/* process doesn't exist */
154 					unlink(lockfile);
155 					continue;
156 				}
157 			}
158 		}
159 		if (flags & PIDLOCK_NONBLOCK)  {
160 			if (locker)
161 				*locker = pid2;
162 			unlink(tempfile);
163 			errno = EWOULDBLOCK;
164 			return -1;
165 		} else
166 			sleep(5);
167 	}
168 	/*
169 	 * Check to see that we really were successful (in case we're
170 	 * using NFS) by making sure that something really is linked
171 	 * to our tempfile (reference count is two).
172 	 */
173 	if (stat(tempfile, &st) != 0)  {
174 		err = errno;
175 		/*
176 		 * We don't know if lockfile was really created by us,
177 		 * so we can't remove it.
178 		 */
179 		unlink(tempfile);
180 		errno = err;
181 		return -1;
182 	}
183 	if (st.st_nlink != 2)
184 		goto lockfailed;
185 
186 	unlink(tempfile);
187  	if (locker)
188  		*locker = getpid();	/* return this process's PID on lock */
189 	errno = 0;
190 	return 0;
191 }
192 
193 #define LOCKPATH	"/var/spool/lock/LCK.."
194 #define	DEVPATH		"/dev/"
195 
196 /*ARGSUSED*/
197 int
198 ttylock(tty, flags, locker)
199 	const char *tty;
200 	int flags;
201 	pid_t *locker;
202 {
203 	char	lockfile[MAXPATHLEN];
204 	char	ttyfile[MAXPATHLEN];
205 	struct stat sb;
206 
207 	_DIAGASSERT(tty != NULL);
208 	/* locker is not used */
209 
210 	/* make sure the tty exists */
211 	strcpy(ttyfile, DEVPATH);
212 	strncat(ttyfile, tty, MAXPATHLEN-strlen(DEVPATH));
213 	if (stat(ttyfile, &sb))  {
214 		errno = ENOENT; return -1;
215 	}
216 	if (!S_ISCHR(sb.st_mode))  {
217 		errno = ENOENT; return -1;
218 	}
219 
220 	/* do the lock */
221 	strcpy(lockfile, LOCKPATH);
222 	strncat(lockfile, tty, MAXPATHLEN-strlen(LOCKPATH));
223 	return pidlock(lockfile, 0, 0, 0);
224 }
225 
226 int
227 ttyunlock(tty)
228 	const char *tty;
229 {
230 	char	lockfile[MAXPATHLEN];
231 	char	ttyfile[MAXPATHLEN];
232 	struct stat sb;
233 
234 	_DIAGASSERT(tty != NULL);
235 
236 	/* make sure the tty exists */
237 	strcpy(ttyfile, DEVPATH);
238 	strncat(ttyfile, tty, MAXPATHLEN-strlen(DEVPATH));
239 	if (stat(ttyfile, &sb))  {
240 		errno = ENOENT; return -1;
241 	}
242 	if (!S_ISCHR(sb.st_mode))  {
243 		errno = ENOENT; return -1;
244 	}
245 
246 	/* undo the lock */
247 	strcpy(lockfile, LOCKPATH);
248 	strncat(lockfile, tty, MAXPATHLEN-strlen(LOCKPATH));
249 	return unlink(lockfile);
250 }
251