roken: fix valgrind leak noise
[metze/heimdal/wip.git] / lib / roken / detach.c
1 /*-
2  * Copyright (c) 2015
3  *      Cryptonector LLC.  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  * 3. Cryptonector LLC may not be used to endorse or promote products
14  *    derived from this software without specific prior written
15  *    permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <config.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #ifdef WIN32
34 #include <io.h>
35 #include <stdlib.h>
36 #else
37 #include <unistd.h>
38 #endif
39 #include "roken.h"
40
41 #ifdef WIN32
42 #define dup2 _dup2
43 #endif
44
45 static int pipefds[2] = {-1, -1};
46
47 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
48 roken_detach_prep(int argc, char **argv, char *special_arg)
49 {
50     ssize_t bytes;
51     size_t i;
52     pid_t child;
53     char **new_argv;
54     char buf[1];
55     char fildes[21];
56     int status;
57
58     pipefds[0] = -1;
59     pipefds[1] = -1;
60
61 #ifdef WIN32
62     if (_pipe(pipefds, 4, _O_NOINHERIT | O_BINARY) == -1)
63         err(1, "failed to setup to detach daemon (_pipe failed)");
64 #else
65     if (pipe(pipefds) == -1)
66         err(1, "failed to setup to detach daemon (pipe failed)");
67 #endif
68
69     new_argv = calloc(argc + 3, sizeof(*new_argv));
70     if (new_argv == NULL)
71         err(1, "Out of memory");
72
73 #ifdef WIN32
74     pipefds[1] = _dup(pipefds[1]); /* The new fd will be inherited */
75     if (pipefds[1] == -1)
76         err(1, "Out of memory");
77 #else
78     fcntl(pipefds[1], F_SETFD, fcntl(pipefds[1], F_GETFD & ~(O_CLOEXEC)));
79 #endif
80
81     if (snprintf(fildes, sizeof(fildes), "%d", pipefds[1]) >= sizeof(fildes))
82         err(1, "failed to setup to detach daemon (fd number %d too large)",
83             pipefds[1]);
84
85     new_argv[0] = argv[0];
86     new_argv[1] = special_arg;
87     new_argv[2] = fildes;
88     for (i = 1; argv[i] != NULL; i++)
89         new_argv[i + 2] = argv[i];
90     new_argv[argc + 2] = NULL;
91
92 #ifndef WIN32
93     fflush(stdout);
94     child = fork();
95 #else
96     {
97         intptr_t child_handle;
98
99         _flushall();
100         child_handle = spawnvp(_P_NOWAIT, argv[0], new_argv);
101         if (child_handle == -1)
102           child = (pid_t)-1;
103         else
104           child = GetProcessId((HANDLE)child_handle);
105     }
106 #endif
107     if (child == (pid_t)-1)
108         err(1, "failed to setup to fork daemon (fork failed)");
109
110 #ifndef WIN32
111     if (child == 0) {
112         int fd;
113
114         (void) close(pipefds[0]);
115         pipefds[0] = -1;
116         /*
117          * Keep stdout/stderr for now so output and errors prior to
118          * detach_finish() can be seen by the user.
119          */
120         fd = open(_PATH_DEVNULL, O_RDWR, 0);
121         if (fd == -1)
122             err(1, "failed to open /dev/null");
123         (void) dup2(fd, STDIN_FILENO);
124         if (fd > STDERR_FILENO)
125             (void) close(fd);
126         if (getenv("ROKEN_DETACH_USE_EXEC")) {
127             (void) execvp(argv[0], new_argv);
128             err(1, "failed to self-re-exec");
129         }
130         free(new_argv);
131         return pipefds[1];
132     }
133 #endif
134
135     /* Parent */
136     free(new_argv);
137     (void) close(pipefds[1]);
138     pipefds[1] = -1;
139     do {
140         bytes = read(pipefds[0], buf, sizeof(buf));
141     } while (bytes == -1 && errno == EINTR);
142     (void) close(pipefds[0]);
143     pipefds[0] = -1;
144     if (bytes == -1) {
145         /*
146          * No need to wait for the process.  We've killed it.  If it
147          * doesn't want to exit, we'd have to wait potentially forever,
148          * but we want to indicate failure to the user as soon as
149          * possible.  A wait with timeout would end the same way
150          * (attempting to kill the process).
151          */
152         err(1, "failed to setup daemon child (read from child pipe)");
153     }
154     if (bytes == 0) {
155         warnx("daemon child preparation failed, waiting for child");
156         status = wait_for_process(child);
157         if (SE_IS_ERROR(status) || SE_PROCSTATUS(status) != 0)
158             errx(SE_PROCSTATUS(status),
159                  "daemon child preparation failed (child exited)");
160     }
161     _exit(0);
162     /* NOTREACHED */
163     return -1;
164 }
165
166 #ifdef WIN32
167 #ifdef dup2
168 #undef dup2
169 #endif
170 #define dup2 _dup2
171 #endif
172
173 ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
174 roken_detach_finish(const char *dir, int daemon_child_fd)
175 {
176     char buf[1] = "";
177     ssize_t bytes;
178     int fd;
179
180     rk_pidfile(NULL);
181     if (pipefds[1] == -1 && daemon_child_fd != -1)
182         pipefds[1] = daemon_child_fd;
183     if (pipefds[0] != -1)
184         (void) close(pipefds[0]);
185     if (pipefds[1] == -1)
186         return;
187
188 #ifdef HAVE_SETSID
189     if (setsid() == -1)
190         err(1, "failed to detach from tty");
191 #endif
192
193 #ifndef WIN32
194     /*
195      * Hopefully we've written any pidfiles by now, if they had to be in
196      * the current directory...
197      *
198      * The daemons do re-open logs and so on, therefore this chdir()
199      * call needs to be optional for testing.
200      */
201     if (dir != NULL && chdir(dir) == -1)
202         err(1, "failed to chdir to /");
203 #endif
204
205     do {
206         bytes = write(pipefds[1], buf, sizeof(buf));
207     } while (bytes == -1 && errno == EINTR);
208     if (bytes == -1)
209         err(1, "failed to signal parent while detaching");
210     (void) close(pipefds[1]);
211     if (bytes != sizeof(buf))
212         errx(1, "failed to signal parent while detaching");
213
214     fd = open(_PATH_DEVNULL, O_RDWR, 0);
215     if (fd == -1)
216         err(1, "failed to open /dev/null");
217     /*
218      * Maybe we should check that our output got written, if redirected
219      * to a file.  File utils normally do this.
220      */
221     (void) dup2(fd, STDOUT_FILENO);
222     (void) dup2(fd, STDERR_FILENO);
223     if (fd > 2)
224         (void) close(fd);
225 }