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