lib/system: Add pb_run_cmd_pipe
authorJeremy Kerr <jk@ozlabs.org>
Thu, 20 Jun 2013 05:27:16 +0000 (13:27 +0800)
committerJeremy Kerr <jk@ozlabs.org>
Mon, 24 Jun 2013 05:07:58 +0000 (13:07 +0800)
Add a function to run a command and capture the output into a buffer.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
lib/system/system.c
lib/system/system.h

index dbdef46bdd9da775a8f02955df27d1325a49b8d5..7bb49973333f3ebebcd4eec0a87fe37994acc67c 100644 (file)
@@ -102,6 +102,40 @@ int pb_rmdir_recursive(const char *base, const char *dir)
        return 0;
 }
 
        return 0;
 }
 
+static int read_pipe(void *ctx, int fd, char **bufp, int *lenp)
+{
+       int rc, len, alloc_len;
+       char *buf;
+
+       alloc_len = 4096;
+       len = 0;
+
+       buf = talloc_array(ctx, char, alloc_len);
+
+       for (;;) {
+               rc = read(fd, buf, alloc_len - len - 1);
+               if (rc <= 0)
+                       break;
+
+               len += rc;
+               if (len == alloc_len - 1) {
+                       alloc_len *= 2;
+                       buf = talloc_realloc(ctx, buf, char, alloc_len);
+               }
+       }
+
+       if (rc < 0) {
+               talloc_free(buf);
+               return rc;
+       }
+
+       buf[len] = '\0';
+       *bufp = buf;
+       *lenp = len;
+
+       return 0;
+}
+
 /**
  * pb_run_cmd - Run the supplied command.
  * @cmd_argv: An argument list array for execv.
 /**
  * pb_run_cmd - Run the supplied command.
  * @cmd_argv: An argument list array for execv.
@@ -110,15 +144,25 @@ int pb_rmdir_recursive(const char *base, const char *dir)
  */
 
 int pb_run_cmd(const char *const *cmd_argv, int wait, int dry_run)
  */
 
 int pb_run_cmd(const char *const *cmd_argv, int wait, int dry_run)
+{
+       return pb_run_cmd_pipe(cmd_argv, wait, dry_run, NULL, NULL, NULL);
+}
+
+int pb_run_cmd_pipe(const char *const *cmd_argv, int wait, int dry_run,
+               void *ctx, char **stdout_buf, int *stdout_buf_len)
 {
 #if defined(DEBUG)
        enum {do_debug = 1};
 #else
        enum {do_debug = 0};
 #endif
 {
 #if defined(DEBUG)
        enum {do_debug = 1};
 #else
        enum {do_debug = 0};
 #endif
-       int status;
+       int status, pipefd[2];
        pid_t pid;
 
        pid_t pid;
 
+       assert(!wait && stdout_buf);
+       assert(!!ctx != !!stdout_buf);
+       assert(!!stdout_buf != !!stdout_buf_len);
+
        if (do_debug) {
                const char *const *p = cmd_argv;
 
        if (do_debug) {
                const char *const *p = cmd_argv;
 
@@ -133,9 +177,22 @@ int pb_run_cmd(const char *const *cmd_argv, int wait, int dry_run)
                pb_log("%s: %s%s\n", __func__, (dry_run ? "(dry-run) " : ""),
                        cmd_argv[0]);
 
                pb_log("%s: %s%s\n", __func__, (dry_run ? "(dry-run) " : ""),
                        cmd_argv[0]);
 
+       if (stdout_buf) {
+               *stdout_buf = NULL;
+               *stdout_buf_len = 0;
+       }
+
        if (dry_run)
                return 0;
 
        if (dry_run)
                return 0;
 
+       if (stdout_buf) {
+               status = pipe(pipefd);
+               if (status) {
+                       pb_log("pipe failed");
+                       return -1;
+               }
+       }
+
        pid = fork();
 
        if (pid == -1) {
        pid = fork();
 
        if (pid == -1) {
@@ -143,12 +200,17 @@ int pb_run_cmd(const char *const *cmd_argv, int wait, int dry_run)
                return -1;
        }
 
                return -1;
        }
 
+
        if (pid == 0) {
                int log = fileno(pb_log_get_stream());
 
                /* Redirect child output to log. */
 
        if (pid == 0) {
                int log = fileno(pb_log_get_stream());
 
                /* Redirect child output to log. */
 
-               status = dup2(log, STDOUT_FILENO);
+               if (stdout_buf) {
+                       status = dup2(pipefd[1], STDOUT_FILENO);
+               } else {
+                       status = dup2(log, STDOUT_FILENO);
+               }
                assert(status != -1);
 
                status = dup2(log, STDERR_FILENO);
                assert(status != -1);
 
                status = dup2(log, STDERR_FILENO);
@@ -162,6 +224,13 @@ int pb_run_cmd(const char *const *cmd_argv, int wait, int dry_run)
        if (!wait && !waitpid(pid, &status, WNOHANG))
                return 0;
 
        if (!wait && !waitpid(pid, &status, WNOHANG))
                return 0;
 
+       if (stdout_buf) {
+               close(pipefd[1]);
+               status = read_pipe(ctx, pipefd[0], stdout_buf, stdout_buf_len);
+               if (status)
+                       return -1;
+       }
+
        if (waitpid(pid, &status, 0) == -1) {
                pb_log("%s: waitpid failed: %s\n", __func__,
                                strerror(errno));
        if (waitpid(pid, &status, 0) == -1) {
                pb_log("%s: waitpid failed: %s\n", __func__,
                                strerror(errno));
index 271c4355d3066cb46c10fe224abd65fa277f82b8..a7a1a120c3142756bd05b644b509a446890b707e 100644 (file)
@@ -18,6 +18,8 @@ struct pb_system_apps {
 extern const struct pb_system_apps pb_system_apps;
 
 int pb_run_cmd(const char *const *cmd_argv, int wait, int dry_run);
 extern const struct pb_system_apps pb_system_apps;
 
 int pb_run_cmd(const char *const *cmd_argv, int wait, int dry_run);
+int pb_run_cmd_pipe(const char *const *cmd_argv, int wait, int dry_run,
+               void *ctx, char **buf, int *buf_len);
 int pb_mkdir_recursive(const char *dir);
 int pb_rmdir_recursive(const char *base, const char *dir);
 
 int pb_mkdir_recursive(const char *dir);
 int pb_rmdir_recursive(const char *base, const char *dir);