1 /*
2    daemoninze.c - functions for properly daemonising an application
3 
4    Copyright (C) 2014-2015 Arthur de Jong
5 
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10 
11    This library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15 
16    You should have received a copy of the GNU Lesser General Public
17    License along with this library; if not, write to the Free Software
18    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19    02110-1301 USA
20 */
21 
22 #include "config.h"
23 
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #ifdef HAVE_PTHREAD_H
33 #include <pthread.h>
34 #endif /* HAVE_PTHREAD_H */
35 
36 #include "daemonize.h"
37 #include "log.h"
38 
39 /* the write end of a pipe that is used to signal the fact that the child
40    process has finished initialising (see daemonize_daemon() and
41    daemonize_ready() for details) */
42 static int daemonizefd = -1;
43 
daemonize_closefds(void)44 void daemonize_closefds(void)
45 {
46   int i;
47   /* close all file descriptors (except stdin/out/err) */
48   i = sysconf(_SC_OPEN_MAX) - 1;
49   /* if the system does not have OPEN_MAX just close the first 32 and
50      hope we closed enough */
51   if (i < 0)
52     i = 32;
53   for (; i > 3; i--)
54     close(i);
55 }
56 
daemonize_redirect_stdio(void)57 void daemonize_redirect_stdio(void)
58 {
59   /* close stdin, stdout and stderr */
60   (void)close(0);   /* stdin */
61   (void)close(1);   /* stdout */
62   (void)close(2);   /* stderr */
63   /* reconnect to /dev/null */
64   (void)open("/dev/null", O_RDWR);  /* stdin, fd=0 */
65   (void)dup(0);     /* stdout, fd=1 */
66   (void)dup(0);     /* stderr, fd=2 */
67 }
68 
69 /* try to fill the buffer until EOF or error */
read_response(int fd,char * buffer,size_t bufsz)70 static int read_response(int fd, char *buffer, size_t bufsz)
71 {
72   int rc;
73   size_t r = 0;
74   while (r < bufsz)
75   {
76     rc = read(fd, buffer + r, bufsz - r);
77     if (rc == 0)
78       break;
79     else if (rc > 0)
80       r += rc;
81     else if ((errno == EINTR) || (errno == EAGAIN))
82       continue; /* ignore these errors and try again */
83     else
84     {
85       log_log(LOG_ERR, "read_response(): read() failed: %s", strerror(errno));
86       return -1;
87     }
88   }
89   return r;
90 }
91 
92 /* The parent process calling daemonize_daemon() will end up here on success */
wait_for_response(int fd)93 static int wait_for_response(int fd)
94 {
95   int i, l, rc;
96   char buffer[1024];
97   /* read return code */
98   errno = 0;
99   i = read_response(fd, (void *)&rc, sizeof(int));
100   log_log(LOG_DEBUG, "DEBUG: wait_for_response(): i=%d, rc=%d", i, rc);
101   if (i != sizeof(int))
102   {
103     log_log(LOG_ERR, "wait_for_response(): read_response() returned %d (expected %d)",
104             i, (int)sizeof(int));
105     if (errno == 0)
106 #ifdef ENODATA
107       errno = ENODATA;
108 #else
109       errno = ENOATTR;
110 #endif
111     return -1;
112   }
113   /* read string length */
114   i = read_response(fd, (void *)&l, sizeof(int));
115   log_log(LOG_DEBUG, "DEBUG: wait_for_response(): i=%d, l=%d", i, l);
116   if ((i != sizeof(int)) || (l <= 0))
117     _exit(rc);
118   /* read string */
119   if ((size_t)l > (sizeof(buffer) - 1))
120     l = sizeof(buffer) - 1;
121   i = read_response(fd, buffer, l);
122   buffer[l] = '\0';
123   if (i == l)
124     fprintf(stderr, "%s", buffer);
125   _exit(rc);
126 }
127 
closefd(void)128 static void closefd(void)
129 {
130   if (daemonizefd >= 0)
131   {
132     close(daemonizefd);
133     daemonizefd = -1;
134   }
135 }
136 
daemonize_daemon(void)137 int daemonize_daemon(void)
138 {
139   int pipefds[2];
140   int i;
141   /* set up a pipe for communication */
142   if (pipe(pipefds) < 0)
143   {
144     log_log(LOG_ERR, "pipe() failed: %s", strerror(errno));
145     return -1;
146   }
147   /* set O_NONBLOCK on the write end to ensure that a call to
148      daemonize_ready() will not lock the application */
149   if ((i = fcntl(pipefds[1], F_GETFL, 0)) < 0)
150   {
151     log_log(LOG_ERR, "fcntl() failed: %s", strerror(errno));
152     close(pipefds[0]);
153     close(pipefds[1]);
154     return -1;
155   }
156   if (fcntl(pipefds[1], F_SETFL, i | O_NONBLOCK) < 0)
157   {
158     log_log(LOG_ERR, "fcntl() failed: %s", strerror(errno));
159     close(pipefds[0]);
160     close(pipefds[1]);
161     return -1;
162   }
163   /* fork() and exit() to detach from the parent process */
164   switch (fork())
165   {
166     case 0:
167       /* we are the child, close read end of pipe */
168       close(pipefds[0]);
169       break;
170     case -1:
171       /* we are the parent, but have an error */
172       log_log(LOG_ERR, "fork() failed: %s", strerror(errno));
173       close(pipefds[0]);
174       close(pipefds[1]);
175       return -1;
176     default:
177       /* we are the parent, close write end and wait for information */
178       close(pipefds[1]);
179       return wait_for_response(pipefds[0]);
180   }
181   /* become process leader */
182   if (setsid() < 0)
183   {
184     log_log(LOG_ERR, "setsid() failed: %s", strerror(errno));
185     close(pipefds[1]);
186     _exit(EXIT_FAILURE);
187   }
188   /* fork again so we cannot allocate a pty */
189   switch (fork())
190   {
191     case 0:
192       /* we are the child */
193       break;
194     case -1:
195       /* we are the parent, but have an error */
196       log_log(LOG_ERR, "fork() failed: %s", strerror(errno));
197       close(pipefds[1]);
198       _exit(EXIT_FAILURE);
199     default:
200       /* we are the parent and we're done */
201       close(pipefds[1]);
202       _exit(EXIT_SUCCESS);
203   }
204   daemonizefd = pipefds[1];
205   /* close the file descriptor on exec (ignore errors) */
206   fcntl(daemonizefd, F_SETFD, FD_CLOEXEC);
207 #ifdef HAVE_PTHREAD_ATFORK
208   /* handle any other forks by closing daemonizefd first */
209   (void)pthread_atfork(NULL, NULL, closefd);
210 #endif /* HAVE_PTHREAD_ATFORK */
211   return 0;
212 }
213 
daemonize_ready(int status,const char * message)214 void daemonize_ready(int status, const char *message)
215 {
216   int l;
217   if (daemonizefd >= 0)
218   {
219     /* we ignore any errors writing */
220     (void)write(daemonizefd, &status, sizeof(int));
221     if ((message == NULL) || (message[0] == '\0'))
222     {
223       l = 0;
224       (void)write(daemonizefd, &l, sizeof(int));
225     }
226     else
227     {
228       l = strlen(message);
229       (void)write(daemonizefd, &l, sizeof(int));
230       (void)write(daemonizefd, message, l);
231     }
232     (void)close(daemonizefd);
233     daemonizefd = -1;
234   }
235 }
236