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