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 
libxl__xenstore_child_wait_deprecated(libxl__gc * gc,uint32_t domid,uint32_t timeout,char * what,char * path,char * state,libxl__spawn_starting * spawning,int (* check_callback)(libxl__gc * gc,uint32_t domid,const char * state,void * userdata),void * check_callback_userdata)160 int libxl__xenstore_child_wait_deprecated(libxl__gc *gc,
161                                  uint32_t domid,
162                                  uint32_t timeout, char *what,
163                                  char *path, char *state,
164                                  libxl__spawn_starting *spawning,
165                                  int (*check_callback)(libxl__gc *gc,
166                                                        uint32_t domid,
167                                                        const char *state,
168                                                        void *userdata),
169                                  void *check_callback_userdata)
170 {
171     char *p;
172     unsigned int len;
173     int rc = 0;
174     struct xs_handle *xsh;
175     int nfds;
176     fd_set rfds;
177     struct timeval tv;
178     unsigned int num;
179     char **l = NULL;
180 
181     xsh = xs_daemon_open();
182     if (xsh == NULL) {
183         LOG(ERROR, "Unable to open xenstore connection");
184         goto err;
185     }
186 
187     xs_watch(xsh, path, path);
188     tv.tv_sec = timeout;
189     tv.tv_usec = 0;
190     nfds = xs_fileno(xsh) + 1;
191     assert(!spawning);
192 
193     while (rc > 0 || (!rc && tv.tv_sec > 0)) {
194         p = xs_read(xsh, XBT_NULL, path, &len);
195         if ( NULL == p )
196             goto again;
197 
198         if ( NULL != state && strcmp(p, state) )
199             goto again;
200 
201         if ( NULL != check_callback ) {
202             rc = (*check_callback)(gc, domid, p, check_callback_userdata);
203             if ( rc > 0 )
204                 goto again;
205         }
206 
207         free(p);
208         xs_unwatch(xsh, path, path);
209         xs_daemon_close(xsh);
210         return rc;
211 again:
212         free(p);
213         FD_ZERO(&rfds);
214         FD_SET(xs_fileno(xsh), &rfds);
215         rc = select(nfds, &rfds, NULL, NULL, &tv);
216         if (rc > 0) {
217             if (FD_ISSET(xs_fileno(xsh), &rfds)) {
218                 l = xs_read_watch(xsh, &num);
219                 if (l != NULL)
220                     free(l);
221                 else
222                     goto again;
223             }
224         }
225     }
226     LOG(ERROR, "%s not ready", what);
227 
228     xs_unwatch(xsh, path, path);
229     xs_daemon_close(xsh);
230 err:
231     return -1;
232 }
233 
234 
235 /*----- spawn implementation -----*/
236 
237 /*
238  * Full set of possible states of a libxl__spawn_state and its _detachable:
239  *
240  *                   detaching rc      mid     timeout      xswatch
241  *  - Undefined         undef   undef   -        undef        undef
242  *  - Idle              any     any     Idle     Idle         Idle
243  *  - Attached OK       0       0       Active   Active       Active
244  *  - Attached Failed   0       non-0   Active   Idle         Idle
245  *  - Detaching         1       maybe   Active   Idle         Idle
246  *  - Partial           any     any     Idle     Active/Idle  Active/Idle
247  *
248  * When in states Detaching or Attached Failed, the middle process has
249  * been sent a SIGKILL.
250  *
251  * The difference between Attached OK and Attached Failed is not
252  * directly visible to callers - callers see these two the same,
253  * although of course Attached OK will hopefully eventually result in
254  * a call to detached_cb, whereas Attached Failed will end up
255  * in a call to failure_cb.
256  */
257 
258 /* Event callbacks. */
259 static void spawn_watch_event(libxl__egc *egc, libxl__xswait_state *xswa,
260                               int rc, const char *xsdata);
261 static void spawn_middle_death(libxl__egc *egc, libxl__ev_child *childw,
262                                pid_t pid, int status);
263 
264 /* Precondition: Partial.  Results: Idle. */
265 static void spawn_cleanup(libxl__gc *gc, libxl__spawn_state *ss);
266 
267 /* Precondition: Attached or Detaching; caller has logged failure reason.
268  * Results: Detaching, or Attached Failed */
269 static void spawn_fail(libxl__egc *egc, libxl__spawn_state *ss, int rc);
270 
libxl__spawn_init(libxl__spawn_state * ss)271 void libxl__spawn_init(libxl__spawn_state *ss)
272 {
273     libxl__ev_child_init(&ss->mid);
274     libxl__xswait_init(&ss->xswait);
275 }
276 
libxl__spawn_spawn(libxl__egc * egc,libxl__spawn_state * ss)277 int libxl__spawn_spawn(libxl__egc *egc, libxl__spawn_state *ss)
278 {
279     STATE_AO_GC(ss->ao);
280     int r;
281     pid_t child;
282     int status, rc;
283 
284     libxl__spawn_init(ss);
285     ss->rc = ss->detaching = 0;
286 
287     ss->xswait.ao = ao;
288     ss->xswait.what = GCSPRINTF("%s startup", ss->what);
289     ss->xswait.path = ss->xspath;
290     ss->xswait.timeout_ms = ss->timeout_ms;
291     ss->xswait.callback = spawn_watch_event;
292     rc = libxl__xswait_start(gc, &ss->xswait);
293     if (rc) goto out_err;
294 
295     pid_t middle = libxl__ev_child_fork(gc, &ss->mid, spawn_middle_death);
296     if (middle ==-1) { rc = ERROR_FAIL; goto out_err; }
297 
298     if (middle) {
299         /* parent */
300         return 1;
301     }
302 
303     /* we are now the middle process */
304 
305     pid_t (*fork_replacement)(void*) =
306         CTX->childproc_hooks
307         ? CTX->childproc_hooks->fork_replacement
308         : 0;
309     child =
310         fork_replacement
311         ? fork_replacement(CTX->childproc_user)
312         : fork();
313 
314     if (child == -1)
315         exit(255);
316     if (!child) {
317         return 0; /* caller runs child code */
318     }
319 
320     int failsig = ss->midproc_cb(gc, ss, child);
321     if (failsig) {
322         kill(child, failsig);
323         _exit(127);
324     }
325 
326     for (;;) {
327         pid_t got = waitpid(child, &status, 0);
328         if (got == -1) {
329             assert(errno == EINTR);
330             continue;
331         }
332         assert(got == child);
333         break;
334     }
335 
336     r = (WIFEXITED(status) && WEXITSTATUS(status) <= 127 ? WEXITSTATUS(status) :
337          WIFSIGNALED(status) && WTERMSIG(status) < 127 ? WTERMSIG(status)+128 :
338          -1);
339     _exit(r);
340 
341  out_err:
342     spawn_cleanup(gc, ss);
343     return rc;
344 }
345 
spawn_cleanup(libxl__gc * gc,libxl__spawn_state * ss)346 static void spawn_cleanup(libxl__gc *gc, libxl__spawn_state *ss)
347 {
348     assert(!libxl__ev_child_inuse(&ss->mid));
349     libxl__xswait_stop(gc, &ss->xswait);
350 }
351 
spawn_detach(libxl__gc * gc,libxl__spawn_state * ss)352 static void spawn_detach(libxl__gc *gc, libxl__spawn_state *ss)
353 /* Precondition: Attached or Detaching, but caller must have just set
354  * at least one of detaching or rc.
355  * Results: Detaching or Attached Failed */
356 {
357     int r;
358 
359     assert(libxl__ev_child_inuse(&ss->mid));
360     assert(ss->detaching || ss->rc);
361     libxl__xswait_stop(gc, &ss->xswait);
362 
363     pid_t child = ss->mid.pid;
364     r = kill(child, SIGKILL);
365     if (r && errno != ESRCH)
366         LOGE(WARN, "%s: failed to kill intermediate child (pid=%lu)",
367              ss->what, (unsigned long)child);
368 }
369 
libxl__spawn_initiate_detach(libxl__gc * gc,libxl__spawn_state * ss)370 void libxl__spawn_initiate_detach(libxl__gc *gc, libxl__spawn_state *ss)
371 {
372     ss->detaching = 1;
373     spawn_detach(gc, ss);
374 }
375 
libxl__spawn_initiate_failure(libxl__egc * egc,libxl__spawn_state * ss,int rc)376 void libxl__spawn_initiate_failure(libxl__egc *egc, libxl__spawn_state *ss,
377                                    int rc)
378 /* The spawn state must be Attached on entry and will be Attached Failed
379  * on return.  */
380 {
381     spawn_fail(egc, ss, rc);
382 }
383 
spawn_fail(libxl__egc * egc,libxl__spawn_state * ss,int rc)384 static void spawn_fail(libxl__egc *egc, libxl__spawn_state *ss, int rc)
385 /* Caller must have logged.  Must be last thing in calling function,
386  * as it may make the callback.  Precondition: Attached or Detaching. */
387 {
388     EGC_GC;
389     assert(rc);
390     if (!ss->rc)
391         ss->rc = rc;
392     spawn_detach(gc, ss);
393 }
394 
spawn_watch_event(libxl__egc * egc,libxl__xswait_state * xswa,int rc,const char * p)395 static void spawn_watch_event(libxl__egc *egc, libxl__xswait_state *xswa,
396                               int rc, const char *p)
397 {
398     /* On entry, is Attached. */
399     EGC_GC;
400     libxl__spawn_state *ss = CONTAINER_OF(xswa, *ss, xswait);
401     if (rc) {
402         if (rc == ERROR_TIMEDOUT)
403             LOG(ERROR, "%s: startup timed out", ss->what);
404         spawn_fail(egc, ss, rc); /* must be last */
405         return;
406     }
407     LOG(DEBUG, "%s: spawn watch p=%s", ss->what, p);
408     ss->confirm_cb(egc, ss, p); /* must be last */
409 }
410 
spawn_middle_death(libxl__egc * egc,libxl__ev_child * childw,pid_t pid,int status)411 static void spawn_middle_death(libxl__egc *egc, libxl__ev_child *childw,
412                                pid_t pid, int status)
413     /* On entry, is Attached or Detaching */
414 {
415     EGC_GC;
416     libxl__spawn_state *ss = CONTAINER_OF(childw, *ss, mid);
417 
418     if ((ss->rc || ss->detaching) &&
419         ((WIFEXITED(status) && WEXITSTATUS(status)==0) ||
420          (WIFSIGNALED(status) && WTERMSIG(status)==SIGKILL))) {
421         /* as expected */
422         const char *what = GCSPRINTF("%s (dying as expected)", ss->what);
423         libxl_report_child_exitstatus(CTX, XTL_DEBUG, what, pid, status);
424     } else if (!WIFEXITED(status)) {
425         int loglevel = ss->detaching ? XTL_WARN : XTL_ERROR;
426         const char *what =
427             GCSPRINTF("%s intermediate process (startup monitor)", ss->what);
428         libxl_report_child_exitstatus(CTX, loglevel, what, pid, status);
429         ss->rc = ERROR_FAIL;
430     } else {
431         if (!status)
432             LOG(ERROR, "%s [%ld]: unexpectedly exited with exit status 0,"
433                 " when we were waiting for it to confirm startup",
434                 ss->what, (unsigned long)pid);
435         else if (status <= 127)
436             LOG(ERROR, "%s [%ld]: failed startup with non-zero exit status %d",
437                 ss->what, (unsigned long)pid, status);
438         else if (status < 255) {
439             int sig = status - 128;
440             const char *str = strsignal(sig);
441             if (str)
442                 LOG(ERROR, "%s [%ld]: died during startup due to fatal"
443                     " signal %s", ss->what, (unsigned long)pid, str);
444             else
445                 LOG(ERROR, "%s [%ld]: died during startup due to unknown fatal"
446                     " signal number %d", ss->what, (unsigned long)pid, sig);
447         }
448         ss->rc = ERROR_FAIL;
449     }
450 
451     spawn_cleanup(gc, ss);
452 
453     if (ss->rc && !ss->detaching) {
454         ss->failure_cb(egc, ss, ss->rc); /* must be last */
455         return;
456     }
457 
458     if (ss->rc && ss->detaching)
459         LOG(WARN,"%s underlying machinery seemed to fail (%d),"
460             " but its function seems to have been successful",
461             ss->what, ss->rc);
462 
463     assert(ss->detaching);
464     ss->detached_cb(egc, ss);
465 }
466 
467 /*
468  * Local variables:
469  * mode: C
470  * c-basic-offset: 4
471  * indent-tabs-mode: nil
472  * End:
473  */
474