struct list async_list;
int sigchld_pipe[2];
struct waiter *sigchld_waiter;
+ bool dry_run;
};
/* Internal data type for process handling
return container_of(process, struct process_info, process);
}
+struct process *procinfo_get_process(struct process_info *procinfo)
+{
+ return &procinfo->process;
+}
+
/* Read as much as possible into the currently-allocated stdout buffer, and
- * possibly realloc it for the next read */
-static int process_read_stdout_once(struct process_info *procinfo)
+ * possibly realloc it for the next read
+ * If the line pointer is not NULL, it is set to the start of the latest
+ * output.
+ *
+ * Returns:
+ * > 0 on success (even though no bytes may have been read)
+ * 0 on EOF (no error, but no more reads can be performed)
+ * < 0 on error
+ **/
+static int process_read_stdout_once(struct process_info *procinfo, char **line)
{
struct process *process = &procinfo->process;
int rc, fd, max_len;
max_len = procinfo->stdout_buf_len - process->stdout_len - 1;
rc = read(fd, process->stdout_buf + process->stdout_len, max_len);
- if (rc <= 0)
+ if (rc == 0)
+ return 0;
+ if (rc < 0) {
+ if (errno == EINTR)
+ return 1;
+ pb_log_fn("read failed: %s\n", strerror(errno));
return rc;
+ }
+
+ if (line)
+ *line = process->stdout_buf + process->stdout_len;
process->stdout_len += rc;
if (process->stdout_len == procinfo->stdout_buf_len - 1) {
procinfo->stdout_buf_len);
}
- return rc;
+ return 1;
}
static int process_setup_stdout_pipe(struct process_info *procinfo)
{
int rc;
- if (!procinfo->process.keep_stdout)
+ if (!procinfo->process.keep_stdout || procinfo->process.raw_stdout)
return 0;
procinfo->stdout_buf_len = 4096;
static void process_setup_stdout_parent(struct process_info *procinfo)
{
- if (!procinfo->process.keep_stdout)
+ if (!procinfo->process.keep_stdout || procinfo->process.raw_stdout)
return;
close(procinfo->stdout_pipe[1]);
{
int log = fileno(pb_log_get_stream());
+ if (procinfo->process.raw_stdout)
+ return;
+
if (procinfo->process.keep_stdout)
dup2(procinfo->stdout_pipe[1], STDOUT_FILENO);
else
dup2(log, STDOUT_FILENO);
- dup2(log, STDERR_FILENO);
+ if (procinfo->process.keep_stdout && procinfo->process.add_stderr)
+ dup2(procinfo->stdout_pipe[1], STDERR_FILENO);
+ else
+ dup2(log, STDERR_FILENO);
}
static void process_finish_stdout(struct process_info *procinfo)
return 0;
do {
- rc = process_read_stdout_once(procinfo);
+ rc = process_read_stdout_once(procinfo, NULL);
} while (rc > 0);
process_finish_stdout(procinfo);
return rc < 0 ? rc : 0;
}
-static int process_stdout_cb(void *arg)
+int process_process_stdout(struct process_info *procinfo, char **line)
{
- struct process_info *procinfo = arg;
int rc;
- rc = process_read_stdout_once(procinfo);
+ rc = process_read_stdout_once(procinfo, line);
/* if we're going to signal to the waitset that we're done (ie, non-zero
* return value), then the waiters will remove us, so we drop the
rc = write(procset->sigchld_pipe[1], &pid, sizeof(pid));
if (rc != sizeof(pid))
- pb_log("%s: write failed: %s\n", __func__, strerror(errno));
+ pb_log_fn("write failed: %s\n", strerror(errno));
}
static int sigchld_pipe_event(void *arg)
return 0;
}
-struct procset *process_init(void *ctx, struct waitset *set)
+struct procset *process_init(void *ctx, struct waitset *set, bool dry_run)
{
struct sigaction sa;
int rc;
procset = talloc(ctx, struct procset);
procset->waitset = set;
+ procset->dry_run = dry_run;
list_init(&procset->async_list);
rc = pipe(procset->sigchld_pipe);
if (rc) {
- pb_log("%s: pipe() failed: %s\n", __func__, strerror(errno));
+ pb_log_fn("pipe() failed: %s\n", strerror(errno));
goto err_free;
}
rc = sigaction(SIGCHLD, &sa, NULL);
if (rc) {
- pb_log("%s: sigaction() failed: %s\n", __func__,
+ pb_log_fn("sigaction() failed: %s\n",
strerror(errno));
goto err_remove;
}
pid = fork();
if (pid < 0) {
- pb_log("%s: fork failed: %s\n", __func__, strerror(errno));
+ pb_log_fn("fork failed: %s\n", strerror(errno));
return pid;
}
if (pid == 0) {
process_setup_stdout_child(procinfo);
+ if (procset->dry_run)
+ exit(EXIT_SUCCESS);
execvp(process->path, (char * const *)process->argv);
exit(EXIT_FAILURE);
}
process_read_stdout(procinfo);
- rc = waitpid(process->pid, &process->exit_status, 0);
- if (rc == -1) {
- pb_log("%s: waitpid failed: %s\n", __func__, strerror(errno));
+ for (;;) {
+ rc = waitpid(process->pid, &process->exit_status, 0);
+ if (rc >= 0)
+ break;
+ if (errno == EINTR)
+ continue;
+
+ pb_log_fn("waitpid failed: %s\n", strerror(errno));
return rc;
}
return 0;
}
+static int process_stdout_cb(struct process_info *procinfo)
+{
+ return process_process_stdout(procinfo, NULL);
+}
+
int process_run_async(struct process *process)
{
struct process_info *procinfo = get_info(process);
return rc;
if (process->keep_stdout) {
+ waiter_cb stdout_cb = process->stdout_cb ?:
+ (waiter_cb)process_stdout_cb;
procinfo->stdout_waiter = waiter_register_io(procset->waitset,
procinfo->stdout_pipe[0],
- WAIT_IN, process_stdout_cb,
- procinfo);
+ WAIT_IN, stdout_cb, procinfo);
talloc_reference(procset, procinfo);
}
void process_stop_async(struct process *process)
{
+ /* Avoid signalling an old pid */
+ if (process->cancelled)
+ return;
+
+ pb_debug("process: sending SIGTERM to pid %d\n", process->pid);
kill(process->pid, SIGTERM);
+ process->cancelled = true;
}
-int process_run_simple_argv(void *ctx, const char *argv[])
+void process_stop_async_all(void)
{
- struct process *process;
+ struct process_info *procinfo;
+ struct process *process = NULL;
+
+ pb_debug("process: cancelling all async jobs\n");
+
+ list_for_each_entry(&procset->async_list, procinfo, async_list) {
+ process = &procinfo->process;
+ /* Ignore the process completion - callbacks may use stale data */
+ process->exit_cb = NULL;
+ process->stdout_cb = NULL;
+ process_stop_async(process);
+ }
+}
+
+int process_get_stdout_argv(void *ctx, struct process_stdout **stdout,
+ const char *argv[])
+{
+ struct process *p;
int rc;
- process = process_create(ctx);
+ p = process_create(NULL);
+ p->path = argv[0];
+ p->argv = argv;
- process->path = argv[0];
- process->argv = argv;
+ if (stdout) {
+ p->keep_stdout = true;
+ *stdout = NULL;
+ }
- rc = process_run_sync(process);
+ rc = process_run_sync(p);
if (!rc)
- rc = process->exit_status;
+ rc = p->exit_status;
+ else {
+ pb_debug("%s: process_run_sync failed: %s.\n", __func__,
+ p->path);
+ if (stdout)
+ pb_debug("%s: stdout: %s\n\n", __func__, p->stdout_buf);
+ goto exit;
+ }
+
+ if (!stdout)
+ goto exit;
+
+ *stdout = talloc(ctx, struct process_stdout);
+
+ if (!*stdout) {
+ rc = -1;
+ goto exit;
+ }
- process_release(process);
+ (*stdout)->len = p->stdout_len;
+ (*stdout)->buf = talloc_memdup(*stdout, p->stdout_buf,
+ p->stdout_len + 1);
+ (*stdout)->buf[p->stdout_len] = 0;
+exit:
+ process_release(p);
return rc;
}
-int process_run_simple(void *ctx, const char *name, ...)
+int process_get_stdout(void *ctx, struct process_stdout **stdout,
+ const char *path, ...)
{
int rc, i, n_argv = 1;
const char **argv;
va_list ap;
- va_start(ap, name);
+ va_start(ap, path);
while (va_arg(ap, char *))
n_argv++;
va_end(ap);
argv = talloc_array(ctx, const char *, n_argv + 1);
- argv[0] = name;
+ argv[0] = path;
- va_start(ap, name);
+ va_start(ap, path);
for (i = 1; i < n_argv; i++)
argv[i] = va_arg(ap, const char *);
va_end(ap);
argv[i] = NULL;
- rc = process_run_simple_argv(ctx, argv);
+ rc = process_get_stdout_argv(ctx, stdout, argv);
talloc_free(argv);
return rc;
}
+
+bool process_exit_ok(struct process *process)
+{
+ return WIFEXITED(process->exit_status) &&
+ WEXITSTATUS(process->exit_status) == 0;
+}