can't get input after first time in do while - c

I am trying to fork a child process put him in sleep and wake him up whenever the user enter a line of text to print number of lines entered.
My code is working fine. But weird thing I found is I have to user two gets(str) statement if I didn't the user will be prompted for 1 time only.
if run the code and comment one gets(str) you will know what I mean.
Your help is appreciated. Thanks
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <wait.h>
#include <unistd.h>
#include <signal.h>
main () {
int n;
char ch;
pid_t child;
int erret;
char str[20];
int c = 0;
if ((child = fork ()) < 0) {
perror ("fork");
exit (0);
}
else {
//waitpid(child,NULL,0);
do {
waitpid (child, NULL, 0);
printf ("Enter a line(s) \n");
//funn();
//fflush(stdin);
//scanf("%d",&n);
gets (str);
gets (str);
erret = kill (child, SIGCHLD);
printf ("Signal %d\n", erret);
if (erret >= 0) {
c++;
printf ("You have entered : %d line(s)\n", c);
//pause();
//waitpid(child,NULL,0);
}
else {
kill (child, SIGKILL);
exit (0);
}
printf ("\nPress 9 to exit :");
fflush (stdin);
scanf ("%d", &n);
fflush (stdin);
} while (n != 9);
kill (child, SIGKILL);
}
}

Your concept is flawed, you're forking with out specifying what the parant and child does. So you have a race conditon on gets. This is because after the fork call two copies of the code is run, one copy by the parent and one copy by the child. So the fix is to add a swich or else if statement to separate your code into sections for the child and parent. BTW as already stated use fgets
switch(fork()):
case -1:
//Error
case 0:
// Child code
default:
// Parant code

There are a number of ways to get repetitive string input as you are trying to do. One of the standard approaches is to gather input until the user signals EOF signifying there is no more data to enter. (On Linux EOF is generated from the terminal with ctrl + d). Without changing your logic and without commenting on your fork, waitpid, etc.. implementation, the following is one way to handle gathering string input for your program until the users sends an EOF by pressing ctrl+d on the keyboard:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <wait.h>
#include <unistd.h>
#include <signal.h>
int main () {
pid_t child;
int erret;
int c = 0;
ssize_t nread = 0; /* number of chars read by getline */
char *lineptr = NULL; /* string read by (NULL forces getline to allocate) */
size_t nbytes = 0; /* number of bytes to read (ignored with lineptr = NULL) */
if ((child = fork ()) < 0) {
perror ("fork");
exit (0);
}
else
{
waitpid (child, NULL, 0);
printf ("\nEnter line(s) of text (ctrl+d to exit):\n\n");
while (printf (" Input: ") && (nread = getline (&lineptr, &nbytes, stdin) != -1))
{
erret = kill (child, SIGCHLD);
printf ("\n Signal %d\n", erret);
if (erret >= 0) {
c++;
printf (" You have entered : %d line(s)\n\n", c);
}
else
{
kill (child, SIGKILL);
exit (0);
}
}
kill (child, SIGKILL);
}
return 0;
}
output:
$ ./bin/ind2
Enter line(s) of text (ctrl+d to exit):
Input: line one
Signal 0
You have entered : 1 line(s)
Input: line 2
Signal 0
You have entered : 2 line(s)
Input: line 3
Signal 0
You have entered : 3 line(s)
Input: Killed

Related

How can I get my C Shell to recognize that this is a command?

