Getting PID from popen

I have a program that uses popen()

to open and read the output from a shell command. The problem is, as far as I can tell, there is no easy way to get the PID of a running process, and therefore you cannot kill it if it gets stuck. So the question is, how can you get the PID from a process opened with popen

?

+7


source to share


2 answers


The solution I came across (and the general consensus) is to create a new function popen

that allows me to get the PID. Since I couldn't find a simple example of this on SO, I wanted to post my implementation in the hope that it will help someone else. Feedback and alternative solutions are welcome.



#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <errno.h>
#include <string>
#include <sstream>

using namespace std;

#define READ   0
#define WRITE  1
FILE * popen2(string command, string type, int & pid)
{
    pid_t child_pid;
    int fd[2];
    pipe(fd);

    if((child_pid = fork()) == -1)
    {
        perror("fork");
        exit(1);
    }

    /* child process */
    if (child_pid == 0)
    {
        if (type == "r")
        {
            close(fd[READ]);    //Close the READ end of the pipe since the child fd is write-only
            dup2(fd[WRITE], 1); //Redirect stdout to pipe
        }
        else
        {
            close(fd[WRITE]);    //Close the WRITE end of the pipe since the child fd is read-only
            dup2(fd[READ], 0);   //Redirect stdin to pipe
        }

        setpgid(child_pid, child_pid); //Needed so negative PIDs can kill children of /bin/sh
        execl("/bin/sh", "/bin/sh", "-c", command.c_str(), NULL);
        exit(0);
    }
    else
    {
        if (type == "r")
        {
            close(fd[WRITE]); //Close the WRITE end of the pipe since parent fd is read-only
        }
        else
        {
            close(fd[READ]); //Close the READ end of the pipe since parent fd is write-only
        }
    }

    pid = child_pid;

    if (type == "r")
    {
        return fdopen(fd[READ], "r");
    }

    return fdopen(fd[WRITE], "w");
}

int pclose2(FILE * fp, pid_t pid)
{
    int stat;

    fclose(fp);
    while (waitpid(pid, &stat, 0) == -1)
    {
        if (errno != EINTR)
        {
            stat = -1;
            break;
        }
    }

    return stat;
}

int main()
{
    int pid;
    string command = "ping 8.8.8.8"; 
    FILE * fp = popen2(command, "r", pid);
    char command_out[100] = {0};
    stringstream output;

    //Using read() so that I have the option of using select() if I want non-blocking flow
    while (read(fileno(fp), command_out, sizeof(command_out)-1) != 0)
    {
        output << string(command_out);
        kill(-pid, 9);
        memset(&command_out, 0, sizeof(command_out));
    }

    string token;
    while (getline(output, token, '\n'))
        printf("OUT: %s\n", token.c_str());

    pclose2(fp, pid);

    return 0;
}

      

+11


source


EXPLANATION

I tried to use certain functions using @ Gillespie's answer, but found that pid

the C / C ++ program was different from the one returned by the terminal command pgrep

, and looking at the output ps -aux | grep myNameProc

, it seemed like the C program process had been forked once more.

I think because it is execl("/bin/sh", "/bin/sh", "-c", command.c_str(), NULL);

actually equivalent /bin/sh cmd string

. So basically the child process of your C (or C ++) program creates a new process that executes /bin/sh yourRealProcess

, where yourRealProcess

is the one given on the line command

.

I decided to do the following: execl(command.c_str(), command.c_str(), (char*)NULL);

. However, as @Gillespie pointed out in the previous comments, this way you won't be able to pass arguments to your process.



C IMPLEMENTATION

As per my needs, I rewrote @Gillespie's functions to include the above modification and work in the C programming language:

FILE * custom_popen(char* command, char type, pid_t* pid)
{
    pid_t child_pid;
    int fd[2];
    pipe(fd);

    if((child_pid = fork()) == -1)
    {
        perror("fork");
        exit(1);
    }

    /* child process */
    if (child_pid == 0)
    {
        if (type == 'r')
        {
            close(fd[0]);    //Close the READ end of the pipe since the child fd is write-only
            dup2(fd[1], 1); //Redirect stdout to pipe
        }
        else
        {
            close(fd[1]);    //Close the WRITE end of the pipe since the child fd is read-only
            dup2(fd[0], 0);   //Redirect stdin to pipe
        }

        setpgid(child_pid, child_pid); //Needed so negative PIDs can kill children of /bin/sh
        execl(command, command, (char*)NULL);
        exit(0);
    }
    else
    {
        printf("child pid %d\n", child_pid);
        if (type == 'r')
        {
            close(fd[1]); //Close the WRITE end of the pipe since parent fd is read-only
        }
        else
        {
            close(fd[0]); //Close the READ end of the pipe since parent fd is write-only
        }
    }

    *pid = child_pid;

    if (type == 'r')
    {
        return fdopen(fd[0], "r");
    }

    return fdopen(fd[1], "w");
}

int custom_pclose(FILE * fp, pid_t pid)
{
    int stat;

    fclose(fp);
    while (waitpid(pid, &stat, 0) == -1)
    {
        if (errno != EINTR)
        {
            stat = -1;
            break;
        }
    }

    return stat;
}

      

0


source







All Articles