How to sync processes inside a while loop in c - c

I am experiencing quite a dilemma:
I have the following program structure:
char* input = (char *)malloc(sizeof(char));
input = "CONTINUE";
while(strcmp(input, "EXIT") != 0 )
{
printf("%s", "prompt: ");
scanf("%s", input);
if( strcmp(input, "test") == 0 )
{
pid_t childPid;
switch(childPid = fork())
{
case -1:
printf("%s", "Error");
break;
case 0:
foo(input);
break;
}
}
else if(/.../)
{
}
else
{
input = "EXIT";
}
}
void foo(char* input)
{
printf("%s %s", "printing" input);
}
So the problem is that when the loop executes it prints "prompt: " the first time,
then it prints "prompt: " again, and finally the child process from the if statement prints its output.
It's like the child processes are out of sync or something and i can't figure out what is causing that extra "prompt: " to be printed the second time. This is a sample output:
prompt: test //typed "test"
prompt: //Where is this comming from?
printing test
prompt: //new iteration of loop expecting input

fork() causes a child process to be created as an exact copy of the parent, but also allows the parent to continue executing.
Your code does nothing in the parent when it successfully spawns a child (fork returns a number greater than 0, indicating the child's process ID; that return is not handled by your switch). Thus, the parent returns to the beginning of the while loop and prints "prompt" again.
The child calls foo (fork returns 0), but after that your code simply does a break from the switch statement. After that point, the child follows the exact same code path as the parent, so it also prints "prompt" and then does a scanf.
What you want to do is probably something more along these lines:
childPid = fork();
if (childPid < 0)
{
perror("fork()");
}
else if (childPid == 0)
{
foo(input);
exit(0); // terminate the child process
}
else
{
waitpid(childPid, NULL, 0); // suspend the parent until the child exits
}
Since you're printfing from both processes, you probably want to suspend the parent until the child has completed (using waitpid). Otherwise the "prompt" will likely get printed before the output from foo(). On some platforms the prints can happen at the same time resulting in intermixed characters or completely garbled terminal output.

Related

Continuous writing and reading using pipes with multiple processes

My code consists of two processes. The parent process continuously reads a single char from stdin and write to the pipe (without the need to press ENTER). The child process reads from the pipe and writes to stdout. My parent process successfully writes to the pipe, but child process isn't printing the output.
The reason the child process isn't printing out the output is because it's stuck in the while loop of the parent process and never enters the child process's while loop.
When I force quit the parent process using the Activity Monitor on my mac, what I typed in actually gets printed out. Followed by "Killed:9"
Is there a way to fix my code so each time the Parent(Input)receives a character, the Child(Output) prints each char out without getting stick in the while loop of the parent process?
char input() {
char input = getchar();
return input;
}
int main(void) {
int inputOutputFd[2];
pid_t childpid = 0;
system("/bin/stty raw igncr -echo");
if(pipe(inputOutputFd) < 0) {
perror("Failed to create pipe");
return 1;
}
if((childpid = fork()) == -1) {
perror("Failed to fork input child");
return 1;
}
//parent's code -INPUT
if (childpid > 0) {
close(inputOutputFd[0]);
printf("Please enter a word or phrase");
while(1) {
char inputChar = input();
write(inputOutputFd[1], &inputChar, sizeof(inputChar));
}
close(inputOutputFd[1]);
wait(NULL);
} else {
//child -OUTPUT
char outputChar;
close(inputOutputFd[1]);
while (read(inputOutputFd[0], &outputChar, sizeof(outputChar)) > 0)
{
printf("%c", outputChar);
fflush(stdin);
}
} //END OF IF-ELSE LOOP
}//END MAIN
Everything works fine, there is nothing stuck or anything, until you're expecting output in your console. The bug is in those two lines:
printf("%c", outputChar);
fflush(stdin);
stdin is standard input. You are writing to standard output.
printf("%c", outputChar);
fflush(stdout);
works for me.

Trouble creating a C program using fork() and execvp() functions

Here is the following code I am current having issues with:
#include <stdio.h>
#include <unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#define MAX_LINE 80
int main(void)
{
char *args[MAX_LINE/2+1];
int background= 0;//integer that acts a boolean for if & appears
int should_run = 1;
int status;
while(should_run)//Just to keep the interface going until the user chooses to stop
{
printf("osh>");//prompt
fflush(stdout);
int i = 0;
while(getchar() != '\n')//Use scanf until a new line is found
{
scanf("%s", &args[i]);
if(strcmp(&args[i], "exit") == 0)//temporary exit protocal
{
printf("Exiting now...");
return 0;
}
if(strcmp(&args[i], "&") == 0)//If we find a & in our input then we changed background to 1 or true
background = 1;
printf("Args[%i] = %s\n", i, &args[i]);//Tester
i++;
}
printf("Background = %i\n",background);//Test
pid_t pid= fork();// Create new child process
printf("process %i created\n", pid);//Test
if(pid < 0)//fork() failed
{
printf("Fork Failed.\n");
return 1;
}
else if(pid == 0)//Child process id
{
printf("Child process started %s command\n", &args[0]);
if(execvp(args[0], args) < 0)//change the current child process to execute the input given by the user
//with args[0] being the command and args being the parameters(ls -l is an example).
{
printf("Command failed\n");
}
return 0;
}
else//Parent Process
{
if(background == 1)//If the user typed in a & then the parent will wait for a change in state from the child, if there is no &
//then we will just finish the parent process
{
printf("Parent process waiting on child\n");
wait(NULL);
}
}
}
return 0;
I have one major issue and one minor issue right now. The major issue is that I have a printf method before execvp starts that says "Child Process started" and I get this line to print, but then nothing else happens. No interrupts are thrown, the program just seems to be frozen on my execvp command.
My minor issue is that when my program starts a prompt "osh>" before asking for input. Now if, for example, I would type in "osh>ls -l" then I get args[0] = s, args1 = -l. Now if I put "osh> ls -l" in that exact format I get args[0] = ls, args1 = -l. Is that a part of scanf() that I am not using properly here to make sure I get ever character after "osh>" and between blank spaces as strings?
EDIT:
here is my output for user input "ls -l"
The problem you're having with the missing character is because getchar() is consuming the first character of your input before scanf gets to take a stab at it. You probably want to do something like:
while (scanf("%s", &buffer) > 0)
{
strcpy(args[i], buffer);
/* then do stuff with args[i] */
}

What am I doing wrong to end this while loop

I'm using "int flag = [0 or 1]" to determine if my loop should go back to the do{}.
Heres my code
void shell()
{
int flag = 1;
do
{
// Retrieve PID of the parent process.
pid_t shell_pid = getpid();
// Print shell prompt.
printf("{%i}$ ", shell_pid);
// Retrieve input from stdin.
char* input = NULL;
size_t input_size = 0;
getline(&input, &input_size, stdin);
char delim = ' ';
char **argvp;
int argc = makeargv(input, &delim, &argvp);
// Remove \n from last arg
char* lastarg = argvp[argc - 1];
while(*lastarg && *lastarg != '\n')
lastarg++;
*lastarg = '\0';
// Create a child process to handle user input.
pid_t pid = fork();
// Parent process.
if (pid != 0)
{
wait(NULL);
}
else // Child process
{
int i;
// Handle user input here!
//got some debugging tools here
printf("========DEBUG==========\n");
printf("There are %i args\n", argc);
for(i = 0; i < argc; i++)
{
printf("arg %i: \"%s\"\n", i, *(argvp + i));
}
printf("=====END DEBUG==========\n\n");
printf("********OUTPUT*********\n");
//handle "echo" here
if((i = strcmp(argvp[0],"echo")) == 0)
{
echo(argc, argvp);
}
//handle "exit" or "quit" here
if((i = strcmp(argvp[0],"exit")) == 0 || (i = strcmp(argvp[0],"quit")) == 0)
{
printf("got in the exit\n");
flag = 0;
}
}
printf("****END OUTPUT*********\n");
// Free memory allocated by getline().
free(input);
}while(flag == 1);
}
and my input/output looks like this: [input is preceded by $]
$ ./shelltest
{13158}$ try first string
========DEBUG==========
There are 3 args
arg 0: "try"
arg 1: "first"
arg 2: "string"
=====END DEBUG==========
********OUTPUT*********
****END OUTPUT*********
{13159}$ echo try another
========DEBUG==========
There are 3 args
arg 0: "echo"
arg 1: "try"
arg 2: "another"
=====END DEBUG==========
********OUTPUT*********
try another
****END OUTPUT*********
{13160}$ quit
========DEBUG==========
There are 1 args
arg 0: "quit"
=====END DEBUG==========
********OUTPUT*********
got in the exit
****END OUTPUT*********
****END OUTPUT*********
{13160}$
Since "got in the exit" was shown in the output, then flag was immediately set to 0.
how is it that the loop continues?
You are using fork(), the parent process is waiting for the child process to exit, the line:
printf("****END OUTPUT*********\n");
is executed by both the parent and child process, thus you saw it twice.
Your child process is exiting the loop but your parent continues to run.
The normal paradigm for shells is for the parent to read and evaluate the input and only fork if a child process is needed.
Using that method, the parent would detect exit and finish without creating a child.
When you print
{13160}$ quit
In reality, 13160 is the pid of the parent of the process that will be created by fork to handle the quit command(To see this in action, print the pid of the child after fork). This one will parse quit and exit by setting flag to 0. You will then see ****END OUTPUT********* twice.
You have to remember that when you fork, memory is not shared with the parent(in Linux it is but it is copy on write. It was last time I read on it) so when child writes flag = 0, parent will not see this value so it will loop again. Hence why it asks for input again.
One other thing you process relationship looks like {13158}->{13159}->{13160}->... Since root process is 13158, I think what you wanted to do is spawn a single children from root after every input. So you might want to check that.

Issue with pipes and fork, it only goes to the child process once instead of everytime I want

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

problem with fork()

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?

Resources