9

Right now, I'm having to start an external process in C. I'm currently using posix_spawn to create the process. It is necessary that I can monitor whether or not the process has terminated. I need to also have a link to the standard out of the process. I've looked at using popen, however, it does not provide an "easy" way of getting the pid. I'm slowly going insane as it can't possibly be this hard to get the stdout of a running process in Linux.

Also, on a further note, I need help deciphering what the file_actions parameter is supposed to mean. man(3) for posix_spawn on this topic says:

If file_actions is not NULL, then the file descriptors open in the child process shall be those open in the calling process as modified by the spawn file actions object pointed to by file_actions and the FD_CLOEXEC flag of each remaining open file descriptor after the spawn file actions have been processed.

If that isn't the definition of a run-on sentence, I have no idea what is.

suspectus
  • 16,548
  • 8
  • 49
  • 57
alekpr
  • 145
  • 3
  • 6
  • When you say STD_OUT, do you mean PID? – suspectus Feb 05 '14 at 18:29
  • No, I mean STD_OUT. I want the standard output of the process with the given PID, which I have from creating it using posix_spawn. – alekpr Feb 05 '14 at 18:35
  • Maybe you want [popen(3)](http://man7.org/linux/man-pages/man3/popen.3.html), otherwise I don't understand your question. Read [Advanced Linux Programming](http://advancedlinuxprogramming.com/) – Basile Starynkevitch Feb 05 '14 at 18:39
  • The problem with using popen is that it doesn't give you the process ID, or any way to monitor whether the process has finished. – alekpr Feb 05 '14 at 18:41
  • 3
    STD_OUT amended to stdout – suspectus Feb 05 '14 at 18:42
  • @alekpr: what and why do you want to monitor? – Karoly Horvath Feb 05 '14 at 18:43
  • 2
    Maybe this post can help: http://stackoverflow.com/questions/3642732/using-dup2-for-piping. You can use fork that clones your process and the file descriptors are the same on the child and parent. By using pipe, fcntl and dup2 you could make a new file descriptor and pipe it back to the parent. Execve preserves the file descriptors 0, 1 and 2 to whatever they points to (normaly stdin, stdout and stderr). – Aquaplanet Feb 05 '14 at 18:47
  • @KarolyHorvath, "...or any way to monitor whether the process has finished." So I can tell if the process has finished. – alekpr Feb 05 '14 at 18:53
  • @Aquaplanet I was trying to avoid having to use fork(), but if I have to I guess I have no choice. – alekpr Feb 05 '14 at 18:56

2 Answers2

14

Since you have the PID (returned from posix_spawn) and you are running Linux, you will find the stdout of the process at /proc/<pid>/fd/1. Just open (or fopen) the file for reading.

The standard way is to use fork though. Use pipe and dup2 to get a file descriptor for reading the child's output, as in this question.

Community
  • 1
  • 1
Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
  • 1
    Also, prefer to `fork` to avoid the small race condition (the process may have died before the call to `open`) – Alexandre C. Feb 05 '14 at 20:01
  • 1
    Thanks, I was wondering what all of the entries in the /proc//fd represented. I'm really trying to avoid using fork/pipe/dup2, but it looks like I might have to. – alekpr Feb 05 '14 at 20:04
  • 1
    Those entries in the proc are the open file descriptors for the corresponding process. – Alexandre C. Feb 05 '14 at 20:22
  • `fork` will create a new process, but what if the process is already running (existed) as the question asked? – Felix F Xu Jun 23 '23 at 14:39
4

You can use posix_spawn for this, without having to use race-condition-prone, Linux-specific /proc/<pid>/fd/N. You can keep all the benefits of posix_spawn.

You were on the right track thinking about file_actions. Below is an example that prints out the child's stdout in Python-style triple quotes, as well as the child's exit code, from the parent process using posix_spawn and file_actions.

Here is an example of the example output.

child pid: 17468
child exit status: 0
child stdout:
"""Hello World!
"""

Here is the example.

#define _DEFAULT_SOURCE
#include <spawn.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

extern char **environ;

static void dump_child_stdout(int filedes)
{
    ssize_t num_read;
    char buf[1];

    printf("child stdout:\n\"\"\"");
    for (;;)
    {
        num_read = read(filedes, buf, sizeof(buf));
        if (num_read > 0)
        {
            printf("%c", buf[0]);
        }
        else
        {
            break;
        }
    }
    printf("\"\"\"\n");
}

int main(int argc, char *argv[])
{
    int status;
    pid_t pid;
    int out[2];
    posix_spawn_file_actions_t action;
    char *args[] = {"/bin/echo", "Hello World!", NULL };

    posix_spawn_file_actions_init(&action);

    pipe(out);

    posix_spawn_file_actions_adddup2(&action, out[1], STDOUT_FILENO);
    posix_spawn_file_actions_addclose(&action, out[0]);

    status = posix_spawn(&pid, args[0], &action, NULL, args, environ);
    if (status == 0)
    {
        printf("child pid: %d\n", pid);
        if (waitpid(pid, &status, 0) < 0)
        {
            perror("waitpid");
        }
        else
        {
            if (WIFEXITED(status))
            {
                printf("child exit status: %d\n", WEXITSTATUS(status));
            }
            else
            {
                printf("child died an unnatural death.\n");
            }

            close(out[1]);
            dump_child_stdout(out[0]);
        }
    }
    else
    {
        fprintf(stderr, "posix_spawn: %s\n", strerror(status));
        close(out[1]);
    }

    posix_spawn_file_actions_destroy(&action);

    return 0;
}
Patater
  • 117
  • 7
  • It's important to note that the buffer is not null terminated. This can cause problems if you want to create a string from the characters returned. – Dula Jan 09 '16 at 00:23