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);
}
Related
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.
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
I've been working on creating my own Unix Shell in C to get practice with its interworkings...I'm having some issues getting my process to run in the background while allowing my shell to continue taking user input. If you could take the time to dissect what I've got below it would be much appreciated!
My variables are below, just incase that helps understand things more...
#define TRUE 1
static char user_input = '\0';
static char *cmd_argv[5]; // array of strings of command
static int cmd_argc = 0; // # words of command
static char buffer[50]; // input line buffer
static int buffer_characters = 0;
int jobs_list_size = 0;
/* int pid; */
int status;
int jobs_list[50];
Here is my main function.
int main(int argc, char **argv)
{
printf("[MYSHELL] $ ");
while (TRUE) {
user_input = getchar();
switch (user_input) {
case EOF:
exit(-1);
case '\n':
printf("[MYSHELL] $ ");
break;
default:
// parse input into cmd_argv - store # commands in cmd_argc
parse_input();
//check for zombie processes
check_zombies();
if(handle_commands() == 0)
create_process();
printf("\n[MYSHELL] $ ");
}
}
printf("\n[MYSHELL] $ ");
return 0;
}
Parse Input...I know, I can't get readline to work on this box :(
If provided the & operator, create the job in the background... (see below)
void parse_input()
{
// clears command line
while (cmd_argc != 0) {
cmd_argv[cmd_argc] = NULL;
cmd_argc--;
}
buffer_characters = 0;
// get command line input
while ((user_input != '\n') && (buffer_characters < 50)) {
buffer[buffer_characters++] = user_input;
user_input = getchar();
}
// clear buffer
buffer[buffer_characters] = 0x00;
// populate cmd_argv - array of commands
char *buffer_pointer;
buffer_pointer = strtok(buffer, " ");
while (buffer_pointer != NULL) {
cmd_argv[cmd_argc] = buffer_pointer;
buffer_pointer = strtok(NULL, " ");
//check for background process execution
if(strcmp(cmd_argv[cmd_argc], "&")==0){
printf("Started job %d\n", getpid());
make_background_job();
}
cmd_argc++;
}
}
Make background job. Closes child process STDIN, opens new STDIN, and executes.
void make_background_job()
{
int pid;
pid = fork();
fclose(stdin); // close child's stdin
fopen("/dev/null", "r"); // open a new stdin that is always empty
fprintf(stderr, "Child pid = %d\n", getpid());
//add pid to jobs list
jobs_list[jobs_list_size] = getpid();
/* printf("jobs list %d", *jobs_list[jobs_list_size]); */
jobs_list_size++;
execvp(*cmd_argv,cmd_argv);
// this should never be reached, unless there is an error
fprintf (stderr, "unknown command: %s\n", cmd_argv[0]);
}
The meat of my job control. Fork spawns child, returns 0 for child and PID for parent.
void create_process()
{
pid_t pid;
pid = fork();
status = 0;
switch(pid){
case -1:
perror("[MYSHELL ] $ (fork)");
exit(EXIT_FAILURE);
case 0:
make_background_job();
printf("\n\n----Just made background job in case 0 of create_process----\n\n");
break;
default:
printf("\n\n----Default case of create_process----\n\n");
// parent process, waiting on child...
waitpid(pid, &status, 0);
if (status != 0)
fprintf (stderr, "error: %s exited with status code %d\n", cmd_argv[0], status);
else
break;
}
}
My problem is when I execute a job in the background, its executing the command twice, and exiting out of the shell. (It functions correctly otherwise if no background process is enabled). Where am I getting confused? I think it may have to do with issues regarding my PID's, as I'm not populating the list correctly either in 'make_background_job'
Here is my output, the example.sh just throws out helloWorld:
[MYSHELL] $ ./example.sh &
Started job 15479
Child pid = 15479
Child pid = 15481
Hello World
Hello World
What seems to happen is
in main() the prompt is displayed, expecting a command
when a command is input, parse_input() is called
it builds the commands array until it finds & where it calls make_background_jobs()
that function forks quickly, and executes in parallel, in two processes, execvp()
execvp() replaces each of the two processes to execute the command
thus two "Hello world" appear.
The problem is in make_background_jobs() where, I think, the expected behavior was that only one of the two processes should execute the command, and the other one (father) returns, to keep the program active.
This can be solved by modifying that function, making the father process return:
void make_background_job()
{
int pid;
pid = fork();
if (pid) return; // The father process returns to keep program active
...
edit
I gave it a try, removing the unnecessary
void make_background_job()
{
int pid;
pid = fork();
if ( ! pid)
{
fclose(stdin); // close child's stdin
fopen("/dev/null", "r"); // open a new stdin that is always empty
fprintf(stderr, "Child Job pid = %d\n", getpid());
//add pid to jobs list
jobs_list[jobs_list_size] = getpid();
/* printf("jobs list %d", *jobs_list[jobs_list_size]); */
jobs_list_size++;
execvp(*cmd_argv,cmd_argv);
// this should never be reached, unless there is an error
fprintf (stderr, "unknown command: %s\n", cmd_argv[0]);
exit(1);
}
waitpid(pid, &status, 0);
}
The background job is created in another process. The father waits for the job to complete.
void parse_input()
{
// clears command line
while (cmd_argc != 0) {
cmd_argv[cmd_argc] = NULL;
cmd_argc--;
}
buffer_characters = 0;
// get command line input
while ((user_input != '\n') && (buffer_characters < 50)) {
buffer[buffer_characters++] = user_input;
user_input = getchar();
}
// clear buffer
buffer[buffer_characters] = 0x00;
// populate cmd_argv - array of commands
char *buffer_pointer;
buffer_pointer = strtok(buffer, " ");
int ok = 0;
while (buffer_pointer != NULL) {
cmd_argv[cmd_argc] = buffer_pointer;
buffer_pointer = strtok(NULL, " ");
//check for background process execution
if(strcmp(cmd_argv[cmd_argc], "&")==0){
ok = 1;
break;
}
cmd_argc++;
}
if (!ok) cmd_argv[cmd_argc = 0] = NULL; // If no & found, reset commands
}
Only parses input.
Below a new handle_commands() that return 0 if there is a command to play, and the main follows.
int handle_commands() { return cmd_argc > 0 ? 0:1; }
int main(int argc, char **argv)
{
printf("[MYSHELL] $ ");
while (TRUE) {
user_input = getchar();
switch (user_input) {
case EOF:
exit(-1);
case '\n':
printf("[MYSHELL] $ ");
break;
default:
// parse input into cmd_argv - store # commands in cmd_argc
parse_input();
//check for zombie processes
check_zombies();
if(handle_commands() == 0)
make_background_job(); // Call directly the bg job
printf("\n[MYSHELL] $ ");
}
}
printf("\n[MYSHELL] $ ");
return 0;
}
The main() calls directly make_background_job().
There is only one fork() in make_background_job. create_process() has been removed.
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?