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