1
2 /*
3 * Copyright (C) 2009 Citrix Ltd.
4 * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
5 * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published
9 * by the Free Software Foundation; version 2.1 only. with the special
10 * exception on linking described in file LICENSE.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
16 */
17
18 #include "libxl_osdeps.h" /* must come before any other headers */
19
20 #include "libxl_internal.h"
21
check_open_fds(const char * what)22 static void check_open_fds(const char *what)
23 {
24 const char *env_debug;
25 int debug;
26 int i, flags, badness = 0;
27
28 env_debug = getenv("_LIBXL_DEBUG_EXEC_FDS");
29 if (!env_debug) return;
30
31 debug = strtol(env_debug, (char **) NULL, 10);
32 if (debug <= 0) return;
33
34 for (i = 4; i < 256; i++) {
35 #ifdef __linux__
36 ssize_t len;
37 char path[PATH_MAX];
38 char linkpath[PATH_MAX+1];
39 #endif
40 flags = fcntl(i, F_GETFD);
41 if ( flags == -1 ) {
42 if ( errno != EBADF )
43 fprintf(stderr, "libxl: execing %s: fd %d flags returned %s (%d)\n",
44 what, i, strerror(errno), errno);
45 continue;
46 }
47
48 if ( flags & FD_CLOEXEC )
49 continue;
50
51 badness++;
52
53 #ifdef __linux__
54 snprintf(path, PATH_MAX, "/proc/%d/fd/%d", getpid(), i);
55 len = readlink(path, linkpath, PATH_MAX);
56 if (len > 0) {
57 linkpath[len] = '\0';
58 fprintf(stderr, "libxl: execing %s: fd %d is open to %s with flags %#x\n",
59 what, i, linkpath, flags);
60 } else
61 #endif
62 fprintf(stderr, "libxl: execing %s: fd %d is open with flags %#x\n",
63 what, i, flags);
64 }
65 if (debug < 2) return;
66 if (badness) abort();
67 }
68
libxl__exec(libxl__gc * gc,int stdinfd,int stdoutfd,int stderrfd,const char * arg0,char * const args[],char * const env[])69 void libxl__exec(libxl__gc *gc, int stdinfd, int stdoutfd, int stderrfd,
70 const char *arg0, char *const args[], char *const env[])
71 /* call this in the child */
72 {
73 if (stdinfd != -1)
74 dup2(stdinfd, STDIN_FILENO);
75 if (stdoutfd != -1)
76 dup2(stdoutfd, STDOUT_FILENO);
77 if (stderrfd != -1)
78 dup2(stderrfd, STDERR_FILENO);
79
80 if (stdinfd > 2)
81 close(stdinfd);
82 if (stdoutfd > 2 && stdoutfd != stdinfd)
83 close(stdoutfd);
84 if (stderrfd > 2 && stderrfd != stdinfd && stderrfd != stdoutfd)
85 close(stderrfd);
86
87 check_open_fds(arg0);
88
89 signal(SIGPIPE, SIG_DFL);
90 /* in case our caller set it to IGN. subprocesses are entitled
91 * to assume they got DFL. */
92
93 if (env != NULL) {
94 for (int i = 0; env[i] != NULL && env[i+1] != NULL; i += 2) {
95 if (setenv(env[i], env[i+1], 1) < 0) {
96 LOGEV(ERROR, errno, "setting env vars (%s = %s)",
97 env[i], env[i+1]);
98 goto out;
99 }
100 }
101 }
102 execvp(arg0, args);
103
104 out:
105 fprintf(stderr, "libxl: cannot execute %s: %s\n", arg0, strerror(errno));
106 _exit(-1);
107 }
108
libxl_report_child_exitstatus(libxl_ctx * ctx,xentoollog_level level,const char * what,pid_t pid,int status)109 void libxl_report_child_exitstatus(libxl_ctx *ctx,
110 xentoollog_level level,
111 const char *what, pid_t pid, int status)
112 {
113
114 if (WIFEXITED(status)) {
115 int st = WEXITSTATUS(status);
116 if (st)
117 LIBXL__LOG(ctx, level, "%s [%ld] exited"
118 " with error status %d", what, (unsigned long)pid, st);
119 else
120 LIBXL__LOG(ctx, level, "%s [%ld] unexpectedly"
121 " exited status zero", what, (unsigned long)pid);
122 } else if (WIFSIGNALED(status)) {
123 int sig = WTERMSIG(status);
124 const char *str = strsignal(sig);
125 const char *coredump = WCOREDUMP(status) ? " (core dumped)" : "";
126 if (str)
127 LIBXL__LOG(ctx, level, "%s [%ld] died due to"
128 " fatal signal %s%s", what, (unsigned long)pid,
129 str, coredump);
130 else
131 LIBXL__LOG(ctx, level, "%s [%ld] died due to unknown"
132 " fatal signal number %d%s", what, (unsigned long)pid,
133 sig, coredump);
134 } else {
135 LIBXL__LOG(ctx, level, "%s [%ld] gave unknown"
136 " wait status 0x%x", what, (unsigned long)pid, status);
137 }
138 }
139
libxl__spawn_record_pid(libxl__gc * gc,libxl__spawn_state * spawn,pid_t pid)140 int libxl__spawn_record_pid(libxl__gc *gc, libxl__spawn_state *spawn, pid_t pid)
141 {
142 int r, rc;
143
144 rc = libxl__ev_child_xenstore_reopen(gc, spawn->what);
145 if (rc) goto out;
146
147 r = libxl__xs_printf(gc, XBT_NULL, spawn->pidpath, "%d", pid);
148 if (r) {
149 LOGE(ERROR,
150 "write %s = %d: xenstore write failed", spawn->pidpath, pid);
151 rc = ERROR_FAIL; goto out;
152 }
153
154 rc = 0;
155
156 out:
157 return rc ? SIGTERM : 0;
158 }
159
160 /*----- spawn implementation -----*/
161
162 /*
163 * Full set of possible states of a libxl__spawn_state and its _detachable:
164 *
165 * detaching rc mid timeout xswatch
166 * - Undefined undef undef - undef undef
167 * - Idle any any Idle Idle Idle
168 * - Attached OK 0 0 Active Active Active
169 * - Attached Failed 0 non-0 Active Idle Idle
170 * - Detaching 1 maybe Active Idle Idle
171 * - Partial any any Idle Active/Idle Active/Idle
172 *
173 * When in states Detaching or Attached Failed, the middle process has
174 * been sent a SIGKILL.
175 *
176 * The difference between Attached OK and Attached Failed is not
177 * directly visible to callers - callers see these two the same,
178 * although of course Attached OK will hopefully eventually result in
179 * a call to detached_cb, whereas Attached Failed will end up
180 * in a call to failure_cb.
181 */
182
183 /* Event callbacks. */
184 static void spawn_watch_event(libxl__egc *egc, libxl__xswait_state *xswa,
185 int rc, const char *xsdata);
186 static void spawn_middle_death(libxl__egc *egc, libxl__ev_child *childw,
187 pid_t pid, int status);
188
189 /* Precondition: Partial. Results: Idle. */
190 static void spawn_cleanup(libxl__gc *gc, libxl__spawn_state *ss);
191
192 /* Precondition: Attached or Detaching; caller has logged failure reason.
193 * Results: Detaching, or Attached Failed */
194 static void spawn_fail(libxl__egc *egc, libxl__spawn_state *ss, int rc);
195
libxl__spawn_init(libxl__spawn_state * ss)196 void libxl__spawn_init(libxl__spawn_state *ss)
197 {
198 libxl__ev_child_init(&ss->mid);
199 libxl__xswait_init(&ss->xswait);
200 }
201
libxl__spawn_spawn(libxl__egc * egc,libxl__spawn_state * ss)202 int libxl__spawn_spawn(libxl__egc *egc, libxl__spawn_state *ss)
203 {
204 STATE_AO_GC(ss->ao);
205 int r;
206 pid_t child;
207 int status, rc;
208
209 libxl__spawn_init(ss);
210 ss->rc = ss->detaching = 0;
211
212 ss->xswait.ao = ao;
213 ss->xswait.what = GCSPRINTF("%s startup", ss->what);
214 ss->xswait.path = ss->xspath;
215 ss->xswait.timeout_ms = ss->timeout_ms;
216 ss->xswait.callback = spawn_watch_event;
217 rc = libxl__xswait_start(gc, &ss->xswait);
218 if (rc) goto out_err;
219
220 pid_t middle = libxl__ev_child_fork(gc, &ss->mid, spawn_middle_death);
221 if (middle ==-1) { rc = ERROR_FAIL; goto out_err; }
222
223 if (middle) {
224 /* parent */
225 return 1;
226 }
227
228 /* we are now the middle process */
229
230 pid_t (*fork_replacement)(void*) =
231 CTX->childproc_hooks
232 ? CTX->childproc_hooks->fork_replacement
233 : 0;
234 child =
235 fork_replacement
236 ? fork_replacement(CTX->childproc_user)
237 : fork();
238
239 if (child == -1)
240 exit(255);
241 if (!child) {
242 return 0; /* caller runs child code */
243 }
244
245 int failsig = ss->midproc_cb(gc, ss, child);
246 if (failsig) {
247 kill(child, failsig);
248 _exit(127);
249 }
250
251 for (;;) {
252 pid_t got = waitpid(child, &status, 0);
253 if (got == -1) {
254 assert(errno == EINTR);
255 continue;
256 }
257 assert(got == child);
258 break;
259 }
260
261 r = (WIFEXITED(status) && WEXITSTATUS(status) <= 127 ? WEXITSTATUS(status) :
262 WIFSIGNALED(status) && WTERMSIG(status) < 127 ? WTERMSIG(status)+128 :
263 -1);
264 _exit(r);
265
266 out_err:
267 spawn_cleanup(gc, ss);
268 return rc;
269 }
270
spawn_cleanup(libxl__gc * gc,libxl__spawn_state * ss)271 static void spawn_cleanup(libxl__gc *gc, libxl__spawn_state *ss)
272 {
273 assert(!libxl__ev_child_inuse(&ss->mid));
274 libxl__xswait_stop(gc, &ss->xswait);
275 }
276
spawn_detach(libxl__gc * gc,libxl__spawn_state * ss)277 static void spawn_detach(libxl__gc *gc, libxl__spawn_state *ss)
278 /* Precondition: Attached or Detaching, but caller must have just set
279 * at least one of detaching or rc.
280 * Results: Detaching or Attached Failed */
281 {
282 int r;
283
284 assert(libxl__ev_child_inuse(&ss->mid));
285 assert(ss->detaching || ss->rc);
286 libxl__xswait_stop(gc, &ss->xswait);
287
288 pid_t child = ss->mid.pid;
289 r = kill(child, SIGKILL);
290 if (r && errno != ESRCH)
291 LOGE(WARN, "%s: failed to kill intermediate child (pid=%lu)",
292 ss->what, (unsigned long)child);
293 }
294
libxl__spawn_initiate_detach(libxl__gc * gc,libxl__spawn_state * ss)295 void libxl__spawn_initiate_detach(libxl__gc *gc, libxl__spawn_state *ss)
296 {
297 ss->detaching = 1;
298 spawn_detach(gc, ss);
299 }
300
libxl__spawn_initiate_failure(libxl__egc * egc,libxl__spawn_state * ss,int rc)301 void libxl__spawn_initiate_failure(libxl__egc *egc, libxl__spawn_state *ss,
302 int rc)
303 /* The spawn state must be Attached on entry and will be Attached Failed
304 * on return. */
305 {
306 spawn_fail(egc, ss, rc);
307 }
308
spawn_fail(libxl__egc * egc,libxl__spawn_state * ss,int rc)309 static void spawn_fail(libxl__egc *egc, libxl__spawn_state *ss, int rc)
310 /* Caller must have logged. Must be last thing in calling function,
311 * as it may make the callback. Precondition: Attached or Detaching. */
312 {
313 EGC_GC;
314 assert(rc);
315 if (!ss->rc)
316 ss->rc = rc;
317 spawn_detach(gc, ss);
318 }
319
spawn_watch_event(libxl__egc * egc,libxl__xswait_state * xswa,int rc,const char * p)320 static void spawn_watch_event(libxl__egc *egc, libxl__xswait_state *xswa,
321 int rc, const char *p)
322 {
323 /* On entry, is Attached. */
324 EGC_GC;
325 libxl__spawn_state *ss = CONTAINER_OF(xswa, *ss, xswait);
326 if (rc) {
327 if (rc == ERROR_TIMEDOUT)
328 LOG(ERROR, "%s: startup timed out", ss->what);
329 spawn_fail(egc, ss, rc); /* must be last */
330 return;
331 }
332 LOG(DEBUG, "%s: spawn watch p=%s", ss->what, p);
333 ss->confirm_cb(egc, ss, p); /* must be last */
334 }
335
spawn_middle_death(libxl__egc * egc,libxl__ev_child * childw,pid_t pid,int status)336 static void spawn_middle_death(libxl__egc *egc, libxl__ev_child *childw,
337 pid_t pid, int status)
338 /* On entry, is Attached or Detaching */
339 {
340 EGC_GC;
341 libxl__spawn_state *ss = CONTAINER_OF(childw, *ss, mid);
342
343 if ((ss->rc || ss->detaching) &&
344 ((WIFEXITED(status) && WEXITSTATUS(status)==0) ||
345 (WIFSIGNALED(status) && WTERMSIG(status)==SIGKILL))) {
346 /* as expected */
347 const char *what = GCSPRINTF("%s (dying as expected)", ss->what);
348 libxl_report_child_exitstatus(CTX, XTL_DEBUG, what, pid, status);
349 } else if (!WIFEXITED(status)) {
350 int loglevel = ss->detaching ? XTL_WARN : XTL_ERROR;
351 const char *what =
352 GCSPRINTF("%s intermediate process (startup monitor)", ss->what);
353 libxl_report_child_exitstatus(CTX, loglevel, what, pid, status);
354 ss->rc = ERROR_FAIL;
355 } else {
356 if (!status)
357 LOG(ERROR, "%s [%ld]: unexpectedly exited with exit status 0,"
358 " when we were waiting for it to confirm startup",
359 ss->what, (unsigned long)pid);
360 else if (status <= 127)
361 LOG(ERROR, "%s [%ld]: failed startup with non-zero exit status %d",
362 ss->what, (unsigned long)pid, status);
363 else if (status < 255) {
364 int sig = status - 128;
365 const char *str = strsignal(sig);
366 if (str)
367 LOG(ERROR, "%s [%ld]: died during startup due to fatal"
368 " signal %s", ss->what, (unsigned long)pid, str);
369 else
370 LOG(ERROR, "%s [%ld]: died during startup due to unknown fatal"
371 " signal number %d", ss->what, (unsigned long)pid, sig);
372 }
373 ss->rc = ERROR_FAIL;
374 }
375
376 spawn_cleanup(gc, ss);
377
378 if (ss->rc && !ss->detaching) {
379 ss->failure_cb(egc, ss, ss->rc); /* must be last */
380 return;
381 }
382
383 if (ss->rc && ss->detaching)
384 LOG(WARN,"%s underlying machinery seemed to fail (%d),"
385 " but its function seems to have been successful",
386 ss->what, ss->rc);
387
388 assert(ss->detaching);
389 ss->detached_cb(egc, ss);
390 }
391
392 /*
393 * Local variables:
394 * mode: C
395 * c-basic-offset: 4
396 * indent-tabs-mode: nil
397 * End:
398 */
399