Search code examples
clinuxgccforkwait

Why parent process is active when not using wait function?


I am using pipes to display file contents. When I comment the wait command (in the last part of the parent process), I expect to see the parent process in the ps command's output (after a while, because reading is completed and I am still browsing output generated by child process), but I see both parent and child processes in the output.

The following code shows argv1 content. After building the program (for example xpager) it should be executed as xpager filename.

My platform is Ubuntu 22.04.4 LTS.

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h>

#define BUFSIZE 8192
#define PPAGER  "/usr/bin/less"

int main(int argc, char *argv[]) {
    pid_t   pid;
    int eerrno, n;
    int in, pfd[2];
    char    buf[BUFSIZE];
    char    *pager, *argv0;


    if (argc != 2) {
        printf("usage: %s <pathname>\n", argv[0]);
        exit(1);
    }

    if ( (in = open(argv[1], O_RDONLY)) < 0) {
        eerrno = errno;
        printf("Open error. %s(%d)\n", strerror(errno), errno);
        exit(eerrno);
    }

    if ( pipe(pfd) < 0) {
        eerrno = errno;
        printf("pipe error. %s(%d)\n", strerror(errno), errno);
        exit(eerrno);
    }


    pid = fork();
    switch (pid) {
        case -1:
            eerrno = errno;
            printf("fork error. %s(%d)\n", strerror(errno), errno);
            exit(eerrno);

        case 0:
            close(pfd[1]);
            close(in);
            if (STDIN_FILENO != pfd[0]) {
                if (dup2(pfd[0], STDIN_FILENO) < 0) {
                    eerrno = errno;
                    printf("dup2 error. %s(%d)\n", strerror(errno), errno);
                    exit(eerrno);
                }
                close(pfd[0]);
            }
            if ((pager=getenv("PPAGER")) == NULL)
                pager = PPAGER;
            if ((argv0=strrchr(pager, '/')) != NULL)
                argv0++;
            else
                argv0=pager; 
            execlp(pager, argv0, argv[1], (char *)0);
        default:
            close(pfd[0]);
            while((n=read(in, buf, BUFSIZE)) > 0) {
                if (write(pfd[1], buf, n) < 0) {
                    eerrno = errno;
                    printf("write errno. %s(%d)\n", strerror(errno), errno);
                    kill (pid, 9);
                    exit(eerrno);
                }
            }
            if (n < 0) {
                eerrno = errno;
                printf("read error. %s(%d)\n", strerror(errno), errno);
                kill(pid, 9);
                exit(eerrno);
            }
            close(pfd[1]);
        //  wait(NULL);
    }

    exit(0);
}

For easier reading, summarized code is as follow:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/wait.h>

#define BUFSIZE 8192
#define PPAGER  "/usr/bin/less"

int main(int argc, char *argv[]) {
        pid_t   pid;
        int     n,in, pfd[2];
        char    buf[BUFSIZE];
        char    *pager, *argv0;

        in = open(argv[1], O_RDONLY);
        pipe(pfd);

        pid = fork();
        switch (pid) {
                case 0:
                        close(pfd[1]);
                        dup2(pfd[0], STDIN_FILENO);
                        pager = PPAGER;
                        argv0="less";
                        execlp(pager, argv0, argv[1], (char *)0);
                default:
                        close(pfd[0]);
                        while ((n=read(in, buf, BUFSIZE)) > 0)
                                write(pfd[1], buf, n);
                        close(pfd[1]);
                //      wait(NULL);
        }
        return(0);
   }

Solution

  • Get rid of the filename argument when calling the pager in the child process. When it's not given a filename, it will read from its standard input by default, and stdin has been redirected to the pipe:

    So change

    execlp(pager, argv0, argv[1], (char *)0);
    

    to

    execlp(pager, argv0, (char *)0);