pidlock.c revision 1.7 1 /* $NetBSD: pidlock.c,v 1.7 1999/09/16 11:45:51 lukem Exp $ */
2
3 /*
4 * Copyright 1996, 1997 by Curt Sampson <cjs (at) 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.7 1999/09/16 11:45:51 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 #ifdef _DIAGNOSTIC
67 if (lockfile == NULL) {
68 errno = EFAULT;
69 return -1;
70 }
71 #endif
72
73
74 if (gethostname(hostname, sizeof(hostname)))
75 return -1;
76 hostname[sizeof(hostname) - 1] = '\0';
77
78 /*
79 * Build a path to the temporary file.
80 * We use the path with the PID and hostname appended.
81 * XXX This is not thread safe.
82 */
83 if (snprintf(tempfile, sizeof(tempfile), "%s.%d.%s", lockfile,
84 (int) getpid(), hostname) >= sizeof(tempfile)) {
85 errno = ENAMETOOLONG;
86 return -1;
87 }
88
89 /* Open it, write pid, hostname, info. */
90 if ( (f = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1 ) {
91 err = errno;
92 unlink(tempfile);
93 errno = err;
94 return -1;
95 }
96 snprintf(s, sizeof(s), "%10d\n", getpid()); /* pid */
97 if (write(f, s, 11) != 11) {
98 err = errno;
99 close(f);
100 unlink(tempfile);
101 errno = err;
102 return -1;
103 }
104 if ((flags & PIDLOCK_USEHOSTNAME)) { /* hostname */
105 if ((write(f, hostname, strlen(hostname)) != strlen(hostname))
106 || (write(f, "\n", 1) != 1)) {
107 err = errno;
108 close(f);
109 unlink(tempfile);
110 errno = err;
111 return -1;
112 }
113 }
114 if (info) { /* info */
115 if (!(flags & PIDLOCK_USEHOSTNAME)) {
116 /* write blank line because there's no hostname */
117 if (write(f, "\n", 1) != 1) {
118 err = errno;
119 close(f);
120 unlink(tempfile);
121 errno = err;
122 return -1;
123 }
124 }
125 if (write(f, info, strlen(info)) != strlen(info) ||
126 (write(f, "\n", 1) != 1)) {
127 err = errno;
128 close(f);
129 unlink(tempfile);
130 errno = err;
131 return -1;
132 }
133 }
134 close(f);
135
136 /* Hard link the temporary file to the real lock file. */
137 /* This is an atomic operation. */
138 lockfailed:
139 while (link(tempfile, lockfile) != 0) {
140 if (errno != EEXIST) {
141 err = errno;
142 unlink(tempfile);
143 errno = err;
144 return -1;
145 }
146 /* Find out who has this lockfile. */
147 if ((f = open(lockfile, O_RDONLY, 0)) != 0) {
148 read(f, s, 11);
149 pid2 = atoi(s);
150 read(f, s, sizeof(s)-2);
151 s[sizeof(s)-1] = '\0';
152 if ((p=strchr(s, '\n')) != NULL)
153 *p = '\0';
154 close(f);
155
156 if (!((flags & PIDLOCK_USEHOSTNAME) &&
157 strcmp(s, hostname))) {
158 if ((kill(pid2, 0) != 0) && (errno == ESRCH)) {
159 /* process doesn't exist */
160 unlink(lockfile);
161 continue;
162 }
163 }
164 }
165 if (flags & PIDLOCK_NONBLOCK) {
166 if (locker)
167 *locker = pid2;
168 unlink(tempfile);
169 errno = EWOULDBLOCK;
170 return -1;
171 } else
172 sleep(5);
173 }
174 /*
175 * Check to see that we really were successful (in case we're
176 * using NFS) by making sure that something really is linked
177 * to our tempfile (reference count is two).
178 */
179 if (stat(tempfile, &st) != 0) {
180 err = errno;
181 /*
182 * We don't know if lockfile was really created by us,
183 * so we can't remove it.
184 */
185 unlink(tempfile);
186 errno = err;
187 return -1;
188 }
189 if (st.st_nlink != 2)
190 goto lockfailed;
191
192 unlink(tempfile);
193 if (locker)
194 *locker = getpid(); /* return this process's PID on lock */
195 errno = 0;
196 return 0;
197 }
198
199 #define LOCKPATH "/var/spool/lock/LCK.."
200 #define DEVPATH "/dev/"
201
202 /*ARGSUSED*/
203 int
204 ttylock(tty, flags, locker)
205 const char *tty;
206 int flags;
207 pid_t *locker;
208 {
209 char lockfile[MAXPATHLEN];
210 char ttyfile[MAXPATHLEN];
211 struct stat sb;
212
213 _DIAGASSERT(tty != NULL);
214 /* locker is not used */
215 #ifdef _DIAGNOSTIC
216 if (tty == NULL || *tty == '\0') {
217 errno = ENOENT;
218 return -1;
219 }
220 #endif
221
222 /* make sure the tty exists */
223 strcpy(ttyfile, DEVPATH);
224 strncat(ttyfile, tty, MAXPATHLEN-strlen(DEVPATH));
225 if (stat(ttyfile, &sb)) {
226 errno = ENOENT; return -1;
227 }
228 if (!S_ISCHR(sb.st_mode)) {
229 errno = ENOENT; return -1;
230 }
231
232 /* do the lock */
233 strcpy(lockfile, LOCKPATH);
234 strncat(lockfile, tty, MAXPATHLEN-strlen(LOCKPATH));
235 return pidlock(lockfile, 0, 0, 0);
236 }
237
238 int
239 ttyunlock(tty)
240 const char *tty;
241 {
242 char lockfile[MAXPATHLEN];
243 char ttyfile[MAXPATHLEN];
244 struct stat sb;
245
246 _DIAGASSERT(tty != NULL);
247 #ifdef _DIAGNOSTIC
248 if (tty == NULL || *tty == '\0') {
249 errno = ENOENT;
250 return -1;
251 }
252 #endif
253
254 /* make sure the tty exists */
255 strcpy(ttyfile, DEVPATH);
256 strncat(ttyfile, tty, MAXPATHLEN-strlen(DEVPATH));
257 if (stat(ttyfile, &sb)) {
258 errno = ENOENT; return -1;
259 }
260 if (!S_ISCHR(sb.st_mode)) {
261 errno = ENOENT; return -1;
262 }
263
264 /* undo the lock */
265 strcpy(lockfile, LOCKPATH);
266 strncat(lockfile, tty, MAXPATHLEN-strlen(LOCKPATH));
267 return unlink(lockfile);
268 }
269