lib/process: Add process_get_stdout
[petitboot] / lib / process / process.c
index 1a16fb6312b4fb74dc8cc753806009e939bd7691..02b6f490dad8318744ba8b4f43b30c6362ceae09 100644 (file)
@@ -60,9 +60,22 @@ static struct process_info *get_info(struct process *process)
        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;
@@ -74,8 +87,17 @@ static int process_read_stdout_once(struct process_info *procinfo)
        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) {
@@ -85,14 +107,14 @@ static int process_read_stdout_once(struct process_info *procinfo)
                                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;
@@ -110,7 +132,7 @@ static int process_setup_stdout_pipe(struct process_info *procinfo)
 
 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]);
@@ -120,12 +142,18 @@ static void process_setup_stdout_child(struct process_info *procinfo)
 {
        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)
@@ -142,7 +170,7 @@ static int process_read_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);
@@ -150,12 +178,11 @@ static int process_read_stdout(struct process_info *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
@@ -184,7 +211,7 @@ static void sigchld_sigaction(int signo, siginfo_t *info,
 
        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)
@@ -258,7 +285,7 @@ struct procset *process_init(void *ctx, struct waitset *set, bool dry_run)
 
        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;
        }
 
@@ -274,7 +301,7 @@ struct procset *process_init(void *ctx, struct waitset *set, bool dry_run)
 
        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;
        }
@@ -326,7 +353,7 @@ static int process_run_common(struct process_info *procinfo)
 
        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;
        }
 
@@ -362,13 +389,18 @@ int process_run_sync(struct process *process)
                if (errno == EINTR)
                        continue;
 
-               pb_log("%s: waitpid failed: %s\n", __func__, strerror(errno));
+               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);
@@ -379,10 +411,11 @@ int process_run_async(struct process *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);
        }
 
@@ -394,53 +427,109 @@ int process_run_async(struct process *process)
 
 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;
+}