Hi everyone.

I had mentioned to some of you that I needed a way of running a subprocess and getting separate stdout and stderr pipes back to the parent. At the time I knew of two ways to do this. Now I have a third. (Note: popen(2) does not capture stderr so it doesn't meet my need.)

The first way was to create my own function which forked and exec'ed the child process, and used dup2 to redirect the IO back to the parent. This worked well but was 125 lines of C code (for stdin, stdout and stderr). The advantages were that it was flexible and light-weight (as far as execution time), but was complex, and I wasn't sure if it was worth adding to slim_source, given that we are moving away from C.

The second was to use popen and redirect stdout on the commandline passed to it, like this:

    stdout_filestr = popen("command 2>errfile", "r");

and then read errfile separately. This seemed hacky to me, but it was simple. I didn't actually implement this...

The third option is simpler than the first and not hacky like the second: It uses a fifo. Something like this, to paraphrase:

    mkfifo(temp_filename, 0600);
    stdout_filestr = popen("command 2>temp_filename", "r");
    stderr_filestr = fopen(temp_filename, "r");

This seems to fit the bill nicely. After adding error handling, the open and close functions (for stdout and stderr only) use only 46 (nicely-spaced) lines of code. Thanks to Clay for suggesting this idea.

Full test programs are attached if you're curious.

If anyone has comments on this please reply. At this point I'm inclined to go with option 3.

    Thanks,
    Jack
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <strings.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>

#define BUFSIZ 1024

int popen3(char *cmd, FILE **in_p, FILE **out_p, FILE **err_p, int *pid)
{
        int in_pipes[2];
        int out_pipes[2];
        int err_pipes[2];
        int status;

        if (in_p == NULL && out_p == NULL && err_p == NULL) {
                return (EINVAL);
        }

        if (in_p != NULL) {
                status = pipe(in_pipes);
                if (status < 0) {
                        goto in_error;
                }
        }

        if (out_p != NULL) {
                status = pipe(out_pipes);
                if (status < 0) {
                        goto out_error;
                }
        }

        if (err_p != NULL) {
                status = pipe(err_pipes);
                if (status < 0) {
                        goto err_error;
                }
        }

        *pid = fork();

        if (*pid > 0) {
                /* Parent */
                if (in_p != NULL) {
                        *in_p = fdopen(in_pipes[0], "w");
                        (void) close(in_pipes[1]);
                }
                if (out_p != NULL) {
                        *out_p = fdopen(out_pipes[1], "r");
                        (void) close(out_pipes[0]);
                }
                if (err_p != NULL) {
                        *err_p = fdopen(err_pipes[1], "r");
                        (void) close(err_pipes[0]);
                }

        } else if (*pid == 0) {
                /* Child */

                if (in_p != NULL) {
                        /* Connect to input pipe if input arg specified. */
                        (void) dup2(in_pipes[1], STDIN_FILENO);
                        (void) close(in_pipes[0]);
                } else {
                        /* Connect input to /dev/null otherwise. */
                        int devnull_fd = open("/dev/null", O_RDONLY);
                        (void) dup2(devnull_fd, STDIN_FILENO);
                }

                if (out_p != NULL) {
                        (void) dup2(out_pipes[0], STDOUT_FILENO);
                        (void) close(out_pipes[1]);
                } else {
                        int devnull_fd = open("/dev/null", O_WRONLY);
                        (void) dup2(devnull_fd, STDOUT_FILENO);
                }

                if (err_p != NULL) {
                        (void) dup2(err_pipes[0], STDERR_FILENO);
                        (void) close(err_pipes[1]);
                } else {
                        int devnull_fd = open("/dev/null", O_WRONLY);
                        (void) dup2(devnull_fd, STDERR_FILENO);
                }

                (void) execl("/bin/sh", "sh", "-c", cmd, (char *)0);
                goto err_fork;

        } else {
                goto err_fork;
        }
        return (0);

err_fork:
        if (in_p != NULL) {
                (void) close(err_pipes[0]);
                (void) close(err_pipes[1]);
        }
err_error:
        if (out_p != NULL) {
                (void) close(out_pipes[0]);
                (void) close(out_pipes[1]);
        }
out_error:
        if (in_p != NULL) {
                (void) close(in_pipes[0]);
                (void) close(in_pipes[1]);
        }
in_error:
        return (errno);
}
        
int pclose3(FILE *in_p, FILE *out_p, FILE *err_p, int pid)
{
        int status;
        int child_status;

        if (in_p != NULL) {
                (void) fclose(in_p);
        }
        if (out_p != NULL) {
                (void) fclose(out_p);
        }
        if (err_p != NULL) {
                (void) fclose(err_p);
        }
        status = waitpid(pid, &child_status, 0);
        if (status == -1) {
                return(errno);
        }
        return(child_status);
}

int
be_run_cmd(char *command, 
    char *command_stdout, int cmd_stdout_bufsize,
    char *command_stderr, int cmd_stderr_bufsize)
{
        char *cmd_stdout_p = command_stdout;
        char *cmd_stderr_p = command_stderr;
        char oneline[BUFSIZ];
        FILE *in_stream = NULL;
        FILE *out_stream = NULL;
        FILE *err_stream = NULL;
        int pid;
        int oneline_strlen;
        int copysize;
        int rval;

        if (popen3(command, &in_stream, &out_stream, &err_stream, &pid) != 0) {
                perror ("popen3");
                return (errno);
        }

        fputs("hello\ngoodbye\n", in_stream);
        fclose(in_stream);
        if (out_stream == NULL) {
                snprintf(command_stderr, cmd_stderr_bufsize,
                    "Error running command \"%s\":out_stream is NULL:\n%s\n",
                    command, strerror(errno));
                rval = errno;
        } else if (err_stream == NULL) {
                snprintf(command_stderr, cmd_stderr_bufsize,
                    "Error running command \"%s\":err_stream is NULL:\n%s\n",
                    command, strerror(errno));
                rval = errno;
        } else {
                while (fgets(oneline, BUFSIZ, out_stream) != NULL) {
                        copysize = cmd_stdout_bufsize -
                            (cmd_stdout_p - command_stdout);
                        oneline_strlen =
                            strlcpy(cmd_stdout_p, oneline, copysize);
                        if (oneline_strlen >= copysize) {
                                cmd_stdout_p += copysize;
                                break;
                        }
                        cmd_stdout_p += oneline_strlen;
                }

                while (fgets(oneline, BUFSIZ, err_stream) != NULL) {
                        copysize = cmd_stderr_bufsize -
                            (cmd_stderr_p - command_stderr);
                        oneline_strlen =
                            strlcpy(cmd_stderr_p, oneline, copysize);
                        if (oneline_strlen >= copysize) {
                                cmd_stderr_p += copysize;
                                break;
                        }
                        cmd_stderr_p += oneline_strlen;
                }

                if ((rval = pclose3(NULL, out_stream, err_stream, pid)) == -1) {
                        copysize = cmd_stderr_bufsize -
                            (cmd_stderr_p - command_stderr);
                        snprintf(cmd_stderr_p, copysize,
                            "Warning: Error cleaning up and getting status of "
                            "command \"%s\":\n%s\n", command, strerror(errno));
                }
        }
        return (rval);
}


int main(int argc, char *argv[])
{
        int status;

        static char output_buffer[BUFSIZ];
        static char error_buffer[BUFSIZ];

        status = be_run_cmd("./first_test", output_buffer, BUFSIZ,
            error_buffer, BUFSIZ);
        printf ("status = %d\n", status);
        printf ("output:%s\n", output_buffer);
        printf ("error:%s\n", error_buffer);
}
#include <stdio.h>
#include <strings.h>

int main(int argc, char *argv[])
{
        char input[1024];
        char output[1024];
        char error[1024];

        while (gets(input) != NULL) {
                strcpy(output, "output:");
                strcat(output, input);
                strcat (output, " ");
                strcat(output, "output:");
                strcat(output, input);
                strcat (output, " ");

                strcpy(error, "error:");
                strcat(error, input);
                strcat (error, " ");
                strcat(error, "error:");
                strcat(error, input);
                strcat (error, " ");

                fputs(output, stdout);
                fputs(error, stderr);
                fputs(output, stdout);
                fputs(error, stderr);
        }

}
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <strings.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>

#define BUFSIZ 1024

int popen3a(char *cmd, FILE **out_p, FILE **err_p)
{
        char *temp_filename = strdup(tmpnam(NULL));
        char *cmdline;
        int cmdline_size;
        int rval = 0;

        if (out_p == NULL && err_p == NULL) {
                return (EINVAL);
        }

        cmdline_size = strlen(temp_filename) + strlen(cmd) + 5;
        cmdline = malloc(cmdline_size);
        strlcpy(cmdline, cmd, cmdline_size);
        strlcat(cmdline, " 2> ", cmdline_size);
        strlcat(cmdline, temp_filename, cmdline_size);

        if (mkfifo(temp_filename, 0600) != 0) {
                rval = errno;
                goto cleanup;
        }

        *out_p = popen(cmdline, "r");
        if (*out_p == NULL) {
                rval = errno;
                goto cleanup;
        }

        *err_p = fopen(temp_filename, "r");
        if (*out_p == NULL) {
                pclose (*out_p);
                rval = errno;
                goto cleanup;
        }

cleanup:
        (void) unlink(temp_filename);
        (void) free(temp_filename);
        (void) free(cmdline);
        return (rval);
}

int pclose3a(FILE *out_p, FILE *err_p)
{
        (void) fclose(err_p);
        return pclose(out_p);
}

int
be_run_cmd(char *command, 
    char *command_stdout, int cmd_stdout_bufsize,
    char *command_stderr, int cmd_stderr_bufsize)
{
        char *cmd_stdout_p = command_stdout;
        char *cmd_stderr_p = command_stderr;
        char oneline[BUFSIZ];
        FILE *out_stream = NULL;
        FILE *err_stream = NULL;
        int pid;
        int oneline_strlen;
        int copysize;
        int rval;

        if (popen3a(command, &out_stream, &err_stream) != 0) {
                perror ("popen3a");
                return (errno);
        }

        if (out_stream == NULL) {
                snprintf(command_stderr, cmd_stderr_bufsize,
                    "Error running command \"%s\":out_stream is NULL:\n%s\n",
                    command, strerror(errno));
                rval = errno;
        } else if (err_stream == NULL) {
                snprintf(command_stderr, cmd_stderr_bufsize,
                    "Error running command \"%s\":err_stream is NULL:\n%s\n",
                    command, strerror(errno));
                rval = errno;
        } else {
                while (fgets(oneline, BUFSIZ, out_stream) != NULL) {
                        copysize = cmd_stdout_bufsize -
                            (cmd_stdout_p - command_stdout);
                        oneline_strlen =
                            strlcpy(cmd_stdout_p, oneline, copysize);
                        if (oneline_strlen >= copysize) {
                                cmd_stdout_p += copysize;
                                break;
                        }
                        cmd_stdout_p += oneline_strlen;
                }

                while (fgets(oneline, BUFSIZ, err_stream) != NULL) {
                        copysize = cmd_stderr_bufsize -
                            (cmd_stderr_p - command_stderr);
                        oneline_strlen =
                            strlcpy(cmd_stderr_p, oneline, copysize);
                        if (oneline_strlen >= copysize) {
                                cmd_stderr_p += copysize;
                                break;
                        }
                        cmd_stderr_p += oneline_strlen;
                }

                if ((rval = pclose3a(out_stream, err_stream)) == -1) {
                        copysize = cmd_stderr_bufsize -
                            (cmd_stderr_p - command_stderr);
                        snprintf(cmd_stderr_p, copysize,
                            "Warning: Error cleaning up and getting status of "
                            "command \"%s\":\n%s\n", command, strerror(errno));
                }
        }
        return (rval);
}



int main(int argc, char *argv[])
{
        int status;
        static char output_buffer[BUFSIZ];
        static char error_buffer[BUFSIZ];

        if (argc < 2) {
                printf ("Usage: %s <command>\n", argv[0]);
                exit (0);
        }

        status = be_run_cmd(argv[1], output_buffer, BUFSIZ,
            error_buffer, BUFSIZ);
        printf ("status = %d\n", status);
        printf ("output:%s\n", output_buffer);
        printf ("error:%s\n", error_buffer);
}
_______________________________________________
caiman-discuss mailing list
[email protected]
http://mail.opensolaris.org/mailman/listinfo/caiman-discuss

Reply via email to