What am I doing wrong to end this while loop - c

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.

Related

Child Process not exiting in Piping [duplicate]

This question already has an answer here:
Strange behavior with Child process
(1 answer)
Closed 1 year ago.
I'm currently testing a program that executes the Linux command echo Hello | wc using piping.
The parent process in main() spawns two child processes, the first one which executes echo Hello, and the second one which executes wc. These two processes communicate via a pipe.
However, when I call waitpid() on the two child processes, only the second process exits. The first process successfully runs execvp(), but hangs there.
Here is the supposed output of the code:
command 0
command 0 should exit
command 1
1 1 6
If I uncommment the line waitpid(id[0],&status,0);
then the output is
command 0
command 0 should exit
command 1
int test(pid_t id[])
{
int i;
int pipefd[2];
char *cat_args[] = {"echo","Hello", NULL};
char *grep_args[] = {"wc", NULL};
// make a pipe
pipe(pipefd);
for(i = 0; i < 2; i++){
id[i] = fork();
if (id[i] == -1){
printf("Unable to create child process");
fprintf(stderr,"fork() failed to spawn child process");
exit(1);
}
else if (id[i] == 0){
printf("command ");
printf("%d\n",i);
if (i == 0){
dup2(pipefd[0],STDIN_FILENO);
}
else if (i == 1){
dup2(pipefd[1],STDOUT_FILENO);
}
// Close pipes
close(pipefd[0]);
close(pipefd[1]);
if (i == 1){
execvp(*cat_args,cat_args); //exit(0) normally
}
else if (i == 0){
printf("command 0 should exit\n");
execvp(*grep_args,grep_args);
printf("command 0 exited\n"); //If first child exits, should print it out
}
}
}
return 0;
}
Here is the main function:
int main(int argc, char **argv)
{
pid_t id[2];
test(id);
int status;
// If this is uncommented, the piped Linux command is never ran
// and main() never exits
//waitpid(id[0],&status,0);
waitpid(id[1],&status,0); // This works
return 0;
}
Thank you.
The sub-process running wc hangs because the pipe is still readable despite the sub-process running echo Hello died. wc still waits for something to read through the pipe.
Why is the pipe still readable? Because you didn't close it in the parent process, the one that does the two forks. Therefore, the parent can still read and write to the pipe and the wc sub-process stays blocked on read() and never receives an EOF.
BTW, your printf("command 0 exited\n") is useless since nothing is executed after a successful execvp().

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] */
}

Interprocess Communication in C, one character at a time

First off, this IS homework, I am not asking for an answer, however I am confused about something.
I have a homework assignment for a programming class, and I am a little confused about how to write the code the specific way that the instructor is asking.
The program first creates a child process, and then proceeds to send command line arguments from the parent process, through a pipe, ONE CHARACTER at a time to the child process, and then read them into the child process ONE CHARACTER at a time, incrementing the character count in the child process each time a character is read in.
I think I accomplished sending the data through the pipe one character at a time, but I have no idea how to "go" to the child process every time a character is sent, read it, increment the number of characters, and then go back to the parent process and repeat.
Here is my code, It works and gives accurate answers, but any tips on how to accomplish what my instructor is asking would be appreciated, thank you!!
// Characters from command line arguments are sent to child process
// from parent process one at a time through pipe.
//
// Child process counts number of characters sent through pipe.
//
// Child process returns number of characters counted to parent process.
//
// Parent process prints number of characters counted by child process.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char **argv)
{
// set up pipe
int pA[2];
char buff[50];
pipe(pA);
// call fork()
pid_t childId = fork();
if (childId == 0) {
// -- running in child process --
int nChars = 0;
// close the output side of pipe
close(pA[1]);
// Receive characters from parent process via pipe
// one at a time, and count them.
nChars = read(pA[0], buff, sizeof(buff)); //this line of code is what i need to change to be reading characters in one at a time
// Return number of characters counted to parent process.
return nChars;
}
else {
// -- running in parent process --
int nChars = 0;
int size = 0;
printf("CS201 - Assignment 3 - Timothy Jensen\n");
// close the input side of the pipe
close(pA[0]);
// Send characters from command line arguments starting with
// argv[1] one at a time through pipe to child process.
for (int i = 1; i < argc; i++)
{
size = strlen(argv[i]);
for (int z = 0; z < size; z++)
{
write(pA[1], &argv[i][z], 1);
}
}
// Wait for child process to return. Reap child process.
// Receive number of characters counted via the value
// returned when the child process is reaped.
wait(&nChars);
printf("child counted %d chars\n", nChars/256);
return 0;
}
}
You need to make the following changes:
Make the last argument 1 in the call to read.
read(pA[0], buff, 1);
Put the above call in a while loop and increment nChar for every successful attempt at read.
while ( read(pA[0], buff, 1) == 1 )
{
++nChars;
}
Close the file descriptor from the parent process once you are done writing to it.
Here's a working version of main.
int main(int argc, char **argv)
{
// set up pipe
int pA[2];
char buff[50];
pipe(pA);
// call fork()
pid_t childId = fork();
if (childId == 0) {
// -- running in child process --
int nChars = 0;
// close the output side of pipe
close(pA[1]);
// Receive characters from parent process via pipe
// one at a time, and count them.
while ( read(pA[0], buff, 1) == 1 )
{
++nChars;
}
return nChars;
}
else {
// -- running in parent process --
int nChars = 0;
int size = 0;
printf("CS201 - Assignment 3 - Timothy Jensen\n");
// close the input side of the pipe
close(pA[0]);
// Send characters from command line arguments starting with
// argv[1] one at a time through pipe to child process.
for (int i = 1; i < argc; i++)
{
size = strlen(argv[i]);
for (int z = 0; z < size; z++)
{
write(pA[1], &argv[i][z], 1);
}
}
close(pA[1]);
// Wait for child process to return. Reap child process.
// Receive number of characters counted via the value
// returned when the child process is reaped.
wait(&nChars);
printf("child counted %d chars\n", nChars/256);
return 0;
}
}
It seems a little silly, but you could change:
nChars = read(pA[0], buff, sizeof(buff));
to:
char ch;
nChars = read(pA[0], &ch, 1);
Of course, you would put the above into a loop to assemble a string 'one character at a time' back into buff.

