pidlock.c revision 1.17 1 1.17 ryo /* $NetBSD: pidlock.c,v 1.17 2020/03/30 08:24:36 ryo Exp $ */
2 1.1 cjs
3 1.1 cjs /*
4 1.12 salo * 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.17 ryo __RCSID("$NetBSD: pidlock.c,v 1.17 2020/03/30 08:24:36 ryo 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.14 christos #include <paths.h>
44 1.1 cjs
45 1.1 cjs /*
46 1.1 cjs * Create a lockfile. Return 0 when locked, -1 on error.
47 1.1 cjs */
48 1.1 cjs int
49 1.9 ad pidlock(const char *lockfile, int flags, pid_t *locker, const char *info)
50 1.1 cjs {
51 1.1 cjs char tempfile[MAXPATHLEN];
52 1.4 mrg char hostname[MAXHOSTNAMELEN + 1];
53 1.1 cjs pid_t pid2 = -1;
54 1.2 cjs struct stat st;
55 1.16 christos ssize_t n;
56 1.16 christos int f = -1, savee;
57 1.1 cjs char s[256];
58 1.1 cjs char *p;
59 1.14 christos size_t len;
60 1.1 cjs
61 1.7 lukem _DIAGASSERT(lockfile != NULL);
62 1.7 lukem /* locker may be NULL */
63 1.7 lukem /* info may be NULL */
64 1.7 lukem
65 1.7 lukem
66 1.2 cjs if (gethostname(hostname, sizeof(hostname)))
67 1.2 cjs return -1;
68 1.4 mrg hostname[sizeof(hostname) - 1] = '\0';
69 1.2 cjs
70 1.17 ryo /* avoid '/' in hostname, as it may contain arbitrary characters */
71 1.17 ryo for (p = hostname; *p != '\0'; p++) {
72 1.17 ryo if (*p == '/')
73 1.17 ryo *p = '_';
74 1.17 ryo }
75 1.17 ryo
76 1.2 cjs /*
77 1.2 cjs * Build a path to the temporary file.
78 1.2 cjs * We use the path with the PID and hostname appended.
79 1.2 cjs * XXX This is not thread safe.
80 1.2 cjs */
81 1.2 cjs if (snprintf(tempfile, sizeof(tempfile), "%s.%d.%s", lockfile,
82 1.15 lukem (int) getpid(), hostname) >= (int)sizeof(tempfile)) {
83 1.1 cjs errno = ENAMETOOLONG;
84 1.1 cjs return -1;
85 1.1 cjs }
86 1.1 cjs
87 1.1 cjs /* Open it, write pid, hostname, info. */
88 1.14 christos if ((f = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1)
89 1.14 christos goto out;
90 1.14 christos
91 1.14 christos (void)snprintf(s, sizeof(s), "%10d\n", getpid()); /* pid */
92 1.14 christos if (write(f, s, (size_t)11) != 11)
93 1.14 christos goto out;
94 1.14 christos
95 1.2 cjs if ((flags & PIDLOCK_USEHOSTNAME)) { /* hostname */
96 1.14 christos len = strlen(hostname);
97 1.15 lukem if ((size_t)write(f, hostname, len) != len
98 1.14 christos || write(f, "\n", (size_t)1) != 1)
99 1.14 christos goto out;
100 1.1 cjs }
101 1.1 cjs if (info) { /* info */
102 1.1 cjs if (!(flags & PIDLOCK_USEHOSTNAME)) {
103 1.1 cjs /* write blank line because there's no hostname */
104 1.14 christos if (write(f, "\n", (size_t)1) != 1)
105 1.14 christos goto out;
106 1.1 cjs }
107 1.14 christos len = strlen(info);
108 1.15 lukem if ((size_t)write(f, info, len) != len ||
109 1.14 christos write(f, "\n", (size_t)1) != 1)
110 1.14 christos goto out;
111 1.1 cjs }
112 1.14 christos (void)close(f);
113 1.14 christos f = -1;
114 1.1 cjs
115 1.1 cjs /* Hard link the temporary file to the real lock file. */
116 1.1 cjs /* This is an atomic operation. */
117 1.2 cjs lockfailed:
118 1.14 christos while (link(tempfile, lockfile) == -1) {
119 1.14 christos if (errno != EEXIST)
120 1.14 christos goto out;
121 1.1 cjs /* Find out who has this lockfile. */
122 1.14 christos if ((f = open(lockfile, O_RDONLY, 0)) != -1) {
123 1.16 christos if ((n = read(f, s, (size_t)11)) == -1)
124 1.14 christos goto out;
125 1.16 christos if (n == 0) {
126 1.14 christos errno = EINVAL;
127 1.14 christos goto out;
128 1.14 christos }
129 1.2 cjs pid2 = atoi(s);
130 1.16 christos if ((n = read(f, s, sizeof(s) - 2)) == -1)
131 1.14 christos goto out;
132 1.16 christos if (n == 0)
133 1.14 christos *s = '\0';
134 1.14 christos s[sizeof(s) - 1] = '\0';
135 1.14 christos if ((p = strchr(s, '\n')) != NULL)
136 1.2 cjs *p = '\0';
137 1.14 christos (void)close(f);
138 1.14 christos f = -1;
139 1.2 cjs
140 1.14 christos if ((flags & PIDLOCK_USEHOSTNAME) == 0 ||
141 1.14 christos strcmp(s, hostname) == 0) {
142 1.14 christos if (kill(pid2, 0) == -1 && errno == ESRCH) {
143 1.2 cjs /* process doesn't exist */
144 1.14 christos (void)unlink(lockfile);
145 1.2 cjs continue;
146 1.2 cjs }
147 1.1 cjs }
148 1.1 cjs }
149 1.1 cjs if (flags & PIDLOCK_NONBLOCK) {
150 1.1 cjs if (locker)
151 1.1 cjs *locker = pid2;
152 1.1 cjs errno = EWOULDBLOCK;
153 1.14 christos goto out;
154 1.1 cjs } else
155 1.1 cjs sleep(5);
156 1.1 cjs }
157 1.2 cjs /*
158 1.2 cjs * Check to see that we really were successful (in case we're
159 1.2 cjs * using NFS) by making sure that something really is linked
160 1.2 cjs * to our tempfile (reference count is two).
161 1.2 cjs */
162 1.14 christos if (stat(tempfile, &st) == -1)
163 1.14 christos goto out;
164 1.2 cjs if (st.st_nlink != 2)
165 1.2 cjs goto lockfailed;
166 1.1 cjs
167 1.14 christos (void)unlink(tempfile);
168 1.2 cjs if (locker)
169 1.2 cjs *locker = getpid(); /* return this process's PID on lock */
170 1.2 cjs errno = 0;
171 1.1 cjs return 0;
172 1.14 christos out:
173 1.16 christos savee = errno;
174 1.14 christos if (f != -1)
175 1.14 christos (void)close(f);
176 1.14 christos (void)unlink(tempfile);
177 1.16 christos errno = savee;
178 1.14 christos return -1;
179 1.14 christos }
180 1.14 christos
181 1.14 christos static int
182 1.14 christos checktty(const char *tty)
183 1.14 christos {
184 1.14 christos char ttyfile[MAXPATHLEN];
185 1.14 christos struct stat sb;
186 1.14 christos
187 1.14 christos (void)strlcpy(ttyfile, _PATH_DEV, sizeof(ttyfile));
188 1.14 christos (void)strlcat(ttyfile, tty, sizeof(ttyfile));
189 1.14 christos
190 1.14 christos /* make sure the tty exists */
191 1.14 christos if (stat(ttyfile, &sb) == -1)
192 1.14 christos return -1;
193 1.14 christos if (!S_ISCHR(sb.st_mode)) {
194 1.14 christos errno = EFTYPE;
195 1.14 christos return -1;
196 1.14 christos }
197 1.14 christos return 0;
198 1.1 cjs }
199 1.1 cjs
200 1.1 cjs #define LOCKPATH "/var/spool/lock/LCK.."
201 1.14 christos
202 1.14 christos static char *
203 1.14 christos makelock(char *buf, size_t bufsiz, const char *tty)
204 1.14 christos {
205 1.14 christos (void)strlcpy(buf, LOCKPATH, bufsiz);
206 1.14 christos (void)strlcat(buf, tty, bufsiz);
207 1.14 christos return buf;
208 1.14 christos }
209 1.1 cjs
210 1.5 christos /*ARGSUSED*/
211 1.1 cjs int
212 1.9 ad ttylock(const char *tty, int flags, pid_t *locker)
213 1.1 cjs {
214 1.1 cjs char lockfile[MAXPATHLEN];
215 1.1 cjs
216 1.7 lukem _DIAGASSERT(tty != NULL);
217 1.7 lukem
218 1.14 christos if (checktty(tty) != 0)
219 1.14 christos return -1;
220 1.1 cjs
221 1.1 cjs /* do the lock */
222 1.14 christos return pidlock(makelock(lockfile, sizeof(lockfile), tty),
223 1.14 christos flags, locker, 0);
224 1.1 cjs }
225 1.1 cjs
226 1.1 cjs int
227 1.9 ad ttyunlock(const char *tty)
228 1.1 cjs {
229 1.1 cjs char lockfile[MAXPATHLEN];
230 1.7 lukem
231 1.7 lukem _DIAGASSERT(tty != NULL);
232 1.1 cjs
233 1.14 christos if (checktty(tty) != 0)
234 1.14 christos return -1;
235 1.1 cjs
236 1.14 christos /* remove the lock */
237 1.14 christos return unlink(makelock(lockfile, sizeof(lockfile), tty));
238 1.1 cjs }
239