I am currently working on a university project to basically built my own simple shell. Everything is working great so far. The only thing giving me trouble is pipes. To make it easier for myself to figure out why they are not working as intended I wrote this little testing program where I try to replicate the bash behaviour of cat | ls
cat | ls
. But i now sadly get this error cat: stdin: Input/output error
and i really can't figure it out.
Here is my program:
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
extern char **environ;
char *argv1[] = {"cat",NULL};
char *argv2[] = {"ls",NULL};
int fd[2];
pid_t pid;
int ret;
pipe(fd);
pid = fork();
if (pid == 0)
{
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
execve("/bin/cat", argv1, environ);
exit (0);
}
else if (pid > 0)
{
close(fd[1]);
dup2(fd[1], STDIN_FILENO);
execve("/bin/ls", argv2, environ);
waitpid(pid, &ret, 0);
}
return (0);
}
You want a pipe like:
ls | cat
But, you're setting this up like:
cat | ls
And, in your current code, for the ls
side, you're doing:
close(fd[1]);
dup2(fd[1], STDIN_FILENO);
This is wrong for two reasons:
dup2
gets a closed fd as its first argumentSo, we need to reverse the pipe order and fix the closing.
Also, after doing dup2(X,...)
we want to do close(X)
.
Also, note that doing waitpid
_after execve
will have no effect unless the execve
fails.
Here is the refactored and working code:
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int
main(void)
{
extern char **environ;
char *argv_cat[] = { "cat", NULL };
char *argv_ls[] = { "ls", NULL };
int fd[2];
pid_t pid;
int ret;
pipe(fd);
pid = fork();
// we want:
// ls | cat
if (pid == 0) {
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execve("/bin/cat", argv_cat, environ);
exit(0);
}
else if (pid > 0) {
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
execve("/bin/ls", argv_ls, environ);
waitpid(pid, &ret, 0);
}
return (0);
}
UPDATE:
waitpid
afterexecve
is pointless. – William Pursell
Not quite. It reaps the [stuck] child process so that it doesn't become a child of the init/systemd
process.
In the above example, I had forgotten to add a close(STDOUT_FILENO)
before the waitpid
to "release" the cat
process.
Here is the adjusted code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
int opt_f;
int
main(int argc,char **argv)
{
extern char **environ;
char *argv_cat[] = { "cat", NULL };
char *argv_ls[] = { "ls", NULL };
int fd[2];
pid_t pid;
int status;
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
char *cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'f':
opt_f = ! opt_f;
break;
}
}
pipe(fd);
pid = fork();
// we want:
// ls | cat
if (pid == 0) {
if (opt_f)
fprintf(stderr,"cld: getpid=%d\n",getpid());
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execve("/bin/cat", argv_cat, environ);
exit(0);
}
else if (pid > 0) {
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
const char *ls = opt_f ? "/bin/gooch" : "/bin/ls";
execve(ls, argv_ls, environ);
fprintf(stderr,"execve failure of '%s' -- %s\n",ls,strerror(errno));
// release other process (cat)
close(STDOUT_FILENO);
// reap the child
pid_t ret = waitpid(pid, &status, 0);
fprintf(stderr,"ret=%d pid=%d status=%8.8X\n",ret,pid,status);
}
return (0);
}
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.