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