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);
}
2
Answers
I believe the problem is that you are passing the wrong arguments to the
exec
call. You are setting up the pipes correctly, redirecting the reading end of the child’s pipe tostdin
, but you then passargv[1]
to the child (which is theless
binary).less
takes a file to read from as its first argument, so when you passargv[1]
the process just reads directly from that file instead of from the pipe you set up. This causesless
to appear to behave correctly, and the parent process gets blocked trying to write to the pipe, since nobody is reading from it.Try passing
"/dev/stdin"
instead ofargv[1]
toexec
and see if that solves your problem./dev/stdin
is a pseudo-file on linux referring to the input stream. Since this is the case, you will also need to pass-f
toless
to force it to display this file.Note that you will most likely still see the parent process running, since
less
is only reading from the file as needed, as stated in its man page.Edit: Instead of redirecting the pipe to
stdin
, you can also pass the pipe’s file descriptor directly to the process. All open file descriptors can be found at/dev/fd/
. Here you will find the files0
,1
and2
forstdin
,stdout
andstderr
respectively. Any currently opened file descriptors (e.g. files, pipes etc) can also be found here.The pipe’s file descriptor is stored in the integer variable you defined (
pfd
). So instead of redirecting tostdin
, you can do:The effect is the same.
In both cases, when
less
reaches the EOF, the parent will terminate andless
will also be shut down by the theinit
process (parent of all processes on the system).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, which has been redirected to the pipe:
So change
to