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