We have a homework question where we have to write a program that takes two arguments, executes them as shell commands pipeing the stdout of the first program into the stdin of the second program.
This is my solution to this homework question. The same file is reproduced below.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#define SHELL "/bin/sh"
extern int main(int, char*[]);
static int readingend(int, int, const char*);
static int writingend(int, int, const char*);
static int usr1handler();
extern int
main(int argc, char *argv[])
{
int pipefd[2], status;
pid_t rpid, wpid;
if (argc != 3) {
fprintf(stderr, "Invalid argument count\n");
return (EXIT_FAILURE);
}
if (pipe(pipefd)) {
perror("Cannot make a pipe");
return (EXIT_FAILURE);
}
signal(SIGUSR1, (void(*)(int))usr1handler);
if ((rpid = fork()) < 0) {
perror("Cannot fork");
return (EXIT_FAILURE);
}
if (rpid == 0)
return (readingend(pipefd[0], pipefd[1], argv[2]));
if ((wpid = fork()) < 0) {
perror("Cannot fork");
kill(rpid, SIGKILL);
return (EXIT_FAILURE);
}
if (wpid == 0)
return (writingend(pipefd[0], pipefd[1], argv[1]));
close(pipefd[0]);
close(pipefd[1]);
waitpid(rpid, &status, 0);
return (status);
}
static int
readingend(int rfd, int wfd, const char *cmd)
{
close(wfd);
dup2(rfd, STDIN_FILENO);
/* execl() only returns on error */
execl(SHELL, SHELL, "-c", cmd, NULL);
fprintf(stderr, "Cannot execute %s: ", cmd);
perror(NULL);
/* clean up */
kill(getppid(), SIGUSR1);
return (EXIT_FAILURE);
}
static int
writingend(int rfd, int wfd, const char *cmd)
{
close(rfd);
dup2(wfd, STDOUT_FILENO);
execl(SHELL, SHELL, "-c", cmd, NULL);
fprintf(stderr, "Cannot execute %s: ", cmd);
perror(NULL);
/* clean up */
kill(getppid(), SIGUSR1);
return (EXIT_FAILURE);
}
static int
usr1handler()
{
/* child already printed diagnostic */
exit(EXIT_FAILURE);
}
The problem is that in some invocations, the first process receives an EIO
upon reading from stdin. This seems to happen only on some shells: It happens with ksh93
and bash
but not with bosh
. Such an error looks like this:
$ LANG=C ./mypipe cat true $ cat: -: Input/output error
Can anybody tell me why this error occurs? What can I do to have that error not occur anymore? Clearly there must be a way as executing cat | true
cat | true
in an ordinary shell produces the desired result without any spurious EIO
.
This issue happens because true
instantly terminates, causing mypipe
to terminate, too, leaving cat
orphaned. If this happens, read()
may fail:
EIO
The process is a member of a background process attempting to read from its controlling terminal, the process is ignoring or blocking the
SIGTTIN
signal, or the process group is orphaned. This error may also be generated for implementation-defined reasons.
This issue can be prevented by waiting on both processes like this:
extern int
main(int argc, char *argv[])
{
int status, estatus;
/* ... */
/* wait for both processes to terminate */
if (wait(&status) == rpid) {
estatus = status;
wait(&status);
} else {
wait(&status);
estatus = status;
}
return (estatus);
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.