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