1

I have the following program:

int main(int argc, char **argv)
{
    char    *program;
    char     stringa[1000] = "";
    int num = 0;
    char snum[10];
    int pipefd[2];
    pipe(pipefd);
    program = argv[1];   

    sprintf(stringa, "./%s", program);

    pid_t pid = fork();
    if (pid < 0 ) {
        perror("fork failed."); 
        exit(1);
    }
    else if (pid == 0) { 
        char* args[] = {stringa, NULL};
        execv(args[0], args);
    }
    else {   
       char procmon_str[] = "./procmon";
       num = pid;
       sprintf(snum, "%d",num);

       pid_t pid2 = fork();
       if (pid2 == 0) { //launch 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);
       }
       else { 
           close(pipefd[1]);
           dup2(pipefd[0], STDIN_FILENO);
           close(pipefd[0]);
           char* args3[] = {"./filter", NULL};
           execv(args3[0], args3);    
       }
    }

   return 0;
}

I launch it like so:

./myProgram process

Then, the following happens:

  • myProgram launches process and determines its PID
  • It then launches the procmon program with that same PID
  • It will launch another process that runs the program filter

The output of procmon should be sent to the input of filter, meaning - filter will read from its standard input what procmon is writing to its standard output.

For some reason, i don't get the desired results.

procmons job is to take a given process's PID, access the corresponding /proc/PID/stat file and print the process state. filter needs to take that and only prints the line where the state changes from one to another. At the moment, i don't get anything from filter.

process goes into a loop (10 iterations) that sleeps for 3 seconds, and then starts another loop that increments a variable 400,000 times.

Am i doing it right?

gambit20088
  • 301
  • 1
  • 9
  • "I don't get the desired results" is a very bad description of your trouble. You should show what you get. You should explain why it isn't what you think you should get. You should give a clearer indication of what each of `process`, `procmon` and `filter` do. Since they're all run from the current directory, they're custom programs available only on your machine. – Jonathan Leffler Feb 01 '17 at 15:21
  • You're right, sorry. I edited my question. – gambit20088 Feb 01 '17 at 15:26
  • Is `filter` known to work? Is `procmon` known to work? I can't use `/proc/PID/stat` because macOS Sierra doesn't have a `/proc` file system, and I'm not on my machine with a Linux VM available. What is `filter` written in — C, Perl, shell, …? On the face of it, the problem is as likely to be in `filter` or perhaps `procmon` as in the program you showed. What does `process` do, come to that? Does it change state frequently enough? Does `procmon` ensure that it flushes its output lines so that the data goes down the pipe properly? – Jonathan Leffler Feb 01 '17 at 15:29
  • `procmon` is definitely known to work, not sure about `filter`. They are both written in C. I added a description of `process` in my question. – gambit20088 Feb 01 '17 at 15:41
  • I strongly recommend concentrating on `filter`. Drive it from the command line with `procmon`, using another terminal to run `process`. I'd even run `procmon` twice, once feeding to `filter` and once writing to the terminal (another one — three terminals available for inspection). The driving program in the question seems to be 'OK', so the culprit is likely one of the others. – Jonathan Leffler Feb 01 '17 at 15:44
  • If you use `dup2` system call, you don't need to close the file descriptor in advance, as it will be closed by it. Indeed, one could argue vaguely that it's better to not do it, as it is less error prone. – Luis Colorado Feb 02 '17 at 07:05

1 Answers1

3

"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.

Jonathan Leffler
  • 666,971
  • 126
  • 813
  • 1,185
  • I edited my question with more explanation regarding what i expect from my program. – gambit20088 Feb 01 '17 at 15:28
  • Given my edits and your test, i assume that if it still doesn't work for me there's probably a problem with `procmon` or `filter`? Given what `filter` does, how can i test it "individually"? – gambit20088 Feb 01 '17 at 15:44
  • I've added suggestions for 'how to test (`filter` in particular, also `procmon`)' underneath the main question. One of the reasons I used the test programs I chose is because I know they work reliably. You should aim to minimize the number of 'unknowns' you're dealing with. – Jonathan Leffler Feb 01 '17 at 15:47