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