/*
* virprocess.c: interaction with processes
*
- * Copyright (C) 2010-2013 Red Hat, Inc.
+ * Copyright (C) 2010-2014 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
+#include <stdlib.h>
#include <sys/wait.h>
#if HAVE_SETRLIMIT
# include <sys/time.h>
return -1;
}
#endif
+
+
+/**
+ * virProcessExitWithStatus:
+ * @status: raw status to be reproduced when this process dies
+ *
+ * Given a raw status obtained by waitpid() or similar, attempt to
+ * make this process exit in the same manner. If the child died by
+ * signal, reset that signal handler to default and raise the same
+ * signal; if that doesn't kill this process, then exit with 128 +
+ * signal number. If @status can't be deciphered, use
+ * EXIT_CANNOT_INVOKE.
+ *
+ * Never returns.
+ */
+void
+virProcessExitWithStatus(int status)
+{
+ int value = EXIT_CANNOT_INVOKE;
+
+ if (WIFEXITED(status)) {
+ value = WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ struct sigaction act;
+ sigset_t sigs;
+
+ if (sigemptyset(&sigs) == 0 &&
+ sigaddset(&sigs, WTERMSIG(status)) == 0)
+ sigprocmask(SIG_UNBLOCK, &sigs, NULL);
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = SIG_DFL;
+ sigfillset(&act.sa_mask);
+ sigaction(WTERMSIG(status), &act, NULL);
+ raise(WTERMSIG(status));
+ value = 128 + WTERMSIG(status);
+ }
+ exit(value);
+}
/*
* virprocess.h: interaction with processes
*
- * Copyright (C) 2010-2013 Red Hat, Inc.
+ * Copyright (C) 2010-2014 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
void
virProcessAbort(pid_t pid);
+void virProcessExitWithStatus(int status) ATTRIBUTE_NORETURN;
+
int
virProcessWait(pid_t pid, int *exitstatus)
ATTRIBUTE_RETURN_CHECK;
#include "virerror.h"
#include "virthread.h"
#include "virstring.h"
+#include "virprocess.h"
#define VIR_FROM_THIS VIR_FROM_NONE
return ret;
}
+
+static int
+test23(const void *unused ATTRIBUTE_UNUSED)
+{
+ /* Not strictly a virCommand test, but this is the easiest place
+ * to test this lower-level interface. It takes a double fork to
+ * test virProcessExitWithStatus. */
+ int ret = -1;
+ int status = -1;
+ pid_t pid;
+
+ if (virFork(&pid) < 0)
+ goto cleanup;
+ if (pid < 0)
+ goto cleanup;
+ if (pid == 0) {
+ if (virFork(&pid) < 0)
+ _exit(EXIT_FAILURE);
+ if (pid == 0)
+ _exit(42);
+ if (virProcessWait(pid, &status) < 0)
+ _exit(EXIT_FAILURE);
+ virProcessExitWithStatus(status);
+ _exit(EXIT_FAILURE);
+ }
+
+ if (virProcessWait(pid, &status) < 0)
+ goto cleanup;
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 42) {
+ printf("Unexpected status %d\n", status);
+ goto cleanup;
+ }
+
+ if (virFork(&pid) < 0)
+ goto cleanup;
+ if (pid < 0)
+ goto cleanup;
+ if (pid == 0) {
+ if (virFork(&pid) < 0)
+ _exit(EXIT_FAILURE);
+ if (pid == 0) {
+ raise(SIGKILL);
+ _exit(EXIT_FAILURE);
+ }
+ if (virProcessWait(pid, &status) < 0)
+ _exit(EXIT_FAILURE);
+ virProcessExitWithStatus(status);
+ _exit(EXIT_FAILURE);
+ }
+
+ if (virProcessWait(pid, &status) < 0)
+ goto cleanup;
+ if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL) {
+ printf("Unexpected status %d\n", status);
+ goto cleanup;
+ }
+
+ ret = 0;
+cleanup:
+ return ret;
+}
+
static void virCommandThreadWorker(void *opaque)
{
virCommandTestDataPtr test = opaque;
DO_TEST(test20);
DO_TEST(test21);
DO_TEST(test22);
+ DO_TEST(test23);
virMutexLock(&test->lock);
if (test->running) {