I am very new at C but am currently working on creating a C program to serve as a shell interface. It is supposed to accept commands and then execute each command in a separate process. I am currently stuck trying to get C to recognize that it is a command. I am unsure how to do this, and can't seem to find any useful examples.
Here is my code, it is saying that everything is not a valid command ("no cmd"). Does anyone have any idea why this would be occurring? Is C not able to recognize it is a command in the execvp() function or do I need to implement something for that specific purpose?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAX_LINE 80
/* 80 chars per line per command */
int main(void) {
//char *args[MAX_LINE/2 + 1];
char *args = calloc(MAX_LINE, (MAX_LINE/2 +1));
const size_t sz = MAX_LINE;
pid_t pid;
/* command line (of 80) has max of 40 arguments*/
int should_run = 1;
while (should_run) {
printf("osh>"); //beginning of command line
fgets(args, sz, stdin); //gets the stdin
char *token = strtok(args, " \n"); //supposed to break str if it has a space or line and recognize there are 2 commands
printf("%s\n", token);
token = strtok(NULL," \n");
printf("%s\n", token);
pid_t parent = getpid();
pid = fork(); //forking child
if(pid == 0){ //if forking occurred
int status = execvp(&args[0], &args); //status of input, is it a command?
printf("%d", status);
printf("forked!");
if(status == -1) { //if cmd err, print
printf("no cmd");
return 1;
} else {
printf("line will be printed");
}
return 0;
}
fflush(stdout); //flush
/*
* After reading user input, the steps are :
* 1: fork a child process
* 2: the child process will invoke execvp()
* 3: parent process waits for the child to exit before
continuing
*/
}
exit(0);
/**
return to the operating system:
-exit this process
-flush all
*/
}
If you look at the documentation for the exec family of functions, you'll note that the functions only return if the exec failed. That's because exec, when successful, completely replaces the calling process with the invoked program.
What you need to do is, from the parent process (i.e., the one that got a positive value returned from fork), wait on the child process via waitpid.
pid_t pid;
pid = fork();
if ( pid < 0 ) {
// Handle the error.
}
else if ( pid == 0 ) {
execvp(&args[0], &args);
// The fact that we've reached this line means that execvp failed.
exit(1);
}
else {
int status;
while ( waitpid(pid, &status, 0) != pid ) {} // We need this loop in case waitpid gets interrupted by a signal.
// status doesn't equal the return value of the child process. We need to extract that with macros.
if ( WIFEXITED(status) ) {
printf("Child process exited with code %i\n", WEXITSTATUS(status));
}
else {
printf("Child process was terminated by signal number %i\n", WTERMSIG(status));
}
}

How to use dup2 to redirect stdin and stdout to pipe file descriptors?

I was trying to fork a process and redirect stdout of the parent to the writing end of the pipe and stdin of the child to the reading end of the pipe. The child is supposed to read integers until the parent prints zero. the parent prints from 1 to 3 and then prints 0. Both the parent and the child prints the time when they start and when they finish. since the parent can't print to stdout it sends it's starting and finishing time to the child and the child prints both its starting time and finishing time and parents starting time and finishing time. I could've used dup and redirected stdout to another file descriptor but I chose to make it simple. The program is very simple but the output that I get doesn't make scene.
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
int main()
{
int fd[2];
int p = pipe(fd);
int ch = fork();
if (ch)
{
// Parent - Counts from 1 to 3
int dp = dup2(fd[1], 1);
printf("Cnt_Started_at_%d\n", time(NULL));
for (int i = 0; i <= 3; i++)
{
printf("Parent %d\n", i);
sleep(1);
}
printf("0\n");
printf("Cnt_Finished_at_%d\n", time(NULL));
}
else
{
// Child - Terminated by 0
int dp = dup2(fd[0], 0);
printf("Trm_Started_at_%d\n", time(NULL));
char buffer[100];
scanf("%s", buffer);
printf("%s\n", buffer);
int i;
while (scanf("Parent %d", &i) && i)
printf("Recieved: %d\n", i);
scanf("%s", buffer);
printf("%s\n", buffer);
printf("Trm_Finished_at_%d\n", time(NULL));
}
}
Output:
Trm_Started_at_1578295974
Cnt_Started_at_1578295974
Parent
Trm_Finished_at_1578295978
The root issue is the usage of 'scanf %s' to read messages. Recall that '%s' will stop reading when it encounter white space, and will put the white space back into the buffer.
The initial message from the parent is 'Cnt_Started_at_1234\n'. This child process will read the token, but will leave the trailing '\n' in the buffer.
Next the parent will send 'Parent 0\n'. The Child will attempt to parse this is scanf("Parent %d", &i) && i). Two issues here:
The 'P' from 'Parent' will not match the left over '\n' from the initial message
When the format is updated to skip of leading spaces, the value of 'i' will be zero, which will cause the while to exit after reading 'Parent 0'.
Possible solution: Allow the scanf to skip of spaces, and eliminate the condition on i
while (scanf(" Parent %d", &i) == 1 )
printf("Recieved: %d\n", i);
The problem here is with your scanf statement. As suggested by #dash-o, change it to treat spaces.
Another problem is that, first i = 0. You need to modify your while to accomodate 0.
Since you are only evaluating i in your while loop, you won't be entering for ``i=0``` case.
Below is the modified program and the output, also, please add various checks for return values of the functions / buffer overflows as you deem right --
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
int main()
{
int fd[2];
int p = pipe(fd);
int ch = fork();
if (ch)
{
// Parent - Counts from 1 to 3
int dp = dup2(fd[1], 1);
printf("Cnt_Started_at_%d\n", time(NULL));
for (int i = 0; i <= 3; i++)
{
printf("Parent %d\n", i);
sleep(1);
}
printf("0\n");
printf("Cnt_Finished_at_%d\n", time(NULL));
}
else
{
// Child - Terminated by 0
int dp = dup2(fd[0], 0);
printf("Trm_Started_at_%d\n", time(NULL));
char buffer[100];
scanf("%s", buffer);
printf("%s\n", buffer);
int i;
while (scanf(" Parent %d", &i) && i >= 0) // notice change here ...
printf("Recieved: %d\n", i);
scanf("%s", buffer);
printf("%s\n", buffer);
printf("Trm_Finished_at_%d\n", time(NULL));
}
}
OUTPUT --
$ ./main.out
Trm_Started_at_1578303662
Cnt_Started_at_1578303662
Recieved: 0
Recieved: 1
Recieved: 2
Recieved: 3
0
Trm_Finished_at_1578303666

