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?
Related
I'm trying to create a simple shell interface program that takes shell commands as user input from the parent process and send the simple shell command to the child process via a pip IPC which actually executes the shell command. The child while loop keeps repeating even after typing "quit"
int main() {
char userInput[BUFFER_SIZE];
int fpipe[2];
pid_t pid;
// INTRODUCTION TO PROGRAM
printf("------- WELCOME TO THE FORKED PARENT -------\n");
printf("------- YOU MAY QUIT AT ANY TIME BY TYPING 'quit' or 'q' -------\n");
if (pipe(fpipe)== -1){
printf("------- PIPE HAS FAILED -------\n");
return 1;
}
pid = fork();
if (pid < 0){
printf("------- FORK HAS FAILED -------\n");
return 1;
}
if (pid > 0){
close(fpipe[0]);
printf("\nosh> ");
scanf("%s", userInput);
while ((strcmp(userInput,"quit") != 0 && strcmp(userInput,"q") != 0)){
printf("\nosh> ");
write(fpipe[1], userInput, strlen(userInput)+1);
scanf("%s", userInput);
}
close(fpipe[1]);
}
else{
close(fpipe[1]);
while(1){
if (read(fpipe[0], userInput, BUFFER_SIZE) == -1){
return 1;
}
if ((strcmp(userInput,"quit") != 0 && strcmp(userInput,"q") != 0)){
system(userInput);
printf("osh> ");
}
else{
break;
}
}
close(fpipe[0]);
}
return 0;
}
The problem is that the loop in the parent stops when the user enters quit, it doesn't send it to the child. So the child's condition for stopping the loop is never matched. It keeps reading from the pipe, which returns nothing because it's at EOF, so it keeps executing the last command that was sent.
The simplest solution is for the child to break out of the loop when it gets EOF from the pipe, rather than looking for quit.
while(1){
int n;
n = read(fpipe[0], userInput, BUFFER_SIZE);
if (n == -1) {
return 1;
}
if (n == 0) {
break;
}
system(userInput);
}
I have written a code which will call exec as shown below. But if I call isValid from main, both child and parent process are returning and I am getting the output twice.
I want to get the out of the exec and want to check the return value in main only once. which process I need to exit inorder to make this work properly?
int isValid(void)
{
int number, statval;
int child_pid;
child_pid = fork();
if(child_pid == -1) { printf("Could not fork! \n"); exit( 1 ); }
else if(child_pid == 0)
{
execl(...); // Child
}
else
{
// Parent
waitpid( child_pid, &statval, WUNTRACED );
if(WIFEXITED(statval))
{
if (WEXITSTATUS(statval) == 0)
return 1;
else
return 0;
}
else
printf("Child did not terminate with exit\n");
}
return 0;
}
int main(void)
{
if (isValid())
{
printf("Valid\n");
}
else
{
printf("Invalid\n");
}
}
The doubled output may occur when the execl fails. The code above does not check the execl return value. Actually the execl returns only in case of failure so there is no need to check the returned value but it is necessary to handle error anyway.
EDIT:
If the exec fails then:
Child's isValid() returns 0 at the end of the function and the main prints "Invalid"
Parent waits for the child exit and then WIFEXITED is true because child exits and WEXITSTATUS is 0 because the child exits normally. Parent's isValid returns 1 and "Valid" is printed.
#include <sys/shm.h>
int *tabPID;
int isValid(void)
{
int number, statval;
if(fork() == 0){
tabPID[1] = getpid();
execl(...); // Child
return -1;
}
// Parent
tabPID[0]=getpid();
usleep(10);//as Basile Starynkevitch suggests
waitpid(tabPID[1], &statval, WUNTRACED );
if(WIFEXITED(statval))
{
if (WEXITSTATUS(statval) == 0)
return 1;
else
return 0;
}
else
printf("Child did not terminate with exit\n");
return 0;
}
int main(void)
{
shmId = shmget(1234, 2*sizeof(int), IPC_CREAT|0666);
tabPID = shmat(shmId, NULL, 0);
if (isValid())
{
printf("Valid\n");
}
else
{
printf("Invalid\n");
}
}
You probably should do some tiny things (perhaps some usleep(3) for a few milliseconds) after the fork but before the waitpid to have some real chance to get the waitpid(2) call succeed. Otherwise, it could happen that the other process won't be scheduled to run.
BTW, you should test the return value from waitpid, .e.g. code
statval = 0;
pid_t wpid = waitpid(child_pid, &statval, WUNTRACED );
if (wpid>0) {
assert (wpid == child_pid);
if (WIFEXITED(statval)) {
if (WEXITSTATUS(statval) == 0)
return 1;
At last, regarding the title of your question, read job control wikipage, read several chapters of Advanced Linux Programming, and consider using daemon(3)
Also, just after the execl be sure to put
perror("execl");
exit(EXIT_FAILURE);
to handle the rare case of execl failure.
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);
}
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
Learning to use the fork() command and how to pipe data between a parent and it's children. I am currently trying to write a simple program to test how the fork and pipe functions work. My problem seems to be the correct use/placement of the wait function. I want the parent to wait for both of its children to finish processing. Here is the code I have so far:
int main(void)
{
int n, fd1[2], fd2[2];
pid_t pid;
char line[100];
if (pipe(fd1) < 0 || pipe(fd2) < 0)
{
printf("Pipe error\n");
return 1;
}
// create the first child
pid = fork();
if (pid < 0)
printf("Fork Error\n");
else if (pid == 0) // child segment
{
close(fd1[1]); // close write end
read(fd1[0], line, 17); // read from pipe
printf("Child reads the message: %s", line);
return 0;
}
else // parent segment
{
close(fd1[0]); // close read end
write(fd1[1], "\nHello 1st World\n", 17); // write to pipe
// fork a second child
pid = fork();
if (pid < 0 )
printf("Fork Error\n");
else if (pid == 0) // child gets return value 0 and executes this block
// this code is processed by the child process only
{
close(fd2[1]); // close write end
read(fd2[0], line, 17); // read from pipe
printf("\nChild reads the message: %s", line);
}
else
{
close(fd2[0]); // close read end
write(fd2[1], "\nHello 2nd World\n", 17); // write to pipe
if (wait(0) != pid)
printf("Wait error\n");
}
if (wait(0) != pid)
printf("Wait error\n");
}
// code executed by both parent and child
return 0;
} // end main
Currently my output looks something along the lines of:
./fork2
Child reads the message: Hello 1st World
Wait error
Child reads the message: Hello 2nd World
Wait error
Where is the appropriate place to make the parent wait?
Thanks,
Tomek
That seems mostly ok (I didn't run it, mind you). Your logic error is in assuming that the children will end in some particular order; don't check the results of wait(0) against a particular pid unless you're sure you know which one you're going to get back!
Edit:
I ran your program; you do have at least one bug, your second child process calls wait(), which you probably didn't want to do. I recommend breaking some of your code out into functions, so you can more clearly see the order of operations you're performing without all the clutter.
i think its better to use something like this, in order to wait for all the childrens.
int stat;
while (wait(&stat) > 0)
{}