pid_t pid;
char *pidfile;
+ bool reap;
};
/*
if (ret == 0 && pid)
*pid = cmd->pid;
+ else
+ cmd->reap = true;
return ret;
}
}
cmd->pid = -1;
+ cmd->reap = false;
if (exitstatus == NULL) {
if (status != 0) {
}
+/*
+ * Abort an async command if it is running, without issuing
+ * any errors or affecting errno. Designed for error paths
+ * where some but not all paths to the cleanup code might
+ * have started the child process.
+ */
+void
+virCommandAbort(virCommandPtr cmd)
+{
+ int saved_errno;
+ int ret;
+ int status;
+ char *tmp = NULL;
+
+ if (!cmd || cmd->pid == -1)
+ return;
+
+ /* See if intermediate process has exited; if not, try a nice
+ * SIGTERM followed by a more severe SIGKILL.
+ */
+ saved_errno = errno;
+ VIR_DEBUG("aborting child process %d", cmd->pid);
+ while ((ret = waitpid(cmd->pid, &status, WNOHANG)) == -1 &&
+ errno == EINTR);
+ if (ret == cmd->pid) {
+ tmp = virCommandTranslateStatus(status);
+ VIR_DEBUG("process has ended: %s", tmp);
+ goto cleanup;
+ } else if (ret == 0) {
+ VIR_DEBUG("trying SIGTERM to child process %d", cmd->pid);
+ kill(cmd->pid, SIGTERM);
+ usleep(10 * 1000);
+ while ((ret = waitpid(cmd->pid, &status, WNOHANG)) == -1 &&
+ errno == EINTR);
+ if (ret == cmd->pid) {
+ tmp = virCommandTranslateStatus(status);
+ VIR_DEBUG("process has ended: %s", tmp);
+ goto cleanup;
+ } else if (ret == 0) {
+ VIR_DEBUG("trying SIGKILL to child process %d", cmd->pid);
+ kill(cmd->pid, SIGKILL);
+ while ((ret = waitpid(cmd->pid, &status, 0)) == -1 &&
+ errno == EINTR);
+ if (ret == cmd->pid) {
+ tmp = virCommandTranslateStatus(status);
+ VIR_DEBUG("process has ended: %s", tmp);
+ goto cleanup;
+ }
+ }
+ }
+ VIR_DEBUG("failed to reap child %d, abandoning it", cmd->pid);
+
+cleanup:
+ VIR_FREE(tmp);
+ cmd->pid = -1;
+ cmd->reap = false;
+ errno = saved_errno;
+}
+
/*
* Release all resources
*/
VIR_FREE(cmd->pidfile);
+ if (cmd->reap)
+ virCommandAbort(cmd);
+
VIR_FREE(cmd);
}
int *exitstatus) ATTRIBUTE_RETURN_CHECK;
/*
- * Release all resources
+ * Abort an async command if it is running, without issuing
+ * any errors or affecting errno. Designed for error paths
+ * where some but not all paths to the cleanup code might
+ * have started the child process.
+ */
+void virCommandAbort(virCommandPtr cmd);
+
+/*
+ * Release all resources. The only exception is that if you called
+ * virCommandRunAsync with a non-null pid, then the asynchronous child
+ * is not reaped, and you must call waitpid() yourself.
*/
void virCommandFree(virCommandPtr cmd);
printf("cannot read pidfile\n");
goto cleanup;
}
+
+ virCommandFree(cmd);
+ cmd = NULL;
+ if (kill(pid, 0) != 0) {
+ printf("daemon should still be running\n");
+ goto cleanup;
+ }
+
while (kill(pid, SIGINT) != -1)
usleep(100*1000);
return ret;
}
+/*
+ * Asynchronously run long-running daemon, to ensure no hang.
+ */
+static int test19(const void *unused ATTRIBUTE_UNUSED)
+{
+ virCommandPtr cmd = virCommandNewArgList("sleep", "100", NULL);
+ pid_t pid;
+ int ret = -1;
+
+ alarm(5);
+ if (virCommandRunAsync(cmd, &pid) < 0) {
+ virErrorPtr err = virGetLastError();
+ printf("Cannot run child %s\n", err->message);
+ goto cleanup;
+ }
+
+ if (kill(pid, 0) != 0) {
+ printf("Child should still be running");
+ goto cleanup;
+ }
+
+ virCommandAbort(cmd);
+
+ if (kill(pid, 0) == 0) {
+ printf("Child should be aborted");
+ goto cleanup;
+ }
+
+ alarm(0);
+
+ ret = 0;
+
+cleanup:
+ virCommandFree(cmd);
+ return ret;
+}
static int
mymain(int argc, char **argv)
DO_TEST(test16);
DO_TEST(test17);
DO_TEST(test18);
+ DO_TEST(test19);
return(ret==0 ? EXIT_SUCCESS : EXIT_FAILURE);
}