Parent gets EOF when child quits after getting an EOF?

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
int ch;
pid_t p = fork();
if (p == 0) {
do {
ch = getchar();
} while (ch >= 0);
return 0;
}
int s;
waitpid(p, &s, 0);
printf("A done\n");
p = 0;
do {
ch = getchar();
} while (ch >= 0 && (++p));
printf("chars: %d\n", p);
return 0;
}
Here's a minimal example code. Theoretically it should read some characters until EOF, and print A done, and read some more characters, and show you how many there are after A done.
However, on my Windows Subsystem for Linux (Ubuntu 18.04), when I hit Ctrl-D for the first time, both the child and parent processes quit (receives an EOF). The output I get is something like
asdfghjkl
^DA done
chars: 0
Why is that? And how do I fix this?
In a fork(2), file descriptors are dup(2)ed, so they share the same file pointer, and what one of the process reads, is not read by the other, as a consequence of this.

How to set status termination of a process C?

My program is a rudimental little shell.
It allow you to run programs in PATH as ls, cd..also with arguments.
To run the program type from terminal "./myshell2" then it starts and you can insert how many commands you want.
It starts a child process, runs execvp,it returns and restarts so you can type a new command.
When typed "Q" or "q" all the entire program should terminates.
The problem is that I don't know how to stop it,the code is below.
My idea is, when typed "Q" or "q", to kill the child process created and send a signal to comunicate its bad termination(of child process).
So the final status(from parent) 'll be not 1 and the function returns.
I commented some parts of the code hoping that it's easier to understand.
It works the problem is that to stop it I need of ctrl C.
I would like to say to child process that he must ends with a non-zero value.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <signal.h>
int main(int argc, char * argv[]) {
while(1)
{
pid_t pid = fork();
if (pid == -1) {
perror("fork error");
exit(EXIT_FAILURE);
}
if (pid == 0) { // child process
printf("type the command to start (and arguments if required) \n"
"Q to quit\n");
char *dest[10]; // allow you to insert
char line[4096];//commands from terminal
if (fgets(line,sizeof(line),stdin)==0) return 1;
int i;
line[strcspn(line, "\n")] = '\0';
char *st = line;
for (i=0; i< 10 && (dest[i]=strsep(&st," "))!=NULL;i++)
continue;//now you typed the command
if ( ( memcmp(dest[0],"Q",1)==0 ) // if Q or q the program
|| (memcmp(dest[0],"q",1)==0) ) //must end
{
printf("got it!\n");
if (kill(getpid(),SIGSEGV)==-1) printf("kill error\n");
//in theory the process should terminates with bad status
// and the value of the variable "status" 'll be not 0
// I think that the problem is in this part of the code
}
if( strcmp(dest[0]," ")!=0 )
{
int res = execvp(dest[0], dest);
}
else
{ int res= execvp(dest[1],dest+1);}
perror("execvp error");
exit(EXIT_FAILURE);
}
int status;
pid_t child = wait(&status);
if (child == -1) {
perror("wait error");
exit(EXIT_FAILURE);
}
if (status==1)
break; //so it can exit from the loop that creates new process
setenv("WAIT","TRUE",0); //dont' worry about
//perror("setenv error\n");
if (memcmp("TRUE",getenv("WAIT"),4) == 0 ) //these 6 lines
printf("WAIT=TRUE\n");
else if(memcmp("FALSE",getenv("WAIT"),4) == 0 )
printf("WAIT=FALSE\n");
printf("end current process (status=%d, child=%d)\n", WEXITSTATUS(status), son);
}
return EXIT_SUCCESS;
}
You're printing out WEXITSTATUS() for all cases, but that isn't right. You need to check if the status returned by wait is an exit status or not using WIFEXITED(). If it's non-zero then the child exited normally. Otherwise, you can use WIFSIGNALED() to see if the child was terminated and you'll get the signal from WTERMSIG()
if(WIFEXITED(status))
{
printf("end current process (status=%d, child=%d)\n", WEXITSTATUS(status), son);
}
else if(WIFSIGNALED(status))
{
printf("end current process (signal=%d, child=%d)\n", WTERMSIG(status), son);
}
You really should have the parent process handle the inputting of the command and leave the child process to run it though.

