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