pidlock.c revision 1.8 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 (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.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