How to output a 'run-time error' of a program ran by execvp function into a file?

I'm writing a program on eclipse, linux, in which I need to output the results of an inner program a.out(ran by execvp()) function, into a file(I'm using execvp function, but every other exec function is good).
The program runs flawlessly for proper input, and outputs into the file I defined.
The problem is that I need to output every output, even errors (run-time errors, like segFault) message that will be received for input that'll cause the inner program a.out to fail (e.g. divide by zero).
here is my code for the program that call to execvp:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
//string for execvp to run "a.out"
char *arg[] = {"./a.out", NULL};
//name of input file
char *input = "input.txt";
pid_t child_pid;
int child_status;
int input_fd, fd_output;
//open input.txt
if ((input_fd = open(input, O_RDONLY)) < 0)
{
perror("");
exit(-1);
}
//redirect standard input to input.txt
dup2(input_fd, 0);
//create output.txt
if ((fd_output = open("output.txt",
O_CREAT|O_TRUNC|O_RDWR, 0644)) < 0)
{
perror("");
exit(-1);
}
//redirect standard output and standard error to output.txt
dup2(fd_output, 1);
dup2(fd_output, 2);
child_pid = fork();
if(child_pid == 0) {
// This is done by the child process.
execvp(arg[0], arg);
// If execvp returns, it must have failed.
perror("fork");
exit(0);
} else {
// This is run by the parent. Wait for the child to terminate.
if (wait(&child_status) == -1) {
printf("error!\n");
}
}
return 0;
}
a.out source file:
#include <stdio.h>
#include <unistd.h>
int main () {
int op, num1, num2;
do {
printf("Please enter operation\r\n");
scanf("%d", &op);
switch(op) {
case 1:
printf("Please enter two numbers\r\n");
scanf("%d %d", &num1, &num2);
printf("The devision is %d\r\n", num1/num2);
break;
case 4:
printf("Bye\r\n");
break;
default:
break;
}
}while(op != 4);
return 0;
}
good `input.txt':
1
3 4
4
bad `input.txt:
1
3 0
4
for the good input, I get output.txt:
Please enter operation
Please enter two numbers
The devision is 0
Please enter operation
Bye
but for the bad output I get a blank output.txt file, no matter what.
So how to output a.out errors (if there's any) into output.txt?

Resources