mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
...to denote what it actually does. The name libxl__xs_write() suggests something taking a buffer and length, akin to write(2), whereas the semantics of the function are actually more akin to printf(3). This patch is a textual substitution of libxl__xs_write with libxl__xs_printf with some associated formatting fixes. Signed-off-by: Paul Durrant <paul.durrant@citrix.com> Acked-by: Ian Jackson <ian.jackson@eu.citrix.com> Cc: Stefano Stabellini <stefano.stabellini@eu.citrix.com> Cc: Ian Campbell <ian.campbell@citrix.com> Cc: Wei Liu <wei.liu2@citrix.com>
465 lines
14 KiB
C
465 lines
14 KiB
C
|
|
/*
|
|
* Copyright (C) 2009 Citrix Ltd.
|
|
* Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
|
|
* Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published
|
|
* by the Free Software Foundation; version 2.1 only. with the special
|
|
* exception on linking described in file LICENSE.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*/
|
|
|
|
#include "libxl_osdeps.h" /* must come before any other headers */
|
|
|
|
#include "libxl_internal.h"
|
|
|
|
static void check_open_fds(const char *what)
|
|
{
|
|
const char *env_debug;
|
|
int debug;
|
|
int i, flags, badness = 0;
|
|
|
|
env_debug = getenv("_LIBXL_DEBUG_EXEC_FDS");
|
|
if (!env_debug) return;
|
|
|
|
debug = strtol(env_debug, (char **) NULL, 10);
|
|
if (debug <= 0) return;
|
|
|
|
for (i = 4; i < 256; i++) {
|
|
#ifdef __linux__
|
|
ssize_t len;
|
|
char path[PATH_MAX];
|
|
char linkpath[PATH_MAX+1];
|
|
#endif
|
|
flags = fcntl(i, F_GETFD);
|
|
if ( flags == -1 ) {
|
|
if ( errno != EBADF )
|
|
fprintf(stderr, "libxl: execing %s: fd %d flags returned %s (%d)\n",
|
|
what, i, strerror(errno), errno);
|
|
continue;
|
|
}
|
|
|
|
if ( flags & FD_CLOEXEC )
|
|
continue;
|
|
|
|
badness++;
|
|
|
|
#ifdef __linux__
|
|
snprintf(path, PATH_MAX, "/proc/%d/fd/%d", getpid(), i);
|
|
len = readlink(path, linkpath, PATH_MAX);
|
|
if (len > 0) {
|
|
linkpath[len] = '\0';
|
|
fprintf(stderr, "libxl: execing %s: fd %d is open to %s with flags %#x\n",
|
|
what, i, linkpath, flags);
|
|
} else
|
|
#endif
|
|
fprintf(stderr, "libxl: execing %s: fd %d is open with flags %#x\n",
|
|
what, i, flags);
|
|
}
|
|
if (debug < 2) return;
|
|
if (badness) abort();
|
|
}
|
|
|
|
void libxl__exec(libxl__gc *gc, int stdinfd, int stdoutfd, int stderrfd,
|
|
const char *arg0, char *const args[], char *const env[])
|
|
/* call this in the child */
|
|
{
|
|
if (stdinfd != -1)
|
|
dup2(stdinfd, STDIN_FILENO);
|
|
if (stdoutfd != -1)
|
|
dup2(stdoutfd, STDOUT_FILENO);
|
|
if (stderrfd != -1)
|
|
dup2(stderrfd, STDERR_FILENO);
|
|
|
|
if (stdinfd > 2)
|
|
close(stdinfd);
|
|
if (stdoutfd > 2 && stdoutfd != stdinfd)
|
|
close(stdoutfd);
|
|
if (stderrfd > 2 && stderrfd != stdinfd && stderrfd != stdoutfd)
|
|
close(stderrfd);
|
|
|
|
check_open_fds(arg0);
|
|
|
|
signal(SIGPIPE, SIG_DFL);
|
|
/* in case our caller set it to IGN. subprocesses are entitled
|
|
* to assume they got DFL. */
|
|
|
|
if (env != NULL) {
|
|
for (int i = 0; env[i] != NULL && env[i+1] != NULL; i += 2) {
|
|
if (setenv(env[i], env[i+1], 1) < 0) {
|
|
LOGEV(ERROR, errno, "setting env vars (%s = %s)",
|
|
env[i], env[i+1]);
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
execvp(arg0, args);
|
|
|
|
out:
|
|
fprintf(stderr, "libxl: cannot execute %s: %s\n", arg0, strerror(errno));
|
|
_exit(-1);
|
|
}
|
|
|
|
void libxl_report_child_exitstatus(libxl_ctx *ctx,
|
|
xentoollog_level level,
|
|
const char *what, pid_t pid, int status)
|
|
{
|
|
|
|
if (WIFEXITED(status)) {
|
|
int st = WEXITSTATUS(status);
|
|
if (st)
|
|
LIBXL__LOG(ctx, level, "%s [%ld] exited"
|
|
" with error status %d", what, (unsigned long)pid, st);
|
|
else
|
|
LIBXL__LOG(ctx, level, "%s [%ld] unexpectedly"
|
|
" exited status zero", what, (unsigned long)pid);
|
|
} else if (WIFSIGNALED(status)) {
|
|
int sig = WTERMSIG(status);
|
|
const char *str = strsignal(sig);
|
|
const char *coredump = WCOREDUMP(status) ? " (core dumped)" : "";
|
|
if (str)
|
|
LIBXL__LOG(ctx, level, "%s [%ld] died due to"
|
|
" fatal signal %s%s", what, (unsigned long)pid,
|
|
str, coredump);
|
|
else
|
|
LIBXL__LOG(ctx, level, "%s [%ld] died due to unknown"
|
|
" fatal signal number %d%s", what, (unsigned long)pid,
|
|
sig, coredump);
|
|
} else {
|
|
LIBXL__LOG(ctx, level, "%s [%ld] gave unknown"
|
|
" wait status 0x%x", what, (unsigned long)pid, status);
|
|
}
|
|
}
|
|
|
|
int libxl__spawn_record_pid(libxl__gc *gc, libxl__spawn_state *spawn, pid_t pid)
|
|
{
|
|
int r, rc;
|
|
|
|
rc = libxl__ev_child_xenstore_reopen(gc, spawn->what);
|
|
if (rc) goto out;
|
|
|
|
r = libxl__xs_printf(gc, XBT_NULL, spawn->pidpath, "%d", pid);
|
|
if (r) {
|
|
LOGE(ERROR,
|
|
"write %s = %d: xenstore write failed", spawn->pidpath, pid);
|
|
rc = ERROR_FAIL; goto out;
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
out:
|
|
return rc ? SIGTERM : 0;
|
|
}
|
|
|
|
int 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)
|
|
{
|
|
char *p;
|
|
unsigned int len;
|
|
int rc = 0;
|
|
struct xs_handle *xsh;
|
|
int nfds;
|
|
fd_set rfds;
|
|
struct timeval tv;
|
|
unsigned int num;
|
|
char **l = NULL;
|
|
|
|
xsh = xs_daemon_open();
|
|
if (xsh == NULL) {
|
|
LOG(ERROR, "Unable to open xenstore connection");
|
|
goto err;
|
|
}
|
|
|
|
xs_watch(xsh, path, path);
|
|
tv.tv_sec = timeout;
|
|
tv.tv_usec = 0;
|
|
nfds = xs_fileno(xsh) + 1;
|
|
assert(!spawning);
|
|
|
|
while (rc > 0 || (!rc && tv.tv_sec > 0)) {
|
|
p = xs_read(xsh, XBT_NULL, path, &len);
|
|
if ( NULL == p )
|
|
goto again;
|
|
|
|
if ( NULL != state && strcmp(p, state) )
|
|
goto again;
|
|
|
|
if ( NULL != check_callback ) {
|
|
rc = (*check_callback)(gc, domid, p, check_callback_userdata);
|
|
if ( rc > 0 )
|
|
goto again;
|
|
}
|
|
|
|
free(p);
|
|
xs_unwatch(xsh, path, path);
|
|
xs_daemon_close(xsh);
|
|
return rc;
|
|
again:
|
|
free(p);
|
|
FD_ZERO(&rfds);
|
|
FD_SET(xs_fileno(xsh), &rfds);
|
|
rc = select(nfds, &rfds, NULL, NULL, &tv);
|
|
if (rc > 0) {
|
|
if (FD_ISSET(xs_fileno(xsh), &rfds)) {
|
|
l = xs_read_watch(xsh, &num);
|
|
if (l != NULL)
|
|
free(l);
|
|
else
|
|
goto again;
|
|
}
|
|
}
|
|
}
|
|
LOG(ERROR, "%s not ready", what);
|
|
|
|
xs_unwatch(xsh, path, path);
|
|
xs_daemon_close(xsh);
|
|
err:
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*----- spawn implementation -----*/
|
|
|
|
/*
|
|
* Full set of possible states of a libxl__spawn_state and its _detachable:
|
|
*
|
|
* detaching rc mid timeout xswatch
|
|
* - Undefined undef undef - undef undef
|
|
* - Idle any any Idle Idle Idle
|
|
* - Attached OK 0 0 Active Active Active
|
|
* - Attached Failed 0 non-0 Active Idle Idle
|
|
* - Detaching 1 maybe Active Idle Idle
|
|
* - Partial any any Idle Active/Idle Active/Idle
|
|
*
|
|
* When in states Detaching or Attached Failed, the middle process has
|
|
* been sent a SIGKILL.
|
|
*
|
|
* The difference between Attached OK and Attached Failed is not
|
|
* directly visible to callers - callers see these two the same,
|
|
* although of course Attached OK will hopefully eventually result in
|
|
* a call to detached_cb, whereas Attached Failed will end up
|
|
* in a call to failure_cb.
|
|
*/
|
|
|
|
/* Event callbacks. */
|
|
static void spawn_watch_event(libxl__egc *egc, libxl__xswait_state *xswa,
|
|
int rc, const char *xsdata);
|
|
static void spawn_middle_death(libxl__egc *egc, libxl__ev_child *childw,
|
|
pid_t pid, int status);
|
|
|
|
/* Precondition: Partial. Results: Idle. */
|
|
static void spawn_cleanup(libxl__gc *gc, libxl__spawn_state *ss);
|
|
|
|
/* Precondition: Attached or Detaching; caller has logged failure reason.
|
|
* Results: Detaching, or Attached Failed */
|
|
static void spawn_fail(libxl__egc *egc, libxl__spawn_state *ss, int rc);
|
|
|
|
void libxl__spawn_init(libxl__spawn_state *ss)
|
|
{
|
|
libxl__ev_child_init(&ss->mid);
|
|
libxl__xswait_init(&ss->xswait);
|
|
}
|
|
|
|
int libxl__spawn_spawn(libxl__egc *egc, libxl__spawn_state *ss)
|
|
{
|
|
STATE_AO_GC(ss->ao);
|
|
int r;
|
|
pid_t child;
|
|
int status, rc;
|
|
|
|
libxl__spawn_init(ss);
|
|
ss->rc = ss->detaching = 0;
|
|
|
|
ss->xswait.ao = ao;
|
|
ss->xswait.what = GCSPRINTF("%s startup", ss->what);
|
|
ss->xswait.path = ss->xspath;
|
|
ss->xswait.timeout_ms = ss->timeout_ms;
|
|
ss->xswait.callback = spawn_watch_event;
|
|
rc = libxl__xswait_start(gc, &ss->xswait);
|
|
if (rc) goto out_err;
|
|
|
|
pid_t middle = libxl__ev_child_fork(gc, &ss->mid, spawn_middle_death);
|
|
if (middle ==-1) { rc = ERROR_FAIL; goto out_err; }
|
|
|
|
if (middle) {
|
|
/* parent */
|
|
return 1;
|
|
}
|
|
|
|
/* we are now the middle process */
|
|
|
|
pid_t (*fork_replacement)(void*) =
|
|
CTX->childproc_hooks
|
|
? CTX->childproc_hooks->fork_replacement
|
|
: 0;
|
|
child =
|
|
fork_replacement
|
|
? fork_replacement(CTX->childproc_user)
|
|
: fork();
|
|
|
|
if (child == -1)
|
|
exit(255);
|
|
if (!child) {
|
|
return 0; /* caller runs child code */
|
|
}
|
|
|
|
int failsig = ss->midproc_cb(gc, ss, child);
|
|
if (failsig) {
|
|
kill(child, failsig);
|
|
_exit(127);
|
|
}
|
|
|
|
for (;;) {
|
|
pid_t got = waitpid(child, &status, 0);
|
|
if (got == -1) {
|
|
assert(errno == EINTR);
|
|
continue;
|
|
}
|
|
assert(got == child);
|
|
break;
|
|
}
|
|
|
|
r = (WIFEXITED(status) && WEXITSTATUS(status) <= 127 ? WEXITSTATUS(status) :
|
|
WIFSIGNALED(status) && WTERMSIG(status) < 127 ? WTERMSIG(status)+128 :
|
|
-1);
|
|
_exit(r);
|
|
|
|
out_err:
|
|
spawn_cleanup(gc, ss);
|
|
return rc;
|
|
}
|
|
|
|
static void spawn_cleanup(libxl__gc *gc, libxl__spawn_state *ss)
|
|
{
|
|
assert(!libxl__ev_child_inuse(&ss->mid));
|
|
libxl__xswait_stop(gc, &ss->xswait);
|
|
}
|
|
|
|
static void spawn_detach(libxl__gc *gc, libxl__spawn_state *ss)
|
|
/* Precondition: Attached or Detaching, but caller must have just set
|
|
* at least one of detaching or rc.
|
|
* Results: Detaching or Attached Failed */
|
|
{
|
|
int r;
|
|
|
|
assert(libxl__ev_child_inuse(&ss->mid));
|
|
assert(ss->detaching || ss->rc);
|
|
libxl__xswait_stop(gc, &ss->xswait);
|
|
|
|
pid_t child = ss->mid.pid;
|
|
r = kill(child, SIGKILL);
|
|
if (r && errno != ESRCH)
|
|
LOGE(WARN, "%s: failed to kill intermediate child (pid=%lu)",
|
|
ss->what, (unsigned long)child);
|
|
}
|
|
|
|
void libxl__spawn_initiate_detach(libxl__gc *gc, libxl__spawn_state *ss)
|
|
{
|
|
ss->detaching = 1;
|
|
spawn_detach(gc, ss);
|
|
}
|
|
|
|
static void spawn_fail(libxl__egc *egc, libxl__spawn_state *ss, int rc)
|
|
/* Caller must have logged. Must be last thing in calling function,
|
|
* as it may make the callback. Precondition: Attached or Detaching. */
|
|
{
|
|
EGC_GC;
|
|
assert(rc);
|
|
ss->rc = rc;
|
|
spawn_detach(gc, ss);
|
|
}
|
|
|
|
static void spawn_watch_event(libxl__egc *egc, libxl__xswait_state *xswa,
|
|
int rc, const char *p)
|
|
{
|
|
/* On entry, is Attached. */
|
|
EGC_GC;
|
|
libxl__spawn_state *ss = CONTAINER_OF(xswa, *ss, xswait);
|
|
if (rc) {
|
|
if (rc == ERROR_TIMEDOUT)
|
|
LOG(ERROR, "%s: startup timed out", ss->what);
|
|
spawn_fail(egc, ss, rc); /* must be last */
|
|
return;
|
|
}
|
|
LOG(DEBUG, "%s: spawn watch p=%s", ss->what, p);
|
|
ss->confirm_cb(egc, ss, p); /* must be last */
|
|
}
|
|
|
|
static void spawn_middle_death(libxl__egc *egc, libxl__ev_child *childw,
|
|
pid_t pid, int status)
|
|
/* On entry, is Attached or Detaching */
|
|
{
|
|
EGC_GC;
|
|
libxl__spawn_state *ss = CONTAINER_OF(childw, *ss, mid);
|
|
|
|
if ((ss->rc || ss->detaching) &&
|
|
((WIFEXITED(status) && WEXITSTATUS(status)==0) ||
|
|
(WIFSIGNALED(status) && WTERMSIG(status)==SIGKILL))) {
|
|
/* as expected */
|
|
const char *what = GCSPRINTF("%s (dying as expected)", ss->what);
|
|
libxl_report_child_exitstatus(CTX, XTL_DEBUG, what, pid, status);
|
|
} else if (!WIFEXITED(status)) {
|
|
int loglevel = ss->detaching ? XTL_WARN : XTL_ERROR;
|
|
const char *what =
|
|
GCSPRINTF("%s intermediate process (startup monitor)", ss->what);
|
|
libxl_report_child_exitstatus(CTX, loglevel, what, pid, status);
|
|
ss->rc = ERROR_FAIL;
|
|
} else {
|
|
if (!status)
|
|
LOG(ERROR, "%s [%ld]: unexpectedly exited with exit status 0,"
|
|
" when we were waiting for it to confirm startup",
|
|
ss->what, (unsigned long)pid);
|
|
else if (status <= 127)
|
|
LOG(ERROR, "%s [%ld]: failed startup with non-zero exit status %d",
|
|
ss->what, (unsigned long)pid, status);
|
|
else if (status < 255) {
|
|
int sig = status - 128;
|
|
const char *str = strsignal(sig);
|
|
if (str)
|
|
LOG(ERROR, "%s [%ld]: died during startup due to fatal"
|
|
" signal %s", ss->what, (unsigned long)pid, str);
|
|
else
|
|
LOG(ERROR, "%s [%ld]: died during startup due to unknown fatal"
|
|
" signal number %d", ss->what, (unsigned long)pid, sig);
|
|
}
|
|
ss->rc = ERROR_FAIL;
|
|
}
|
|
|
|
spawn_cleanup(gc, ss);
|
|
|
|
if (ss->rc && !ss->detaching) {
|
|
ss->failure_cb(egc, ss, ss->rc); /* must be last */
|
|
return;
|
|
}
|
|
|
|
if (ss->rc && ss->detaching)
|
|
LOG(WARN,"%s underlying machinery seemed to fail (%d),"
|
|
" but its function seems to have been successful",
|
|
ss->what, ss->rc);
|
|
|
|
assert(ss->detaching);
|
|
ss->detached_cb(egc, ss);
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* mode: C
|
|
* c-basic-offset: 4
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*/
|