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 */
28*aa772005SRobert Watson
29*aa772005SRobert Watson #include <sys/param.h>
30*aa772005SRobert Watson #include <sys/file.h>
31*aa772005SRobert Watson #include <sys/stat.h>
32*aa772005SRobert Watson
33*aa772005SRobert Watson #include <stdio.h>
34*aa772005SRobert Watson #include <stdlib.h>
35*aa772005SRobert Watson #include <unistd.h>
36*aa772005SRobert Watson #include <fcntl.h>
37*aa772005SRobert Watson #include <string.h>
38*aa772005SRobert Watson #include <time.h>
39*aa772005SRobert Watson #include <err.h>
40*aa772005SRobert Watson #include <errno.h>
41*aa772005SRobert Watson
42*aa772005SRobert Watson #include "flopen.h"
43*aa772005SRobert Watson
44*aa772005SRobert Watson struct pidfh {
45*aa772005SRobert Watson int pf_fd;
46*aa772005SRobert Watson char pf_path[MAXPATHLEN + 1];
47*aa772005SRobert Watson dev_t pf_dev;
48*aa772005SRobert Watson ino_t pf_ino;
49*aa772005SRobert Watson };
50*aa772005SRobert Watson
51*aa772005SRobert Watson static int _pidfile_remove(struct pidfh *pfh, int freeit);
52*aa772005SRobert Watson
53*aa772005SRobert Watson static int
pidfile_verify(const struct pidfh * pfh)54*aa772005SRobert Watson pidfile_verify(const struct pidfh *pfh)
55*aa772005SRobert Watson {
56*aa772005SRobert Watson struct stat sb;
57*aa772005SRobert Watson
58*aa772005SRobert Watson if (pfh == NULL || pfh->pf_fd == -1)
59*aa772005SRobert Watson return (EINVAL);
60*aa772005SRobert Watson /*
61*aa772005SRobert Watson * Check remembered descriptor.
62*aa772005SRobert Watson */
63*aa772005SRobert Watson if (fstat(pfh->pf_fd, &sb) == -1)
64*aa772005SRobert Watson return (errno);
65*aa772005SRobert Watson if (sb.st_dev != pfh->pf_dev || sb.st_ino != pfh->pf_ino)
66*aa772005SRobert Watson return (EINVAL);
67*aa772005SRobert Watson return (0);
68*aa772005SRobert Watson }
69*aa772005SRobert Watson
70*aa772005SRobert Watson static int
pidfile_read(const char * path,pid_t * pidptr)71*aa772005SRobert Watson pidfile_read(const char *path, pid_t *pidptr)
72*aa772005SRobert Watson {
73*aa772005SRobert Watson char buf[16], *endptr;
74*aa772005SRobert Watson int error, fd, i;
75*aa772005SRobert Watson
76*aa772005SRobert Watson fd = open(path, O_RDONLY);
77*aa772005SRobert Watson if (fd == -1)
78*aa772005SRobert Watson return (errno);
79*aa772005SRobert Watson
80*aa772005SRobert Watson i = read(fd, buf, sizeof(buf) - 1);
81*aa772005SRobert Watson error = errno; /* Remember errno in case close() wants to change it. */
82*aa772005SRobert Watson close(fd);
83*aa772005SRobert Watson if (i == -1)
84*aa772005SRobert Watson return (error);
85*aa772005SRobert Watson else if (i == 0)
86*aa772005SRobert Watson return (EAGAIN);
87*aa772005SRobert Watson buf[i] = '\0';
88*aa772005SRobert Watson
89*aa772005SRobert Watson *pidptr = strtol(buf, &endptr, 10);
90*aa772005SRobert Watson if (endptr != &buf[i])
91*aa772005SRobert Watson return (EINVAL);
92*aa772005SRobert Watson
93*aa772005SRobert Watson return (0);
94*aa772005SRobert Watson }
95*aa772005SRobert Watson
96*aa772005SRobert Watson static struct pidfh *
pidfile_open(const char * path,mode_t mode,pid_t * pidptr)97*aa772005SRobert Watson pidfile_open(const char *path, mode_t mode, pid_t *pidptr)
98*aa772005SRobert Watson {
99*aa772005SRobert Watson struct pidfh *pfh;
100*aa772005SRobert Watson struct stat sb;
101*aa772005SRobert Watson int error, fd, len, count;
102*aa772005SRobert Watson struct timespec rqtp;
103*aa772005SRobert Watson
104*aa772005SRobert Watson if (pidptr != NULL)
105*aa772005SRobert Watson *pidptr = -1;
106*aa772005SRobert Watson
107*aa772005SRobert Watson if (path == NULL)
108*aa772005SRobert Watson return (NULL);
109*aa772005SRobert Watson
110*aa772005SRobert Watson pfh = malloc(sizeof(*pfh));
111*aa772005SRobert Watson if (pfh == NULL)
112*aa772005SRobert Watson return (NULL);
113*aa772005SRobert Watson
114*aa772005SRobert Watson len = snprintf(pfh->pf_path, sizeof(pfh->pf_path),
115*aa772005SRobert Watson "%s", path);
116*aa772005SRobert Watson if (len >= (int)sizeof(pfh->pf_path)) {
117*aa772005SRobert Watson free(pfh);
118*aa772005SRobert Watson errno = ENAMETOOLONG;
119*aa772005SRobert Watson return (NULL);
120*aa772005SRobert Watson }
121*aa772005SRobert Watson
122*aa772005SRobert Watson /*
123*aa772005SRobert Watson * Open the PID file and obtain exclusive lock.
124*aa772005SRobert Watson * We truncate PID file here only to remove old PID immediatelly,
125*aa772005SRobert Watson * PID file will be truncated again in pidfile_write(), so
126*aa772005SRobert Watson * pidfile_write() can be called multiple times.
127*aa772005SRobert Watson */
128*aa772005SRobert Watson fd = flopen(pfh->pf_path,
129*aa772005SRobert Watson #ifdef O_CLOEXEC
130*aa772005SRobert Watson O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK | O_CLOEXEC, mode);
131*aa772005SRobert Watson #else
132*aa772005SRobert Watson O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, mode);
133*aa772005SRobert Watson #endif
134*aa772005SRobert Watson if (fd == -1) {
135*aa772005SRobert Watson if (errno == EWOULDBLOCK && pidptr != NULL) {
136*aa772005SRobert Watson count = 20;
137*aa772005SRobert Watson rqtp.tv_sec = 0;
138*aa772005SRobert Watson rqtp.tv_nsec = 5000000;
139*aa772005SRobert Watson for (;;) {
140*aa772005SRobert Watson errno = pidfile_read(pfh->pf_path, pidptr);
141*aa772005SRobert Watson if (errno != EAGAIN || --count == 0)
142*aa772005SRobert Watson break;
143*aa772005SRobert Watson nanosleep(&rqtp, 0);
144*aa772005SRobert Watson }
145*aa772005SRobert Watson if (errno == EAGAIN)
146*aa772005SRobert Watson *pidptr = -1;
147*aa772005SRobert Watson if (errno == 0 || errno == EAGAIN)
148*aa772005SRobert Watson errno = EEXIST;
149*aa772005SRobert Watson }
150*aa772005SRobert Watson free(pfh);
151*aa772005SRobert Watson return (NULL);
152*aa772005SRobert Watson }
153*aa772005SRobert Watson
154*aa772005SRobert Watson #ifndef O_CLOEXEC
155*aa772005SRobert Watson if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
156*aa772005SRobert Watson error = errno;
157*aa772005SRobert Watson unlink(pfh->pf_path);
158*aa772005SRobert Watson close(fd);
159*aa772005SRobert Watson free(pfh);
160*aa772005SRobert Watson errno = error;
161*aa772005SRobert Watson return (NULL);
162*aa772005SRobert Watson }
163*aa772005SRobert Watson #endif
164*aa772005SRobert Watson
165*aa772005SRobert Watson /*
166*aa772005SRobert Watson * Remember file information, so in pidfile_write() we are sure we write
167*aa772005SRobert Watson * to the proper descriptor.
168*aa772005SRobert Watson */
169*aa772005SRobert Watson if (fstat(fd, &sb) == -1) {
170*aa772005SRobert Watson error = errno;
171*aa772005SRobert Watson unlink(pfh->pf_path);
172*aa772005SRobert Watson close(fd);
173*aa772005SRobert Watson free(pfh);
174*aa772005SRobert Watson errno = error;
175*aa772005SRobert Watson return (NULL);
176*aa772005SRobert Watson }
177*aa772005SRobert Watson
178*aa772005SRobert Watson pfh->pf_fd = fd;
179*aa772005SRobert Watson pfh->pf_dev = sb.st_dev;
180*aa772005SRobert Watson pfh->pf_ino = sb.st_ino;
181*aa772005SRobert Watson
182*aa772005SRobert Watson return (pfh);
183*aa772005SRobert Watson }
184*aa772005SRobert Watson
185*aa772005SRobert Watson static int
pidfile_write(struct pidfh * pfh)186*aa772005SRobert Watson pidfile_write(struct pidfh *pfh)
187*aa772005SRobert Watson {
188*aa772005SRobert Watson char pidstr[16];
189*aa772005SRobert Watson int error, fd;
190*aa772005SRobert Watson
191*aa772005SRobert Watson /*
192*aa772005SRobert Watson * Check remembered descriptor, so we don't overwrite some other
193*aa772005SRobert Watson * file if pidfile was closed and descriptor reused.
194*aa772005SRobert Watson */
195*aa772005SRobert Watson errno = pidfile_verify(pfh);
196*aa772005SRobert Watson if (errno != 0) {
197*aa772005SRobert Watson /*
198*aa772005SRobert Watson * Don't close descriptor, because we are not sure if it's ours.
199*aa772005SRobert Watson */
200*aa772005SRobert Watson return (-1);
201*aa772005SRobert Watson }
202*aa772005SRobert Watson fd = pfh->pf_fd;
203*aa772005SRobert Watson
204*aa772005SRobert Watson /*
205*aa772005SRobert Watson * Truncate PID file, so multiple calls of pidfile_write() are allowed.
206*aa772005SRobert Watson */
207*aa772005SRobert Watson if (ftruncate(fd, 0) == -1) {
208*aa772005SRobert Watson error = errno;
209*aa772005SRobert Watson _pidfile_remove(pfh, 0);
210*aa772005SRobert Watson errno = error;
211*aa772005SRobert Watson return (-1);
212*aa772005SRobert Watson }
213*aa772005SRobert Watson
214*aa772005SRobert Watson snprintf(pidstr, sizeof(pidstr), "%u", getpid());
215*aa772005SRobert Watson if (pwrite(fd, pidstr, strlen(pidstr), 0) != (ssize_t)strlen(pidstr)) {
216*aa772005SRobert Watson error = errno;
217*aa772005SRobert Watson _pidfile_remove(pfh, 0);
218*aa772005SRobert Watson errno = error;
219*aa772005SRobert Watson return (-1);
220*aa772005SRobert Watson }
221*aa772005SRobert Watson
222*aa772005SRobert Watson return (0);
223*aa772005SRobert Watson }
224*aa772005SRobert Watson
225*aa772005SRobert Watson static int
pidfile_close(struct pidfh * pfh)226*aa772005SRobert Watson pidfile_close(struct pidfh *pfh)
227*aa772005SRobert Watson {
228*aa772005SRobert Watson int error;
229*aa772005SRobert Watson
230*aa772005SRobert Watson error = pidfile_verify(pfh);
231*aa772005SRobert Watson if (error != 0) {
232*aa772005SRobert Watson errno = error;
233*aa772005SRobert Watson return (-1);
234*aa772005SRobert Watson }
235*aa772005SRobert Watson
236*aa772005SRobert Watson if (close(pfh->pf_fd) == -1)
237*aa772005SRobert Watson error = errno;
238*aa772005SRobert Watson free(pfh);
239*aa772005SRobert Watson if (error != 0) {
240*aa772005SRobert Watson errno = error;
241*aa772005SRobert Watson return (-1);
242*aa772005SRobert Watson }
243*aa772005SRobert Watson return (0);
244*aa772005SRobert Watson }
245*aa772005SRobert Watson
246*aa772005SRobert Watson static int
_pidfile_remove(struct pidfh * pfh,int freeit)247*aa772005SRobert Watson _pidfile_remove(struct pidfh *pfh, int freeit)
248*aa772005SRobert Watson {
249*aa772005SRobert Watson int error;
250*aa772005SRobert Watson
251*aa772005SRobert Watson error = pidfile_verify(pfh);
252*aa772005SRobert Watson if (error != 0) {
253*aa772005SRobert Watson errno = error;
254*aa772005SRobert Watson return (-1);
255*aa772005SRobert Watson }
256*aa772005SRobert Watson
257*aa772005SRobert Watson if (unlink(pfh->pf_path) == -1)
258*aa772005SRobert Watson error = errno;
259*aa772005SRobert Watson if (close(pfh->pf_fd) == -1) {
260*aa772005SRobert Watson if (error == 0)
261*aa772005SRobert Watson error = errno;
262*aa772005SRobert Watson }
263*aa772005SRobert Watson if (freeit)
264*aa772005SRobert Watson free(pfh);
265*aa772005SRobert Watson else
266*aa772005SRobert Watson pfh->pf_fd = -1;
267*aa772005SRobert Watson if (error != 0) {
268*aa772005SRobert Watson errno = error;
269*aa772005SRobert Watson return (-1);
270*aa772005SRobert Watson }
271*aa772005SRobert Watson return (0);
272*aa772005SRobert Watson }
273*aa772005SRobert Watson
274*aa772005SRobert Watson static int
pidfile_remove(struct pidfh * pfh)275*aa772005SRobert Watson pidfile_remove(struct pidfh *pfh)
276*aa772005SRobert Watson {
277*aa772005SRobert Watson
278*aa772005SRobert Watson return (_pidfile_remove(pfh, 1));
279*aa772005SRobert Watson }
280*aa772005SRobert Watson
281*aa772005SRobert Watson #if 0
282*aa772005SRobert Watson static int
283*aa772005SRobert Watson pidfile_fileno(const struct pidfh *pfh)
284*aa772005SRobert Watson {
285*aa772005SRobert Watson
286*aa772005SRobert Watson if (pfh == NULL || pfh->pf_fd == -1) {
287*aa772005SRobert Watson errno = EINVAL;
288*aa772005SRobert Watson return (-1);
289*aa772005SRobert Watson }
290*aa772005SRobert Watson return (pfh->pf_fd);
291*aa772005SRobert Watson }
292*aa772005SRobert Watson #endif
293