13

My question is extension of this one: popen creates an extra sh process

Motives:

1) My program need to create a child which does tail on a file. I need to process the output line by line. That is why I am using popen because it returns FILE *. I can easily fetch single line, do what I need to do and print it.

One problem with popen is that you do not get pid of child (tail command in my case).

2) My program should not exit before its child is done. So I need to do wait; but without pid, I cannot do it.

How can I achieve both the goals?

A possible (kludge) solution: do execvp("tail -f file > tmpfile") and the keep reading that tmpfile. I am not sure how good this solution is, though.

Community
  • 1
  • 1
hari
  • 9,439
  • 27
  • 76
  • 110

4 Answers4

20

Why aren't you using pipe/fork/exec method?

pid_t pid = 0;
int pipefd[2];
FILE* output;
char line[256];
int status;

pipe(pipefd); //create a pipe
pid = fork(); //span a child process
if (pid == 0)
{
// Child. Let's redirect its standard output to our pipe and replace process with tail
 close(pipefd[0]);
 dup2(pipefd[1], STDOUT_FILENO);
 dup2(pipefd[1], STDERR_FILENO);
 execl("/usr/bin/tail", "/usr/bin/tail", "-f", "path/to/your/file", (char*) NULL);
}

//Only parent gets here. Listen to what the tail says
close(pipefd[1]);
output = fdopen(pipefd[0], "r");

while(fgets(line, sizeof(line), output)) //listen to what tail writes to its standard output
{
//if you need to kill the tail application, just kill it:
  if(something_goes_wrong)
    kill(pid, SIGKILL);
}

//or wait for the child process to terminate
waitpid(pid, &status, 0);
Patryk
  • 1,421
  • 8
  • 21
  • 1
    Do I need to `close(pipefd[0])` after I am done reading at the end? or right before `kill`? – hari Aug 03 '11 at 19:32
  • Incidentally, if you use GLib, there's a pre-baked implementation of this with tons of features, plus it handles all the errors codes and so on. See http://developer.gnome.org/glib/2.30/glib-Spawning-Processes.html (disclaimer: I implemented this, though many years ago) – Havoc P Dec 11 '11 at 23:17
  • @havoc or libHX's HXproc_* family of functions, which does not use functions with that awkardly-many parameters as glib. – jørgensen Dec 11 '11 at 23:43
  • sure, just mentioning the library I know. – Havoc P Dec 11 '11 at 23:52
1
  1. You can use pipe, a function of the exec* family and fdopen. This is non-standard, but so is popen.
  2. You don't need to wait. Just read the pipe up to EOF.
  3. execvp("tail -f file > tmpfile") won't work, redirection is a feature of the shell and you're not running the shell here. Even if it worked it would be an awful solution. Suppose you have read to the end of the file, but the child process has not ended yet. What do you do?
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • 4
    1. They are not non-standard, just outside of the C standard. They are part of the POSIX standard. – kennytm Jul 19 '11 at 08:21
0

You can use wait as it doesn't want a PID to wait for but simply waits for the any child process to exit. If you have created other child processes you can keep track of them, and if wait returns an unknown PID you can assume it's from your popen process.

DarkDust
  • 90,870
  • 19
  • 190
  • 224
  • If in some error case, I need to kill the child, can I do that? (does that scenario arise in the first place?), I am just thinking out loud. – hari Jul 19 '11 at 07:47
  • Lets say an error occurred in the program and it has to exit, will `wait` make sure that child is also killed/terminated before exiting main program? – hari Jul 19 '11 at 07:52
  • You could [create a process group](http://stackoverflow.com/questions/2281690/multithreaded-c-program-how-to-kill-processes-spawned-by-threads/2281729#2281729), then simply calling `kill(getpgrp(), SIGTERM);` will kill all your processes. – DarkDust Jul 19 '11 at 08:05
0

I'm not sure why you need the process ID of the child. When the child exits, your pipe read will return an EOF. If you need to terminate the child, just close the pipe.

jbruni
  • 1,238
  • 8
  • 12
  • a child can be "tail -f" which never returns. In that case, when I do ctrl-C, before my main program exits, I want the child - "tail -f" to be done. – hari Jul 19 '11 at 08:15