Reputation: 282
I'm experimenting with Linux and I'm working currently on writing a program which simulates the Linux shell.
I have a main function which parses the input and for my question is irrelevant for now. After each line is parsed the process line method is called which handles everything. For now I'm supporting regular processes, background processes and currently working on unnamed pipes that contain only 2 commands (cmd1 | cmd2).
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include <unistd.h>
void wait_for_background_process(void* arg) {
int status;
pid_t* pid = (pid_t*) arg;
do {
printf("Waiting for %d\n", *pid);
waitpid(*pid, &status, WUNTRACED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
/************************
function: void pipeCommand(char** cmd1, char** cmd2)
comment: This pipes the output of cmd1 into cmd2.
**************************/
void pipe_command(char** cmd1, char** cmd2) {
int fds[2]; // file descriptors
if(pipe(fds) < 0) {
perror("myShell");
exit(EXIT_FAILURE);
}
pid_t pid;
pid = fork();
if(pid == 0) {
dup2(fds[1], 1);
if(execvp(cmd1[0], cmd1) < 0) {
perror("myShell");
exit(EXIT_FAILURE);
}
execvp(cmd1[0], cmd1);
} else if(pid < 0) {
perror("myShell");
exit(EXIT_FAILURE);
} else {
wait(NULL);
dup2(fds[0], 0);
if(execvp(cmd2[0], cmd2)) {
perror("myShell");
exit(EXIT_FAILURE);
}
}
}
/*
* Checks if the command is pipe command, if so we will return the
* index of the pipe
*/
int is_pipe_command(char** arglist, int count) {
int i = 0;
for(i = 0; i < count; i++) {
if(strcmp(arglist[i], "|") == 0) {
return i;
}
}
return 0;
}
int process_arglist(int count, char** arglist) {
pid_t pid;
int pipe_index;
pid = fork();
if (pid == 0) {
// Child process
if(strcmp(arglist[count-1],"&") == 0) {
char** background_arglist = (char**) malloc((count)*sizeof(char*));
if(background_arglist == NULL) {
printf("malloc failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
int i = 0;
for(i = 0; i < count - 1; i++) {
background_arglist[i] = arglist[i];
}
background_arglist[count - 1] = NULL;
if (execvp(background_arglist[0], background_arglist) == -1) {
perror("myShell");
}
} else if(pipe_index = is_pipe_command(arglist, count)) {
char** cmd1 = (char**) malloc((pipe_index+1)*sizeof(char*));
if(cmd1 == NULL) {
printf("malloc failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
int i;
int cmd1index = 0;
for(i = 0; i < pipe_index; i++) {
cmd1[cmd1index] = arglist[i];
cmd1index++;
}
cmd1[pipe_index] = NULL;
char** cmd2 = (char**) malloc((count - pipe_index)*sizeof(char*));
if(cmd2 == NULL) {
printf("malloc failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
int cmd2index = 0;
for(i = pipe_index+1; i < count; i++) {
cmd2[cmd2index] = arglist[i];
cmd2index++;
}
cmd2[count-pipe_index-1] = NULL;
pipe_command(cmd1, cmd2);
} else {
if (execvp(arglist[0], arglist) == -1) {
perror("myShell");
}
}
exit(EXIT_FAILURE);
} else if (pid < 0) {
// Error forking
perror("myShell");
exit(EXIT_FAILURE);
} else {
// Parent process
if(strcmp(arglist[count-1],"&") == 0) {
// The child is a background process
pthread_t thread;
pthread_create(&thread, NULL, wait_for_background_process, &pid);
}
else {
// Regular process
}
}
return 1;
}
We can focus on the pipe_command function, which gets the 2 commands correctly, I can't understand why I'm not getting any output for calling for example ls -l | sort
or ls -l | grep "a"
.
Thanks.
Upvotes: 4
Views: 441
Reputation: 14046
After dup
you need to close fds[1]
. Otherwise the second command process (sort
or grep
in your example) will not get an EOF
from its stdin
read. The EOF
is needed so that the process knows when to stop processing and exit.
Explicitly, the else
block in pipe_command
should have a close
in it like thus:
} else {
wait(NULL);
dup2(fds[0], 0);
close(fds[1]); /* ADDED THIS LINE */
if(execvp(cmd2[0], cmd2)) {
perror("myShell");
exit(EXIT_FAILURE);
}
}
One more thing to note. Normally the first command process also needs a similar close(fds[0]);
. But it is not needed in your case as the second process waits for the first process to exit before calling execvp
which results in the first process implicitly closing all its open file descriptors.
Upvotes: 2