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