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