So I have been trying to implement piping in my own shell program so that I can actually understand what UNIX is doing. I'm very very close at the moment, but for some reason my program is going into an infinite loop when I pipe. I'm pretty sure my problem is stemming from my waitpid arguments not ever letting the last pipe close, because if I make the final loop in the code below be i is less than count-1 instead of i is less than count it will run the command on the left of the pipe. But once I put it back to i is less than count it just loops forever.
if(pipes)
{
for (i=0;i<count-1;i++)
{
if( pipe(fd) ==-1)
{
perror("Pipe failure");
return;
}
read[i+1] = fd[0];
write[i] = fd[1];
}
}
for(i=0;i<count;i++)
{
pid = fork();
if(pid < 0)
{printf("fork failed");}
else if(pid>0)
{pids[i] = pid;}
else
{
if(write[i] != -1)
{
if(dup2(write[i],STDOUT_FILENO) ==-1)
{
perror("dup2 failure");
exit(1);
}
}
if(read[i] !=-1)
{
if (dup2(read[i], STDIN_FILENO)==-1)
{
perror("dup2 failure");
exit(1);
}
}
for (j=0; j<count; j++)
{
close(write[j]);
close(read[j]);
}
execvp(input[i], input);
exit(1);
}//end else
}//end for
for(i=0; i < count; i++){
if(write[i] != -1)
close(write[i]);
if( read[i] != -1)
close (read [i]);
waitpid(pids[i], &status,0);
}
}
return (status);}
I think I'm really really close to the solution but for the time being I'm stuck. I've researched piping a ton, but I guess I'm just not quite getting it. Thanks for any help.
Please format your code. It is unreadable right now.
That being said, a few errors:
You are not setting write[0]. You are setting write[count-1] and read[count].
You are waiting for pids[i] while the parent still has read[i] open. If input[i] wants will not exit until it gets EOF on stdin, it will never happen.
Related
So I'm trying to read a line with multiple commands and creating a child process for each command to handle it. I'm having some trouble once I try to uncomment the if(fd2) statement for stdout redirection. Instead of the desired output appearing once, it appears multiple times. I'm not exactly sure what is causing this, I'm closing all of the file descriptors and having the child exit.
The commands I'm dealing with may have both stdout and stdin redirection, and I'm successful in setting up the redirection for stdin, but I cant seem to find my bug. I don't get a perror() warning on display. It's not so much as a problem with dup2(), but rather with open(), if I just uncomment that part I get the error.
int concCmd(Cmd cmdM[], int iCmdCnt, Token tokenM[], int iTokenCnt){
long pid, wpid;
int i, status=0, fd = 0, fd2=0;
fflush(0);
for(i=0; i<iCmdCnt; i++){ //create a process for every command
pid=fork();
switch(pid){
case -1:
perror("Fork\n");
return 1;
case 0:
if(cmdM[i].iStdinRedirectIdx != 0){ //does command need stdin redirection
if((fd = open(tokenM[cmdM[i].iStdinRedirectIdx], O_RDONLY)) <0){
perror("open stdin");
return 1;
}
if((dup2(fd,STDIN_FILENO))<0){
perror("dup2");
return 1;
}
close(fd); //close in parent process
}
if(cmdM[i].iStdoutRedirectIdx != 0){ //command needs stdout redirection
// if((fd2 = open(tokenM[cmdM[i].iStdoutRedirectIdx], O_WRONLY|O_CREAT|O_EXCL)) < 0){
// perror("open stdout");
// return 1;
// }
// if((dup2(fd, STDOUT_FILENO))<0){
// perror("dup2");
// return 1;
// }
close(fd2);
}
//EXECVP HERE!!!
//execvp (cmdM[i].szCmdNm, tokenM[
exit(0);
default:
wpid = wait(&status);
close(fd);
close(fd2);
fprintf(stderr, "%ld %ld\n", (long)getpid(), wpid);
}
}
return 0;
}
I went ahead an added a fflush(0) before I started forking, to clear out the buffer, as suggested in another question, which should reasonably clear the buffer for when I call open for stdout, but I still get the same errors.
I was going to make a comment, because what I want to say is very simple, but I need to reference some code, so maybe posting an answer is easier.
// if((fd2 = open(tokenM[cmdM[i].iStdoutRedirectIdx], O_WRONLY|O_CREAT|O_EXCL)) < 0){
// perror("open stdout");
// return 1;
// }
// if((dup2(fd, STDOUT_FILENO))<0){
// perror("dup2");
// return 1;
// }
In your code, the part about fd2 which you comment in your question has a typo.
dup2(fd, STDOUT_FILENO) should be dup2(fd2, STDOUT_FILENO)
I don't know whether it is really a typo in your source code, or you just made a mistake when you posting your question.
Trying to build a linux shell everything works great except this one error message at the top!
i set LINE=81, and it prints out the error message total length/LINE times. im having trouble finding a way to fix that. I think it's because it keeps looping and since itll always have that condition itll print. and I dont want it to terminate, just refresh and keep inputting, any ideas? I am required ot provide an error message.
edit:when adding the '\n' it still functions the same.
char commands[LINE];
while (fgets(commands, LINE, stdin)!= NULL) {
if (strlen(commands) >= LINE - 1) {
printf("Too Many arguments please enter %d characters\n",LINE-2);
continue;
}
childPID = fork();
if (childPID < 0) {
perror("fork");
exit(-1);
}
if (childPID == 0) {
parse(commands);
exit(0);
} else {
if (waitpid(childPID, &status, 0) !=childPID) {
perror("waitpid");
} else
if (!WIFEXITED(status)) {
printf("Exited Abnormally\n");
}
putc('\n', stdout);
fputs(PROMPT,stdout);
}
commands[0] = '\0';
}
exit(0);
}
You should put \n at the end of the error message:
printf("Too Many arguments please enter %d characters\n",LINE-2);
stdout is normally line-buffered, so the buffer isn't flushed until you print a newline. When your program forks, the buffer is copied into the child. When the child calls exit(), it flushes its copy of the buffer. And when the parent calls putc('\n') it flushes its copy. As a result, the message is printed twice.
so turned out I needed to iterate through and then print and move my prompt accordingly
while (fgets(commands, LINE, stdin)!= NULL) {
if (strlen(commands) > LINE-2) {
fprintf(stderr,"Too many chars\n");
while (getchar() != '\n' && !feof(stdin));
}
else
{
childPID = fork();
if (childPID < 0) {
perror("fork");
exit(-1);
}
if (childPID == 0) {
parse(commands);
exit(0);
} else {
if (waitpid(childPID, &status, 0) !=childPID) {
perror("waitpid is not responding");
} else
if (!WIFEXITED(status)) {
fprintf(stderr,"Unusually exit\n");
}
//putc('\n', stdout);
//fputs(PROMPT,stdout);
}
}
putc('\n', stdout);
fputs(PROMPT,stdout);
}
exit(0);
}
I am trying to implement multiple pipes in C. This is the main part of the function that takes care of piping
ProcesscommandwithPipes()
{
............................
for (k=0; k <= num_of_pipes; k++)
{
read[k]= -1;
write[k] = -1;
}
//create required number of pipes
for(j=0; j < num_of_pipes; j++)
{
if( pipe(fd) == -1 )
{
perror("Pipe failure");
return;
}
read[j+1] = fd[0];
write[j] = fd[1];
}
for(k=0; k<= num_of_pipes; k++)
{
pid = fork();
if(pid < 0 )
{
printf("fork failed\n");
}
else if (pid == 0)
{
if(write[k] != -1)
{
if( dup2(write[k],1) == -1){
perror("dup2 error");
exit(1);}
}
if(read[k] != -1)
{
if( dup2(read[k],0) == -1)
{
perror("dup2read error");
exit(1);
}
}
for (h=0; h<= num_of_pipes;h++)
{
close(write[h]);
close(read[h]);
}
if(execvp((const char*)commandArgv[k][0], commandArgv[k]) < 1)
{
perror("error");
exit(1);
}
exit(0);
}
else
{
processid[k] = pid;
printf("waiting on process:%d\n", processid[k]);
close(write[k]);
close(read[k]);
waitpid(processid[k], &status, 0);
}
}
For some reason, the following command works fine
ls|grep tmp|sort
But the following command doesn't work, although it is pretty much the same
cat tmp1.out|grep tmp|sort
(tmp1.out contains the list of the files in the cur dir, same as the output of ls)
There is no error message too.But it just exits without printing anything on screen(though the stdout of the last command is not changed)
P.S: cat tmp1.out|grep tmp works fine too.
contents of tmp1.out:
a.out
sample
shell.c
tmp1.out
tmp.out
b.c
Any inputs?
I know you are trying to write a shell, but have you considered using popen?
FILE *p = popen("cat tmp1.out|grep tmp|sort", "r");
The problem might be that you call waitpid() within your loop and thus you won't start the second process before the first one terminates. But now the first one could hang if the pipe buffer size is reached and you have a deadlock or a process might be killed by a broken pipe. Besides, for(k=0; k<= num_of_pipes; k++) looks strange to me, because that loops num_of_pipes+1 times
So the code is this one:
int main ()
{
int pid, fd[2], i, j;
char comanda[1000], comm[100][100], *var, *var2, vect[100][100], text[1000], check[10000];
if(pipe(fd)<0)
{
perror("pipe error");
exit(1);
}
if((pid = fork()) < 0 )
{
perror("fork error");
exit(1);
}
j = 0;
if(pid){
do {
if( j > 0) fgets (check , 1000 , stdin); //just in case there's still something in the buffer
printf("enter command: \n");
scanf("%[^\t\n]", comanda);
if(var = strtok(comanda, " "))
{
i=0;
while(var != NULL)
{
strcpy(vect[i], var);
var = strtok(NULL, " ");
i++;
}
}
else
strcpy(vect[0], comanda);
if(strcmp(vect[0], "login") == 0)
{
write(fd[1], "login ", 6);
write(fd[1], vect[1], strlen(vect[1]));
printf("I got login");
}
else if(strcmp(vect[0], "quit") == 0)
{
exit(1);
}
else
printf("I got the command %s \n", vect[0]);
j++;
} while(1);
close(fd[0]);
close(fd[1]);
wait(NULL);
}
else
{
close(fd[1]);
printf("copil? \n");
int i=0;
read(fd[0], text, sizeof(text));
var2 = strtok(text, " ");
j=0;
while(var2 != NULL)
{
strcpy(comm[j], var2);
var2 = strtok(NULL, " ");
j++;
}
if( strcmp(comm[0], "login") == 0)
{
//comanda e login, deci verificam username-ul.
if(login(comm[1]))
{
printf("OK, Logged IN! \n");
}
else
{
printf("Username not in /etc/passwd \n");
}
}
close(fd[0]);
exit(0);
}
return 0;
}
What I want is to read commands from the console line and everytime I get a known command, to go to the child and execute it. Right now it reads the commands fine, the login works fine BUT only ONE time. After that, it still gets the login, it prints "I got login", but it doesn't go to the child and check it if it's ok or not.
You have three major problems that I can see:
The first is that you don't terminate the string you read from the pipe in the child;
The other, and the source of your problem, is that the child does not loop, but performs once and then exits;
The third would be if you change the child to loop, and you exit the parent process then the child process would be abandoned, and just keep on waiting for input that never comes.
This is because your child process just exits. It seems that first "read(fd[0], text, sizeof(text));" blocks until it will receive some data from parent process. Then it executes data and exits.
BTW it is good idea to call waitpid function to avoid zombie process which could be some problem with your application. What is more you should "close(fd[0]);" at the beginning of parent process not at the and with "close(fd[1]);"
I had the same problem. My idea was to control another process through a pipe like it would get input from the keyboard/stdin. It was the classical need of inter process communication with application that are not designed for it.
The problem was, that all the example codes just worked once at the beginning to send something to the child process stdin. It appeared to me that the pipe has to be closed on parents side so that the child side receive the data. Once closed I couldn't "reopen" it to send another command.
What saved my day was this:
http://www.rkoucha.fr/tech_corner/pty_pdip.html
http://en.wikipedia.org/wiki/Pseudo_terminal
I do not know why it took me almost two days to figure it out, because it sound really obvious thing to to after all. I'm posting this here, because I landed on this side and the answers wasn't really solving my problem.
Sincerely,
Robert
I'm writing a shell which forks, with the parent reading the input and the child process parsing and executing it with execvp.
pseudocode of main method:
do{
pid = fork();
print pid;
if (p<0) { error; exit; }
if (p>0) { wait for child to finish; read input; }
else { call function to parse input; exit; }
}while condition
return;
what happens is that i never seem to enter the child process (pid printed is always positive, i never enter the else). however, if i don't call the parse function and just have else exit, i do correctly enter parent and child alternatingly.
full code:
int main(int argc, char *argv[]){
char input[500];
pid_t p;
int firstrun = 1;
do{
p = fork();
printf("PID: %d", p);
if (p < 0) {printf("Error forking"); exit(-1);}
if (p > 0){
wait(NULL);
firstrun = 0;
printf("\n> ");
bzero(input, 500);
fflush(stdout);
read(0, input, 499);
input[strlen(input)-1] = '\0';
}
else exit(0);
else { if (parse(input) != 0 && firstrun != 1) { printf("Error parsing"); exit(-1); } exit(0); }
}while(strcmp(input, "exit") != 0);
return 0;
}
EDIT:
-that else exit(0) just something i forgot there from playing around
-adding a newlines to the prints shows that it does in fact correctly fork and enter the child process; thank you, the problem seems to be in the parse
One culprit is else exit(0);
That would execute in the child shell, which means it never gets to the parsing stage. The code is also syntactically invalid because after that you have another else.
`if (p >= 0) {
if (p == 0) {/* chile process */}
else if (p > 0) {/* parent process */}
} else {
/* handle the error returned by fork() */
}`
I'd do it like the above pseudo code.
else exit(0); is what the child process is doing in your code.
Your core is a tad messy with all the nested if's and else's. There are some dangling else statements as well (else exit(0);). I'd start by cleaning those up. I can't see any other logical problems with your code. It's simple enough.
Swap the lines
else exit(0);
and
else { if (parse(input) != 0 && firstrun != 1) { printf("Error parsing"); exit(-1); } exit(0); }
Apart from everything everybody else has said about the fact that the else's are a complete mess, there are some other issues you will hit when you have fixed them.
In the child, the input array will be garbage on the first run because you don't put anything in it before forking.
It seems completely pointless to fork at all since you are not exec'ing anything in the child but you are waiting for the child to finish in the parent. Why not just call parse from the parent?