Thread: Problems with pipe and exec in linux

  1. #1
    Registered User
    Join Date
    Jan 2016
    Posts
    4

    Problems with pipe and exec in linux

    I want to communicate two programs using pipes.

    First of all I don't want to use named pipe, just simple dup2(). I have two programs:
    1) program2.c - want to read from pipes using select (check after 15s if is possibility). I use exec and pass the pipe to argument.
    2) program1.c - endless loop, waiting for signal, and after signal write to pipe


    I know that problem is around the pipe because when I don't use pipe - just simple text file - all work fine. What I get? Information from select - "No communicate so far.." Like select no return any result

    About pipes:
    1) pipefd[1] - write to pipe a short information about pid - program2.c
    2) pipefd[0] - read from pipefd[1] and display

    We run program like:
    ./program2 n

    n - how many process we want to have ( fork() )




    I would be grateful for any help

    I clean up a code so and remove unimportant things:


    gcc program1.c -lrt -o program1
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <signal.h>
    #include <time.h>
    #include <fcntl.h>
    #include <string.h>
    
    
    sig_atomic_t wyjsc = 0; 
    int *fd;
    
    void handler_signal(int signal)
    {
       sigset_t pending;  
       struct timespec ts; 
       char buf[10]; 
       int ret3; 
       ssize_t ret_out;
       int j = 0; 
    
    
       switch(signal)
       {
        case SIGUSR1: 
            wyjsc = 1; 
            break;
        case SIGALRM:
                wyjsc = 0; 
            break; 
        default:
            fprintf(stderr,"Bad singal: %d \n", signal); 
            return; 
      }
    
    
       // fd = open("?", O_RDWR | O_CREAT | O_APPEND); ???
       snprintf(buf, 10, "%ld\n", (long) getpid()); 
       while(wyjsc)
        {
        ret_out = write(fd[0], buf, strlen(buf));
        if(ret_out == -1) 
            perror("blad zapisu do bliku"); 
        }   
    
    
    }
    int main(int argc, char *argv[])
    {
       int i; 
       long val; 
       char *endptr; 
       struct sigaction sa;     
    
    
       if(argc != 2)
       {
        fprintf(stderr,"Niepoprawna liczba argumetnow procesu P \n");
        exit(EXIT_FAILURE);
       }    
    
    
       val = strtol(argv[1],&endptr, 0);    
    
    
    
       fd = (int *)val; // Can I do like that?
    
       sa.sa_handler = &handler_signal;
       sa.sa_flags = SA_RESTART | SA_NODEFER; 
       if(sigaction(SIGUSR1,&sa,NULL) == -1)
       {
        perror("Nie mozna obsluzyc SIGUSR1");   
       }
    
       while(1){ }  
    
       return;
    }


    gcc program2.h -o program2
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <signal.h>
    #include <time.h>
    #include <fcntl.h>
    #include <string.h>
    
    
    int main(int argc, char *argv[])
    {
      int i ; 
      pid_t pid; 
      pid_t pid2; 
    
    
      int val;
      char *endptr;  
    
      int pipefd[2]; 
    
      char parametr[32]; 
    
    
       if(argc != 2)
       {
            fprintf(stderr,"Niepoprawna liczba argumetnow procesu P \n");
            exit(EXIT_FAILURE);
       }
    
    
       val = strtol(argv[1],&endptr, 0);
    
      /* pipe  
          pipe0 - write
          pipe1 - read
      */
      if(pipe(pipefd) == -1)
       {
        perror("pipe");
        exit(EXIT_FAILURE);
       }
    
    
      sprintf(parametr,"%d",pipefd[0]); //convert - we want to use exec 
    
       pid = fork() ;
       if(pid < 0)
       {
        perror("fork!"); 
       }  
       else if(pid == 0)
       {
        /* FIRST CHILD ! 
            create all proces in val
        */
        for(i = 0;i<val; i++)
        {
                pid2 = fork(); 
                if(pid2 < 0)
                 {
                    perror("Fork");         
                 }
                 else if(pid2==0)
                 {
                    int ret;
                    /* Child process */     
             // read 
            dup2(pipefd[0],STDIN_FILENO); // write      
            close(pipefd[1]);
            ret = execl("program1","program1", parametr,NULL); 
                if(ret == -1)
                        perror("Execl");
                    }
                 else{
                        wait(NULL);
                 }
        } /* END OF FOR */ 
       }
       else{
        /* PARENT */
        // select //    
        fd_set set;
        struct timeval timeout;
        int sel; 
        char buf;   
    
        close(pipefd[0]) ;  
    
    
        FD_ZERO(&set); 
        FD_SET(pipefd[1], &set); // READ
    
    
        timeout.tv_sec = 17; 
        timeout.tv_usec = 0; 
    
    
        sel = select(FD_SETSIZE,&set,NULL,NULL,&timeout);
        if(sel < 0 )
        {
            perror("Select");  
        }
        else if(sel == 0)
        {
            printf("No communicate \n");
        }
        else{
            // read from pipe - 
            while(read(pipefd[0], &buf,1) > 0)
                write(STDOUT_FILENO, &buf,1);  
        }
    
       } 
    }
    Last edited by matkuz; 01-04-2016 at 07:33 AM.

  2. #2
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Do I understand your set up correctly? Below, master is your program2, and slave is your program1.
    • master creates N child processes, with one pipe from the standard output of each child back to the master process
    • each child process executes a separate slave binary
    • master uses select() to read from each pipe
    • each slave waits for a signal (SIGUSR1) before writing to the pipe; it then (closes the pipe and) exits
    • master waits until no more pipes are open
      (no more input is possible from the pipes)

    You should tackle the slave first. Assume that the parent/master has executed it with the write end of the pipe at standard output. In essence, the slave only needs to wait for a signal, then write to standard output, and exit.

    You do not need a signal handler for the slave at all. You can use sigwaitinfo() or sigtimedwait() to wait for a signal (in the specified set) to arrive. It does not need any arguments; it's very straightforward program. For ease of use, you should have it print its ID to standard error (will be visible on console),
    Code:
        fprintf(stderr, "Child %ld waiting for a signal.\n", (long)getpid());
        fflush(stderr);
    Write the slave first. You can then trivially test them, say four at a time, by running
    bash -c '( for ((i=0; i<4; i++)); do sleep 0.2 ; ./slave & done ; wait )'
    in one terminal, and sending the signals to each child in another. Their start is staggered by 0.2 seconds for easy debugging. You can see their pipe output and whatever they print to standard error in the first terminal.

    Only attack the master process when you get that working right.

    Now, your current slave process does not work. How would you fix it, if you knew the pipe write end is always at STDOUT_FILENO?

    (No, you cannot cast a long to an int using (int *). If lval is long and ival is int, the assignment using an explicit cast is just ival = (int)lval;. For numeric types, you can also just write ival = lval; so the compiler does the casting implicitly.)

  3. #3
    Registered User
    Join Date
    Jan 2016
    Posts
    4
    Thanks a lot mate ! It helped me really.

    Please tell me if I understand corectlly:
    1) I don't really need have sa.sa_handler in my program because I can use simply sigwaitforinfo -
    2) After that I exec the slave proces where:
    a) I delete stupid conversion, just do fd = lval;
    b) so I change the line to :
    Code:
    ret_out = write(fd, buf, strlen(buf)); // That's it?
    3) I change code in the master process because I should give STDOUT_FILENO instead STDIN_FILENO.

    When I could send a simple singal to child I should try to just transfer signal from master to child.

    What more I get one more question:
    3A)Where should I close the pipe in child?
    Code:
    dup2(pipefd[0],STDIN_FILENO); // write      
    close(pipefd[1]);
    close(pipefd[0]); // before exec shoud I close both?
    3B)
    Can I use sigwaitinfo if I have two signlas - head - SIGUSR1 and additionaly - SIGALRM ?


    I really need to pipe start working. Each comment is valuable.

    In the end I want mension that I have to keep the architecture like in first post...
    Last edited by matkuz; 01-04-2016 at 02:49 PM.

  4. #4
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Let's not run before we can walk. Consider the slave program only for now.

    You will save a lot of time and effort by working on one problem at a time. The slave can be tested without the master, so it makes a lot of sense to create, and test, it first. Completely.

    Quote Originally Posted by matkuz View Post
    1) I don't really need have sa.sa_handler in my program because I can use simply sigwaitforinfo
    True.

    Quote Originally Posted by matkuz View Post
    In the slave, 2) After that I exec the slave proces
    You don't need the fd at all. You can just print to standard output normally.

    If you do need to pass the file descriptor number as a parameter, you can use
    Code:
        FILE *pipeout;
        int   pipeoutfd;
        char  dummy;
    
        if (sscanf(argv[1], "%d %c", &pipeoutfd, &dummy) != 1 || pipeoutfd < 0) {
            fprintf(stderr, "%s: Invalid file descriptor number.\n", argv[1]);
            return EXIT_FAILURE;
        }
    
        pipeout = fdopen(pipeoutfd, "wb");
        if (!pipeout) {
            fprintf(stderr, "%s: Not a writable file descriptor.\n", argv[1]);
            return EXIT_FAILURE;
        }
    
        /* At this point, use only the 'pipeout' stream,
         * similar to stderr (or stdout).
         *
         * The stream now "owns" the file descriptor.
         * Do not use the pipeoutfd directly.
         * If the pipeout stream is closed, using
         *     fclose(pipeout);
         * the pipeoutfd file descriptor is closed too.
         *
         * After printing to the pipeout stream, you can call
         *     fflush(pipeout);
         * to make sure the data is written immediately to the pipe.
         * (Otherwise the library might buffer some writes, for
         *  efficiency.)
        */
    and then just fprintf(pipeout, "..."...); (optionally followed by fflush(pipeout); if you want to ensure the data gets written to the pipe now, instead of being buffered for later).

    Although the sscanf() does not detect integer overflow (whereas strtol() does), the above does recognize bad parameters, like a number followed by a letter. For full error checking, I like to use a separate helper function that uses strol(), checks for garbage following the number, and that the number fits in an int.

    Quote Originally Posted by matkuz View Post
    3) I change code in the master process
    Worry about this only after you get the slave working. I'll help you look at this in detail then.

    Quote Originally Posted by matkuz View Post
    3A)Where should I close the pipe in child?
    You don't.

    If you meant where in the master process, after forking a new child process but before executing the child process, there is more detail forthcoming on this. But again, worry about this after you get the slave working correctly.

    (To ease your mind: It is easy to fork the slave processes and create the pipes so that the slave processes will only see the write end of each pipe, and that will be the standard output. It does involve a few steps, including fcntl(pipe_read_end, F_SETFD, O_CLOEXEC), to make sure that the read ends of any pipes are not open in the child processes -- they're automatically closed when a child process executes the slave binary).



    Consider this example slave program:
    Code:
    #define  _POSIX_C_SOURCE 200809L
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <signal.h>
    #include <string.h>
    #include <stdio.h>
    #include <errno.h>
    
    const char *strsignalconst(const int signum)
    {
        static char buffer[16];
        switch (signum) {
        case SIGHUP:  return "SIGHUP";
        case SIGINT:  return "SIGINT";
        case SIGTERM: return "SIGTERM";
        case SIGPIPE: return "SIGPIPE";    
        case SIGUSR1: return "SIGUSR1";
        case SIGUSR2: return "SIGUSR2";
        case SIGKILL: return "SIGKILL";
        default:
            snprintf(buffer, sizeof buffer, "%d", signum);
            return (const char *)buffer;
        }
    }
    
    int main(void)
    {
        const long  pid = (long)getpid();
        sigset_t    mask;
        siginfo_t   info;
        int         signum;
    
        /* Mask of signals we wait for. */
        sigemptyset(&mask);
        sigaddset(&mask, SIGUSR1);
        sigaddset(&mask, SIGTERM);
    
        /* If the parent process dies before we do, and we try to write
         * to the pipe, we get an error, and a SIGPIPE signal, which by
         * default aborts the program. The error is enough for us,
         * so we will also block SIGPIPE. */
        sigaddset(&mask, SIGPIPE);
    
        /* We block the signals, so that they're not delivered
         * unless we specifically wait for them. */
        sigprocmask(SIG_BLOCK, &mask, NULL);
    
        /* This slave is now ready to receive the signal. */
        fprintf(stderr, "Slave %ld is ready for a SIGUSR1 signal.\n", pid);
        fflush(stderr);
    
        while (1) {
    
            /* Clear signal info structure. */
            memset(&info, 0, sizeof info);
    
            /* Wait for a signal. */
            signum = sigwaitinfo(&mask, &info);
    
            if (signum == -1) {
                /* Just interrupted for some reason? */
                if (errno == EINTR)
                    continue;
                /* Output the error string and exit. */
                fprintf(stderr, "Slave %ld: %s.\n", pid, strerror(errno));
                return EXIT_FAILURE;
            }
    
            break;
        }
    
        /* Print signal information to standard error. */
        fprintf(stderr, "Slave %ld exited due to signal %d (%s) from process %ld.\n",
                        pid, signum, strsignalconst(signum), (long)info.si_pid);
        fflush(stderr);
    
        /* Print signal information to standard output. */
        fprintf(stdout, "Slave %ld: %s from %ld.\n",
                        pid, strsignalconst(signum), (long)info.si_pid);
        fflush(stdout);
    
        /* If there was an I/O error, return EXIT_FAILURE. */
        if (ferror(stdout) || ferror(stderr))
            return EXIT_FAILURE;
    
        return EXIT_SUCCESS;
    }
    You can compile it using e.g.
    gcc -Wall -Wextra -O2 slave.c -o slave

    To test it, run in one terminal
    Code:
    bash -c '( for((i=0; i<4; i++)); do sleep .2 ; ./slave & done ; wait )'
    or, if you want each slave output to a separate file (simulates a pipe better),
    Code:
    bash -c '( for((i=0; i<4; i++)); do sleep .2 ; rm -f out.$i ; ./slave > out.$i & done ; wait )'
    Look at the process numbers in the Slave # is ready for a SIGUSR1 signal lines. In another terminal, you can use
    kill -USR1 #
    or for example
    kill -TERM #
    where the # is a process number. Each slave will report which signal killed it, and which process sent the signal. After all slaves are killed, the bash command in the original terminal will complete.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Problems with pipe and exec in linux
    By matkuz in forum C Programming
    Replies: 2
    Last Post: 01-04-2016, 07:34 AM
  2. Exec and Pipe problem
    By Matteaus in forum C Programming
    Replies: 2
    Last Post: 09-06-2010, 09:02 AM
  3. Replies: 4
    Last Post: 10-14-2009, 04:44 PM
  4. Replies: 3
    Last Post: 06-02-2009, 06:13 PM
  5. exec with linux pipe command
    By rotis23 in forum C Programming
    Replies: 4
    Last Post: 08-22-2002, 08:34 AM

Tags for this Thread