xref: /netbsd/lib/libutil/pidlock.c (revision bf9ec67e)
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@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