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
?
source to share
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;
}
source to share
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;
}
source to share