Does anyone know why the printf("Type q to quit") line prints twice in the terminal when I run this code:
#include <stdio.h>
#include <unistd.h>
int main (int argc, char *argv[])
{
char run[2];
run[0]='a';
int pid=0;
while (run[0]!= 'q')
{
printf("Type q to quit \n");
fgets (run, 2, stdin);
pid=fork();
//wait();
if(pid==0) { break;}
}
}
I would like the child to break from the loop and the parent to continue looping (to create new children). If I call wait() the execution ends after the first iteration regardless of whether I enter 'q' or not. Otherwise it works as expected but prints the "Type q to quit" line twice every time. Why does this happen?
You have three bugs. First, the carriage return that the user types, after whatever letter, counts as input. Assuming the user types only one character per line, your fgets calls will alternate between returning the character you care about, and returning '\n'. You need to read and discard characters until you reach the end of each line of input, on each iteration. This is what is causing the double printouts.
Second, you need to test for 'q' in between reading from stdin and calling fork; right now, you fork once more after reading the 'q'. Right now this is invisible, but once the child process starts doing something useful it won't be.
Third, this program will go into an infinite loop if the user types ^D at it, because you're not checking for EOF.
Putting that together, corrected code looks like this. I've also fixed some style nits, and arranged so that the parent process exits immediately rather than dropping out of the for loop; this means that when control reaches the point marked with a comment, you know you're in a child process, rather than in the parent on its way out.
#include <stdio.h>
#include <unistd.h>
int
main(void)
{
pid_t pid;
int c;
for (;;)
{
puts("Type q to quit");
c = getchar();
if (c == 'q')
return 0;
if (c == '\n')
continue;
while (c != EOF && c != '\n')
c = getchar();
if (c == EOF)
return 0;
pid = fork();
if (pid == 0)
break;
else if (pid == -1)
{
perror("fork");
return 1;
}
}
/* control reaches this point only in child processes */
return 0;
}
When you type x<enter> on your terminal, you'll get (assuming ordinary encodings all over) two bytes sent to your process: one for the x, one for the newline.
Your fgets call reads at most one byte (so, the x), forks of a child that dies "instantly", prints the messages and calls fgets. fgets picks up where it left: it reads the newline char without blocking, your code forks again for no reason, and loops back.
At that point there's nothing left in the input stream, so fgets waits for I/O.
See for example: I am not able to flush stdin for ways to "clear out" the input stream that you could use here.
You need to wait for these forked process down the line:
int main (int argc, char *argv[])
{
char run[2];
run[0]='a';
int pid=0;
int pids[256]; // should be enough.
int j,i = 0;
while (run[0]!= 'q')
{
printf("Type q to quit \n");
fgets (run, 2, stdin);
pid=fork();
//wait();
if(pid==0) { break;}
else { pids[i] = pid; i++; }
}
for (j = 0 ; j < i && pid != 0 ; j++)
wait(pids[j]);
if(pid == 0){
// do something
}
}
You could instead have the child call another function (such as a program wrapper or even itself in non-forkable version) instead of breaking. The waiting would remain the same i.e. wait only once all forks are, well, forked.
Related
#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.
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.
I am trying to write a shell script in
C language. In short details, if user enters a command I send it to the system() call, if user enters more than one command like "ls;whoami" I parse it and create child processes to execute all of them. Now it works but my methods such as gets() and getting input by the user does not seem well and when I put multi commands, prompt text becomes unseen. Do you have any suggestion or if you see any mistakes or wrong usage because I am not the C guy then I would be grateful.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define RESET "\033[0m"
#define BOLDGREEN "\033[1m\033[32m" /* Bold Green */
#define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */
char input[50];
char command[50];
char *inputget;
char *dotcomma;
char *p;
pid_t pid;
void welcome(){
char* welcomestr = "\n\TEST\n\n";
printf("%s%s%s",BOLDMAGENTA,welcomestr,RESET);
}
void prompt(){
char *username = getenv("USER");
char hostname[1024];
gethostname(hostname, 1024);
char currentDirectory[256];
getcwd(currentDirectory, 256);
printf("%s%s#%s%s%s ~%s $ %s",BOLDGREEN,username,hostname,RESET,BOLDBLUE,currentDirectory,RESET);
}
int main(int argc, char* argv[]){
inputget=input;
welcome();
prompt();
gets(inputget);
if(argc == 1) {
while(strcmp(inputget, "quit")!=0){
p = strsep(&inputget, ";\n");
while(p != NULL){
pid_t parent = getpid();
pid = fork();
if (pid==-1){
perror("failed to fork");
}
else if (pid==0){
system(p);
exit(0);
}else{
p = strsep(&inputget, ";\n");
}
}
wait(NULL);
prompt();
scanf("%s",input);
inputget=input;
}
exit(0);
}else{
//get argc 2 and and read-run commands from text.file
}
}
Let's begin with the worst part: using gets. Don't use this function anymore,
it's dangerous and deprecated. You should use fgets instead.
fgets(input, sizeof input, stdin);
There is no reason why any of these variables
char input[50];
char command[50];
char *inputget;
char *dotcomma;
char *p;
pid_t pid;
have to be global variables, declare them in main.
Stream buffers like stdout are buffered, content is physically written on
the device once the buffer is full or you call fflush to flush the buffer.
One exception is stdout when is connected to a terminal, in that case printf
will flush immediately when a newline is printed. That's why you almost always
see that the format of printf statements end with \n, like
printf("Your age is %d\n", age);
When you don't want to print a newline, because you are printing something like
a prompt, then you should flush stdout yourself.
void prompt(){
char *username = getenv("USER");
char hostname[1024];
gethostname(hostname, 1024);
char currentDirectory[256];
getcwd(currentDirectory, 256);
printf("%s%s#%s%s%s ~%s $ %s",BOLDGREEN,username,hostname,RESET,BOLDBLUE,currentDirectory,RESET);
fflush(stdout); // <-- you need this here
}
The last thing is where you are executing wait. The problem is the
synchronization between the children and the parent process. Once a child is
created, it begins to run immediately. If the child is also printing to stdout
and you don't synchronize with the parent, then there's no guarantee which
output will be printed first.
In your case, if the user enters cmd1;cmd2;cmd3, you are forking 3 times
but you are only doing one wait after you've forked all children. That means
that the three children will run concurrently and the order of their output is
undefined. After all children are forked, you finally do wait(NULL), but this
only waits for one child, then you execute prompt() but remember, the other
children might be still running and hence the output of the prompt might come
before the output of the other children that are running. That is perhaps what
you've been observing.
If you want to emulate the shell, then cmd2 can only start after cmd1 is
finished and cmd3 only after cmd2 is finished. Only when the three commands
are finished you can execute prompt(). That means that you have to wait for
every child to end before the next child can be forked. That's why you have to
move the wait in the parent block before the next fork is called.
// "quit\n" because fgets does not remove the newline
while(strcmp(inputget, "quit\n") != 0) {
p = strsep(&inputget, ";\n");
while(p != NULL) {
if(p[0] == 0)
{
// handles "empty fields", when
// two delimiters come one after
// the other
p = strsep(&inputget, ";\n");
continue;
}
pid = fork();
if (pid==-1) {
perror("failed to fork");
}
else if (pid==0) {
system(p);
exit(0);
} else {
wait(NULL); // <-- here the parent waits
// until the child is finished
p = strsep(&inputget, ";\n");
}
}
prompt();
fgets(input, sizeof input, stdin);
inputget = input;
}
Also note that I'm not using scanf here. scanf("%s"... reads until the first
non-white character, so a command like cat /etc/fstab will only read cat and
your shell will only execute cat and it would block until you close stdin
(by pressing Ctrl+D). The next time, scanf won't wait
for user input and will read /etc/fstab instead and try to execute
/etc/fstab, which will fail, as /etc/fstab is not a script or binary.
That's why it's better to use fgets.
I'd also use a longer buffer for the user input, depending on the command
length, 49 bytes is too short. I'd use 1024 or more. Or you can use getline to
fetch a whole line without the worry about buffer sizes.
I have:
#include <stdio.h>
/* Copy input to output; 2nd version. */
main(void)
{
int c;
while ((c = getchar()) != EOF)
putchar(c);
return 0;
}
I want to terminate the while loop by entering an end-of-line character.
I Have Tried Inputing:
"\t"
"\0"
%d
%f
%c
%x
%n
EOF
"EOF"
\nEOF
int
float
char
long
long long
array
1 => 10
all letters
all symbols on keyboard
.
.
.
Question: What is the magical EOF character that I'm looking for?
*I am Sorry if this is a really easy question for you,but please be nice I'm only a beginner trying to learn something.
On Windows, Ctrl+Z;
on Linux, Ctrl+D.
There is NO EOF character. "EOF" is a logical condition that represents "end of file" has been met.
On Linux machine, you can "signal" the standard input EOF condition by pressing Ctrl+D in the beginning of the line.
Windows systems reserve a character Ctrl+Z, which is 0x1A in hex, to indicate this "end of file" condition. You can input this character by pressing Ctrl+Z. It is still not a real EOF character though. Rather, it is a convention in Windows.
Here ya go #Andy. You just used an int by accident instead of char c.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char c;
while ((c = getchar()) != '\t') // while input != tab, remember to use single
putchar(c); // quotes for characters '\n' etc.
system("pause");
return 0;
}
If you are curious about the signals in UNIX/LINUX system this code might help, wrote it for one of my OS labs. Essentially, the program keep asking for a user input. However, when you try to quit during in the beginning with ctrl+z or ctrl+c it doesn't allow you to because the signal gets ignored by the parent and gets handled by the signal handlers for the child process. Note, the parent is sleeping in the beginning, but when it wakes up it kills the child process and ends the program.
#include <stdio.h>
#include <signal.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/wait.h>
#define maxLength 1024
//****************************************
// Signal Handlers For Child Process
//****************************************
void ctrlchandler(){
signal(SIGINT, SIG_IGN); //Ignore ctrl-c
write(1, "Don't even think about it!", 26);
}
void ctrlzhandler(){
signal(SIGTSTP, SIG_IGN); //Ignore ctrl-z
write(1, "Nice Try.", 9);
}
//****************************************
// Main Program
//****************************************
int main(int argc, char* argv[]){
pid_t pid;
int status;
//Dynamically allocate char array for input line
char *inputLine = (char*)malloc(maxLength*sizeof(char));
//Ignore Ctrl-z and Ctrl-c
signal(SIGINT, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
//Fork Process
if((pid = fork())<0){
//If fork fails
printf("Fork Child Process Faild.\n");
}
//Parent Process
else if(pid != 0){
printf("Parent: My child %d has been spawned.\n",pid);
printf("My pid is %d\n",getpid());
sleep(30);
kill(pid, SIGKILL);
if(waitpid(pid, &status, WUNTRACED))
printf("Child %d has terminated abnormally.\n",pid);
}
//Child Process
else{
sleep(1); //Wait for parent to output first
while(1){
signal(SIGTSTP, ctrlzhandler);
signal(SIGINT, ctrlchandler);
printf("Enter Input:");
fgets(inputLine, maxLength, stdin);
}
}
//Free allocated char array
free(inputLine);
return 0;
}
main()
{
printf ("%d=%x sizeof=%d\n", EOF, EOF, sizeof(EOF));
}
The output is:
-1=ffffffff sizeof=4
EOF is not a char, it is an int
if you type the Control sequence signifying end of file - it will be translated to an int whose value is -1
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.