How to sync processes inside a while loop in 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.

Pipes and processes

Premise:
Write a program to query the user for two input strings. Each input string should be a unix command, with arguments allowed. For example, input 1 could be ls -l and input 2 could be more. The program will then create a pipe, and two child processes. The first child process will run the command specified in the first input. It will output to the pipe instead of standard output. The second child process will run the command specified in the second input. It will take its input from the pipe rather than standard input. The parent process will wait on its two children to complete, then the whole thing will repeat. Execution will stop when the '#' symbol is entered as the first command. Here is the code I have:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(){
/* Program Termination Symbol */
const char terminate = '#';
/* String delimiter */
const char delimiter = ' ';
/* Pipe file ID's */
int fileID[2];
/* Parent ID's */
int pid1, pid2;
/* String token */
char * token, * token2;
/* User input */
char * user_input, line[100];
user_input = (char *) malloc(100);
/* Unix Commands */
char * command1[10], *command2[10];
for (int i=0; i<10; i++)
{
command1[i] = (char *)malloc(100*sizeof(char));
command2[i] = (char *)malloc(100*sizeof(char));
}
/* Begin main program logic */
printf("Please enter the first command: \n");
user_input = gets(line);
while (user_input[0] != terminate)
{
token = (char *) malloc(100*sizeof(char));
for (int i=0; i<10; i++)
{
if (i == 0)
{
token = strtok(user_input, &delimiter);
} else {
token = strtok(NULL, &delimiter);
}
if (token != NULL)
{
strcpy(command1[i], token);
} else {
command1[i] = 0;
}
}
printf("Please enter the second command: \n");
user_input = gets(line);
token2 = (char *) malloc(100*sizeof(char));
for (int i=0; i<10; i++)
{
if (i == 0)
{
token2 = strtok(user_input, &delimiter);
} else {
token2 = strtok(NULL, &delimiter);
}
if (token2 != NULL)
{
strcpy(command2[i], token2);
} else {
command2[i] = 0;
}
}
/* Pipe and execute user commands */
/* Create pipe */
pipe(fileID);
/* Create child processes */
pid1 = fork();
if (pid1 != 0)
{
pid2 = fork();
}
/* First child process */
if (pid1 == 0)
{
dup2(fileID[1], 1);
execvp(command1[0], command1);
}
/* Second child process */
if (pid2 == 0)
{
dup2(fileID[0], 0);
execvp(command2[0], command2);
}
/* Wait for children to terminate */
wait(&pid1);
wait(&pid2);
/* Repeat */
printf("Please enter the first command: \n");
user_input = gets(line);
}
return 0;
}
The problem I'm running into is with my waits. If I have both, which would make sense to me (one wait per child) then the program freezes after executing the first pipe. If I remove the second wait, then the program will begin its loop again, but will not accept keyboard input other than enter, and will produce a segfault. So, with both waits, input and output is...
Please enter the first command:
ls
Please enter the second command:
more
Pipe
Pipe.c
Pipe.c~
...and then it locks up. If I remove the second wait, input/output is...
Please enter the first command:
ls
Please enter the second command:
more
Pipe
Pipe.c
Pipe.c~
Please enter the first command:
(I hit enter, nothing else will work)
Segmentation fault
Anyone have any suggestions? It's clearly related to waiting on the two processes, but I'm at a loss as to how to handle it.
This program is now 100% functional - thank you so much for your help, everyone! Stack overflow has been one of the best resources on the internet. Thank you all so much for taking the time to look over my code and give me your suggestions.
I agree with everything torak said, but to address your problem, you need to close your pipes. I think you are "hanging" because the pipe is still open.
So in the parent, right before the "waits", I would close the pipes.
close(fileID[0]);
close(fileID[1]);
wait(&pid_status);
wait(&pid_status);
Then, right before each execvp, I would close the ends of the pipe the child will not be using:
close(fileID[0]);
dup2(fileID[1], 1);
execvp(command1[0], command1);
close(fileID[1]);
dup2(fileID[0], 0);
execvp(command2[0], command2);
That should resolve your hanging. In addition to the suggestions made by torak, I would also recommend fgets instead of gets to prevent a buffer overflow.
A couple of things. Not sure that they are the cause of your problems, but still things to consider before submitting your homework.
I don't think you are using wait correctly. According to http://linux.die.net/man/2/wait it doesn't take pid pointer as an argument.
Each time around the loop you call malloc for token and token2, but I don't see a corresponding release of the memory.
You've writen a single monolithic function. Good coding practice would suggest breaking it out into a collection of subroutines
Finally, and its possibly related to point 3, the following two lines of code appear twice in your code. Again it's not an error, but unnecessary duplication, and inelegant.
printf("Please enter the first command: \n");
user_input = gets(line);
First of all, you're calling wait from the child processes as well [edit: no, you're not, since each child calls execvp].
Also, wait doesn't take a pointer to the child's pid, but to a variable where the process's status will be written to (which means you're throwing away your child's pid).
Finally, try using waitpid with the "WNOHANG" option. It won't hang, you can put both on a loop while you do other stuff, and you can check to see if the child processes have exited by inspecting the status variables. man waitpid.

Resources