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