"Don't get the desired results" is not a good description of the problem you are facing.
On the whole, the code is not bad. I've made some significant changes and a load of insignificant ones (moving variable declarations, initializing variables instead of assigning them, formatting). The significant changes include:
- Checking that the program is called with an argument.
- Not creating the pipe until after the first child is created.
- Reporting errors and exiting if the
execv()
fails.
- Not redirecting standard error of the
procmon
process to the pipe.
The pipe creation might be important. As originally written, the process
had both ends of the pipe open, so filter
would not get EOF on the pipe while process
continued. Since process
is unlikely to use the pipe (it has no formal knowledge of which file descriptors are open for them), there really is no point and could be some harm in keeping the pipe open.
Not redirecting standard error to the pipe allowed me to see an error message from not having a shebang in the test scripts I used.
I used a set of functions from stderr.h
and stderr.c
, which are available on GitHub at https://github.com/jleffler/soq/tree/master/src/libsoq. They simplify error reporting, so most of my programs use them.
This leads to the following code, which is recognizably similar to what you have:
#include "stderr.h"
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
err_setarg0(argv[0]);
if (argc != 2)
err_usage("program");
pid_t pid = fork();
if (pid < 0)
err_syserr("failed to fork");
else if (pid == 0)
{
char stringa[1000] = "";
char *program = argv[1];
sprintf(stringa, "./%s", program);
char *args[] = {stringa, NULL};
execv(args[0], args);
err_syserr("failed to execute '%s': ", args[0]);
}
else
{
int pipefd[2];
pipe(pipefd);
pid_t pid2 = fork();
if (pid2 < 0)
err_syserr("failed to fork");
else if (pid2 == 0) // launch procmon
{
int num = pid;
char snum[10];
sprintf(snum, "%d", num);
char procmon_str[] = "./procmon";
char *args2[] = {procmon_str, snum, NULL};
close(pipefd[0]); // close reading end in the child
dup2(pipefd[1], 1); // send stdout to the pipe
//dup2(pipefd[1], 2); // send stderr to the pipe
close(pipefd[1]); // this descriptor is no longer needed
execv(args2[0], args2);
err_syserr("failed to execute '%s': ", args2[0]);
}
else
{
close(pipefd[1]);
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
char *args3[] = {"./filter", NULL};
execv(args3[0], args3);
err_syserr("failed to execute '%s': ", args3[0]);
}
}
/*NOTREACHED*/
return 0;
}
I was then faced with the problem of testing this. I created three shell scripts — process
, procmon
and filter
. It seems that it is not critical what process
does as long as it takes some time doing it. The procmon
is probably meant to monitor a process's status; it can't be a standard program because you run it in the current directory. Similarly, filter
is presumably meant to modify what it reads from its input. So, I invented scripts to do those jobs:
process
#!/bin/sh
exec timeout -t 2m -- dribbler -m "$0: PID $$" -r 0.2 -s 0.5 -t
procmon
#!/bin/sh
exec timeout -t 2m -- dribbler -m "$0: PID $1" -r 0.3 -t
filter
#!/bin/sh
echo "$0 at work"
exec grep -e '^[0-9]*9[0-9]*:' -- -
The dribbler
program is a home-brew that writes information slowly, and the timeout
program (also home-brew, with versions back to 1989, rather than the GNU program of the same name) stops its process after a designated time. The -r
and -s
options to dribbler
implement a Gaussian time distribution (-s
the mean sleep time, defaulting to one second, -r
the standard deviation of the randomness). The filter
script announces that it is busy and then looks for 9s in the first field of the output.
With that infrastructure, I got output like:
$ pp37 process
./filter at work
0: ./process: PID 48812
1: ./process: PID 48812
2: ./process: PID 48812
…
9: ./process: PID 48812
10: ./process: PID 48812
…
20: ./process: PID 48812
21: ./process: PID 48812
9: ./procmon: PID 48812
22: ./process: PID 48812
23: ./process: PID 48812
…
92: ./process: PID 48812
93: ./process: PID 48812
49: ./procmon: PID 48812
94: ./process: PID 48812
95: ./process: PID 48812
96: ./process: PID 48812
97: ./process: PID 48812
98: ./process: PID 48812
99: ./process: PID 48812
100: ./process: PID 48812
101: ./process: PID 48812
102: ./process: PID 48812
…
116: ./process: PID 48812
117: ./process: PID 48812
59: ./procmon: PID 48812
118: ./process: PID 48812
119: ./process: PID 48812
…
140: ./process: PID 48812
69: ./procmon: PID 48812
141: ./process: PID 48812
…
161: ./process: PID 48812
162: ./process: PID 48812
79: ./procmon: PID 48812
163: ./process: PID 48812
…
179: ./process: PID 48812
180: ./process: PID 48812
89: ./procmon: PID 48812
181: ./process: PID 48812
182: ./process: PID 48812
90: ./procmon: PID 48812
183: ./process: PID 48812
91: ./procmon: PID 48812
184: ./process: PID 48812
185: ./process: PID 48812
186: ./process: PID 48812
92: ./procmon: PID 48812
187: ./process: PID 48812
188: ./process: PID 48812
93: ./procmon: PID 48812
189: ./process: PID 48812
94: ./procmon: PID 48812
190: ./process: PID 48812
191: ./process: PID 48812
95: ./procmon: PID 48812
192: ./process: PID 48812
193: ./process: PID 48812
96: ./procmon: PID 48812
194: ./process: PID 48812
195: ./process: PID 48812
196: ./process: PID 48812
97: ./procmon: PID 48812
197: ./process: PID 48812
98: ./procmon: PID 48812
198: ./process: PID 48812
199: ./process: PID 48812
200: ./process: PID 48812
201: ./process: PID 48812
99: ./procmon: PID 48812
202: ./process: PID 48812
…
220: ./process: PID 48812
109: ./procmon: PID 48812
221: ./process: PID 48812
…
234: ./process: PID 48812
235: ./process: PID 48812
$
The output of process
is not filtered so every line of that is shown, but the output of procmon
is filtered and only lines with a 9
are shown. This seems to be behaving correctly.