xref: /freebsd/contrib/openbsm/compat/pidfile.h (revision aa77200569e397d6ff1fdb4d255d0fa254d0a128)
1*aa772005SRobert Watson /*-
2*aa772005SRobert Watson  * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3*aa772005SRobert Watson  * All rights reserved.
4*aa772005SRobert Watson  *
5*aa772005SRobert Watson  * Redistribution and use in source and binary forms, with or without
6*aa772005SRobert Watson  * modification, are permitted provided that the following conditions
7*aa772005SRobert Watson  * are met:
8*aa772005SRobert Watson  * 1. Redistributions of source code must retain the above copyright
9*aa772005SRobert Watson  *    notice, this list of conditions and the following disclaimer.
10*aa772005SRobert Watson  * 2. Redistributions in binary form must reproduce the above copyright
11*aa772005SRobert Watson  *    notice, this list of conditions and the following disclaimer in the
12*aa772005SRobert Watson  *    documentation and/or other materials provided with the distribution.
13*aa772005SRobert Watson  *
14*aa772005SRobert Watson  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15*aa772005SRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*aa772005SRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*aa772005SRobert Watson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18*aa772005SRobert Watson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*aa772005SRobert Watson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*aa772005SRobert Watson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*aa772005SRobert Watson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*aa772005SRobert Watson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*aa772005SRobert Watson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*aa772005SRobert Watson  * SUCH DAMAGE.
25*aa772005SRobert Watson  *
26*aa772005SRobert Watson  * Derived from FreeBSD head/lib/libutil/pidfile.c r231938
27*aa772005SRobert Watson  * $P4: //depot/projects/trustedbsd/openbsm/compat/pidfile.h#1 $
28*aa772005SRobert Watson  */
29*aa772005SRobert Watson 
30*aa772005SRobert Watson #include <sys/param.h>
31*aa772005SRobert Watson #include <sys/file.h>
32*aa772005SRobert Watson #include <sys/stat.h>
33*aa772005SRobert Watson 
34*aa772005SRobert Watson #include <stdio.h>
35*aa772005SRobert Watson #include <stdlib.h>
36*aa772005SRobert Watson #include <unistd.h>
37*aa772005SRobert Watson #include <fcntl.h>
38*aa772005SRobert Watson #include <string.h>
39*aa772005SRobert Watson #include <time.h>
40*aa772005SRobert Watson #include <err.h>
41*aa772005SRobert Watson #include <errno.h>
42*aa772005SRobert Watson 
43*aa772005SRobert Watson #include "flopen.h"
44*aa772005SRobert Watson 
45*aa772005SRobert Watson struct pidfh {
46*aa772005SRobert Watson 	int	pf_fd;
47*aa772005SRobert Watson 	char	pf_path[MAXPATHLEN + 1];
48*aa772005SRobert Watson 	dev_t	pf_dev;
49*aa772005SRobert Watson 	ino_t	pf_ino;
50*aa772005SRobert Watson };
51*aa772005SRobert Watson 
52*aa772005SRobert Watson static int _pidfile_remove(struct pidfh *pfh, int freeit);
53*aa772005SRobert Watson 
54*aa772005SRobert Watson static int
55*aa772005SRobert Watson pidfile_verify(const struct pidfh *pfh)
56*aa772005SRobert Watson {
57*aa772005SRobert Watson 	struct stat sb;
58*aa772005SRobert Watson 
59*aa772005SRobert Watson 	if (pfh == NULL || pfh->pf_fd == -1)
60*aa772005SRobert Watson 		return (EINVAL);
61*aa772005SRobert Watson 	/*
62*aa772005SRobert Watson 	 * Check remembered descriptor.
63*aa772005SRobert Watson 	 */
64*aa772005SRobert Watson 	if (fstat(pfh->pf_fd, &sb) == -1)
65*aa772005SRobert Watson 		return (errno);
66*aa772005SRobert Watson 	if (sb.st_dev != pfh->pf_dev || sb.st_ino != pfh->pf_ino)
67*aa772005SRobert Watson 		return (EINVAL);
68*aa772005SRobert Watson 	return (0);
69*aa772005SRobert Watson }
70*aa772005SRobert Watson 
71*aa772005SRobert Watson static int
72*aa772005SRobert Watson pidfile_read(const char *path, pid_t *pidptr)
73*aa772005SRobert Watson {
74*aa772005SRobert Watson 	char buf[16], *endptr;
75*aa772005SRobert Watson 	int error, fd, i;
76*aa772005SRobert Watson 
77*aa772005SRobert Watson 	fd = open(path, O_RDONLY);
78*aa772005SRobert Watson 	if (fd == -1)
79*aa772005SRobert Watson 		return (errno);
80*aa772005SRobert Watson 
81*aa772005SRobert Watson 	i = read(fd, buf, sizeof(buf) - 1);
82*aa772005SRobert Watson 	error = errno;	/* Remember errno in case close() wants to change it. */
83*aa772005SRobert Watson 	close(fd);
84*aa772005SRobert Watson 	if (i == -1)
85*aa772005SRobert Watson 		return (error);
86*aa772005SRobert Watson 	else if (i == 0)
87*aa772005SRobert Watson 		return (EAGAIN);
88*aa772005SRobert Watson 	buf[i] = '\0';
89*aa772005SRobert Watson 
90*aa772005SRobert Watson 	*pidptr = strtol(buf, &endptr, 10);
91*aa772005SRobert Watson 	if (endptr != &buf[i])
92*aa772005SRobert Watson 		return (EINVAL);
93*aa772005SRobert Watson 
94*aa772005SRobert Watson 	return (0);
95*aa772005SRobert Watson }
96*aa772005SRobert Watson 
97*aa772005SRobert Watson static struct pidfh *
98*aa772005SRobert Watson pidfile_open(const char *path, mode_t mode, pid_t *pidptr)
99*aa772005SRobert Watson {
100*aa772005SRobert Watson 	struct pidfh *pfh;
101*aa772005SRobert Watson 	struct stat sb;
102*aa772005SRobert Watson 	int error, fd, len, count;
103*aa772005SRobert Watson 	struct timespec rqtp;
104*aa772005SRobert Watson 
105*aa772005SRobert Watson 	if (pidptr != NULL)
106*aa772005SRobert Watson 		*pidptr = -1;
107*aa772005SRobert Watson 
108*aa772005SRobert Watson 	if (path == NULL)
109*aa772005SRobert Watson 		return (NULL);
110*aa772005SRobert Watson 
111*aa772005SRobert Watson 	pfh = malloc(sizeof(*pfh));
112*aa772005SRobert Watson 	if (pfh == NULL)
113*aa772005SRobert Watson 		return (NULL);
114*aa772005SRobert Watson 
115*aa772005SRobert Watson 	len = snprintf(pfh->pf_path, sizeof(pfh->pf_path),
116*aa772005SRobert Watson 	    "%s", path);
117*aa772005SRobert Watson 	if (len >= (int)sizeof(pfh->pf_path)) {
118*aa772005SRobert Watson 		free(pfh);
119*aa772005SRobert Watson 		errno = ENAMETOOLONG;
120*aa772005SRobert Watson 		return (NULL);
121*aa772005SRobert Watson 	}
122*aa772005SRobert Watson 
123*aa772005SRobert Watson 	/*
124*aa772005SRobert Watson 	 * Open the PID file and obtain exclusive lock.
125*aa772005SRobert Watson 	 * We truncate PID file here only to remove old PID immediatelly,
126*aa772005SRobert Watson 	 * PID file will be truncated again in pidfile_write(), so
127*aa772005SRobert Watson 	 * pidfile_write() can be called multiple times.
128*aa772005SRobert Watson 	 */
129*aa772005SRobert Watson 	fd = flopen(pfh->pf_path,
130*aa772005SRobert Watson #ifdef O_CLOEXEC
131*aa772005SRobert Watson 	    O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK | O_CLOEXEC, mode);
132*aa772005SRobert Watson #else
133*aa772005SRobert Watson 	    O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, mode);
134*aa772005SRobert Watson #endif
135*aa772005SRobert Watson 	if (fd == -1) {
136*aa772005SRobert Watson 		if (errno == EWOULDBLOCK && pidptr != NULL) {
137*aa772005SRobert Watson 			count = 20;
138*aa772005SRobert Watson 			rqtp.tv_sec = 0;
139*aa772005SRobert Watson 			rqtp.tv_nsec = 5000000;
140*aa772005SRobert Watson 			for (;;) {
141*aa772005SRobert Watson 				errno = pidfile_read(pfh->pf_path, pidptr);
142*aa772005SRobert Watson 				if (errno != EAGAIN || --count == 0)
143*aa772005SRobert Watson 					break;
144*aa772005SRobert Watson 				nanosleep(&rqtp, 0);
145*aa772005SRobert Watson 			}
146*aa772005SRobert Watson 			if (errno == EAGAIN)
147*aa772005SRobert Watson 				*pidptr = -1;
148*aa772005SRobert Watson 			if (errno == 0 || errno == EAGAIN)
149*aa772005SRobert Watson 				errno = EEXIST;
150*aa772005SRobert Watson 		}
151*aa772005SRobert Watson 		free(pfh);
152*aa772005SRobert Watson 		return (NULL);
153*aa772005SRobert Watson 	}
154*aa772005SRobert Watson 
155*aa772005SRobert Watson #ifndef O_CLOEXEC
156*aa772005SRobert Watson 	if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
157*aa772005SRobert Watson 		error = errno;
158*aa772005SRobert Watson 		unlink(pfh->pf_path);
159*aa772005SRobert Watson 		close(fd);
160*aa772005SRobert Watson 		free(pfh);
161*aa772005SRobert Watson 		errno = error;
162*aa772005SRobert Watson 		return (NULL);
163*aa772005SRobert Watson 	}
164*aa772005SRobert Watson #endif
165*aa772005SRobert Watson 
166*aa772005SRobert Watson 	/*
167*aa772005SRobert Watson 	 * Remember file information, so in pidfile_write() we are sure we write
168*aa772005SRobert Watson 	 * to the proper descriptor.
169*aa772005SRobert Watson 	 */
170*aa772005SRobert Watson 	if (fstat(fd, &sb) == -1) {
171*aa772005SRobert Watson 		error = errno;
172*aa772005SRobert Watson 		unlink(pfh->pf_path);
173*aa772005SRobert Watson 		close(fd);
174*aa772005SRobert Watson 		free(pfh);
175*aa772005SRobert Watson 		errno = error;
176*aa772005SRobert Watson 		return (NULL);
177*aa772005SRobert Watson 	}
178*aa772005SRobert Watson 
179*aa772005SRobert Watson 	pfh->pf_fd = fd;
180*aa772005SRobert Watson 	pfh->pf_dev = sb.st_dev;
181*aa772005SRobert Watson 	pfh->pf_ino = sb.st_ino;
182*aa772005SRobert Watson 
183*aa772005SRobert Watson 	return (pfh);
184*aa772005SRobert Watson }
185*aa772005SRobert Watson 
186*aa772005SRobert Watson static int
187*aa772005SRobert Watson pidfile_write(struct pidfh *pfh)
188*aa772005SRobert Watson {
189*aa772005SRobert Watson 	char pidstr[16];
190*aa772005SRobert Watson 	int error, fd;
191*aa772005SRobert Watson 
192*aa772005SRobert Watson 	/*
193*aa772005SRobert Watson 	 * Check remembered descriptor, so we don't overwrite some other
194*aa772005SRobert Watson 	 * file if pidfile was closed and descriptor reused.
195*aa772005SRobert Watson 	 */
196*aa772005SRobert Watson 	errno = pidfile_verify(pfh);
197*aa772005SRobert Watson 	if (errno != 0) {
198*aa772005SRobert Watson 		/*
199*aa772005SRobert Watson 		 * Don't close descriptor, because we are not sure if it's ours.
200*aa772005SRobert Watson 		 */
201*aa772005SRobert Watson 		return (-1);
202*aa772005SRobert Watson 	}
203*aa772005SRobert Watson 	fd = pfh->pf_fd;
204*aa772005SRobert Watson 
205*aa772005SRobert Watson 	/*
206*aa772005SRobert Watson 	 * Truncate PID file, so multiple calls of pidfile_write() are allowed.
207*aa772005SRobert Watson 	 */
208*aa772005SRobert Watson 	if (ftruncate(fd, 0) == -1) {
209*aa772005SRobert Watson 		error = errno;
210*aa772005SRobert Watson 		_pidfile_remove(pfh, 0);
211*aa772005SRobert Watson 		errno = error;
212*aa772005SRobert Watson 		return (-1);
213*aa772005SRobert Watson 	}
214*aa772005SRobert Watson 
215*aa772005SRobert Watson 	snprintf(pidstr, sizeof(pidstr), "%u", getpid());
216*aa772005SRobert Watson 	if (pwrite(fd, pidstr, strlen(pidstr), 0) != (ssize_t)strlen(pidstr)) {
217*aa772005SRobert Watson 		error = errno;
218*aa772005SRobert Watson 		_pidfile_remove(pfh, 0);
219*aa772005SRobert Watson 		errno = error;
220*aa772005SRobert Watson 		return (-1);
221*aa772005SRobert Watson 	}
222*aa772005SRobert Watson 
223*aa772005SRobert Watson 	return (0);
224*aa772005SRobert Watson }
225*aa772005SRobert Watson 
226*aa772005SRobert Watson static int
227*aa772005SRobert Watson pidfile_close(struct pidfh *pfh)
228*aa772005SRobert Watson {
229*aa772005SRobert Watson 	int error;
230*aa772005SRobert Watson 
231*aa772005SRobert Watson 	error = pidfile_verify(pfh);
232*aa772005SRobert Watson 	if (error != 0) {
233*aa772005SRobert Watson 		errno = error;
234*aa772005SRobert Watson 		return (-1);
235*aa772005SRobert Watson 	}
236*aa772005SRobert Watson 
237*aa772005SRobert Watson 	if (close(pfh->pf_fd) == -1)
238*aa772005SRobert Watson 		error = errno;
239*aa772005SRobert Watson 	free(pfh);
240*aa772005SRobert Watson 	if (error != 0) {
241*aa772005SRobert Watson 		errno = error;
242*aa772005SRobert Watson 		return (-1);
243*aa772005SRobert Watson 	}
244*aa772005SRobert Watson 	return (0);
245*aa772005SRobert Watson }
246*aa772005SRobert Watson 
247*aa772005SRobert Watson static int
248*aa772005SRobert Watson _pidfile_remove(struct pidfh *pfh, int freeit)
249*aa772005SRobert Watson {
250*aa772005SRobert Watson 	int error;
251*aa772005SRobert Watson 
252*aa772005SRobert Watson 	error = pidfile_verify(pfh);
253*aa772005SRobert Watson 	if (error != 0) {
254*aa772005SRobert Watson 		errno = error;
255*aa772005SRobert Watson 		return (-1);
256*aa772005SRobert Watson 	}
257*aa772005SRobert Watson 
258*aa772005SRobert Watson 	if (unlink(pfh->pf_path) == -1)
259*aa772005SRobert Watson 		error = errno;
260*aa772005SRobert Watson 	if (close(pfh->pf_fd) == -1) {
261*aa772005SRobert Watson 		if (error == 0)
262*aa772005SRobert Watson 			error = errno;
263*aa772005SRobert Watson 	}
264*aa772005SRobert Watson 	if (freeit)
265*aa772005SRobert Watson 		free(pfh);
266*aa772005SRobert Watson 	else
267*aa772005SRobert Watson 		pfh->pf_fd = -1;
268*aa772005SRobert Watson 	if (error != 0) {
269*aa772005SRobert Watson 		errno = error;
270*aa772005SRobert Watson 		return (-1);
271*aa772005SRobert Watson 	}
272*aa772005SRobert Watson 	return (0);
273*aa772005SRobert Watson }
274*aa772005SRobert Watson 
275*aa772005SRobert Watson static int
276*aa772005SRobert Watson pidfile_remove(struct pidfh *pfh)
277*aa772005SRobert Watson {
278*aa772005SRobert Watson 
279*aa772005SRobert Watson 	return (_pidfile_remove(pfh, 1));
280*aa772005SRobert Watson }
281*aa772005SRobert Watson 
282*aa772005SRobert Watson #if 0
283*aa772005SRobert Watson static int
284*aa772005SRobert Watson pidfile_fileno(const struct pidfh *pfh)
285*aa772005SRobert Watson {
286*aa772005SRobert Watson 
287*aa772005SRobert Watson 	if (pfh == NULL || pfh->pf_fd == -1) {
288*aa772005SRobert Watson 		errno = EINVAL;
289*aa772005SRobert Watson 		return (-1);
290*aa772005SRobert Watson 	}
291*aa772005SRobert Watson 	return (pfh->pf_fd);
292*aa772005SRobert Watson }
293*aa772005SRobert Watson #endif
294