fork() causing non-deterministic behavior: child process doesn't execute - c

I have a very simple c program:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[] ){
int rc = fork();
if (rc < 0){
// fork failed
fprintf(stderr, "fork failed\n");
} else if (rc == 0) {
printf("hi, I'm child! %d\n", (int) getpid());
fflush(stdout);
} else {
printf("hi, I'm the parent of %d\n", rc);
fflush(stdout);
}
return 0;
}
For some reason, when I compile and run this program, sometimes both print statements will show, but other times only the parent process will show.
Why is this? Even if I don't use wait(), both processes should still run.
Using gcc 7.5.0 ubuntu 18.04

Because the IDE stops listening when the parent process closes, or the shell leaves the child process stuck in the background.
To see the arbitrary printf order:
int status;
printf("hi, I'm the parent of %d\n", rc);
fflush(stdout);
waitpid(pid, &status, 0);

Related

execl is suceeding - however exit(1) is being called

I'm trying to better understand exec() - so I have the following script in testing.c
#include <stdio.h>
#include <sys/types.h>
#include <wait.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char ** argv)
{
if(argc < 2) {
fprintf(stderr,"Error:: Expecting an Argument!\n");
exit(-1);
}
pid_t pid;
pid = fork();
if (pid==0) {
execlp("./testing","testing",NULL);
fprintf(stderr, "I want to get to here...\n");
exit(-1);
}
wait(NULL);
printf("Parent and child done\n");
return 0;
}
The block below is the output after I execute with ./testing one:
Error:: Expecting an Argument!
Parent and child done
In reading how exec() works, I was expecting to be able to fprintf after my execlp call since it should be returning -1, I'm wondering if I need to be setting an errno or something or being more explicity about throwing something so that execlp recoginize the error?
If the execlp function successfully starts the given program, it does not return. The current program image gets replaced with the program image of the new program. So even if the new program exits with status -1, it still doesn't go back to the program that called execlp.
If you want to get the exit status of the child process, pass the address of an int to wait and read that:
int main(int argc, char ** argv)
{
if(argc < 2) {
fprintf(stderr,"Error:: Expecting an Argument!\n");
exit(-1);
}
pid_t pid;
pid = fork();
if (pid == -1 {
perror("fork failed");
exit(-1);
} else if (pid == 0) {
execlp("./testing","testing",NULL);
perror("execlp failed");
exit(-1);
}
int status;
wait(&status);
printf("child exit status: %d\n", WEXITSTATUS(status));
printf("Parent and child done\n");
return 0;
}
The output:
Error:: Expecting an Argument!
Parent and child done
is from
(first line) child process tries to run but no command line parameter.
(second line) parent process finishes

C, Fork(), take CL argument and execute CHDIR

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
char *getcwd(char *buf, size_t size); //define getcwd
char PATH_MAX[1024]; //define max size of path
int chdir(const char *path);
int main(int argc, char *argv[]) { // gets arguments when program ran, no arguments means argv=1
pid_t pid; //process ID = pid
pid=fork();
char cwd[1024]; //compare directory to max character size
if(pid==0){ //child has been forked! //child process created
int ret;
printf("Child PID=%d\n", getpid());
getcwd(PATH_MAX, sizeof(PATH_MAX));
printf(" My current working directory is: %s\n", PATH_MAX);
ret= execl("/bin/ls", "ls", "-a", "-l", "-h", NULL);
printf("%d\n", ret); //why isn't this printed out?
}
//}
else {
int status;
//parent process
//wait for child to complete
printf("Parent PID=%d\n", getpid());
if (waitpid(pid, &status, 0) == -1) {
printf("ERROR");
}
else {
printf("Child done.\n");
getcwd(PATH_MAX, sizeof(PATH_MAX));
printf("0");
exit(0);
}
}
}
I left my commented out code so you can see my thought process. If my understanding is correct the shell(terminal) is its own process so when you call fork, it creates a new child process and its parent becomes the shell. So trying to chdir in the child process will not translate over to the shell and you will remain in the same Directory so you would need to execute the chdir function in the parent PID, which is now the shell, yes?
I am having a hard time trying to figure out where exactly I should be putting this chdir() command and what flavor of exec I need to use to execute the terminal commands.
I am testing 3 different commands as command line arguments when running in terminal. This is after making the file with gcc -o script script.c
$ ./script
result - print out current directory
print out "Usage: "<dir>" string. no command executed
$ ./script .
result -"Executing ls . --all -l --human-readable" string
executes above commands
$./script /
result - should execute above commands but change directory before
executing
$./script /blah/blah
result - can't execute chdir
exit status: 1
I believe this code should cause the child process to return a -1 which would terminate it, or if my if statement is correct it would print out the error message.
Any help would be appreciated, I believe I got the logic down, or at least somewhat. Just having a hard time implementing chdir.
I cooked your program a little and got the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
int main(int argc, char *argv[]) // gets arguments when program ran, no arguments means argv=1
{
int ret;
pid_t pid; //process ID = pid
pid=fork();
char cwd[1024]; //compare directory to max character size
char newPath[200]=".";
if( argc > 1 )
{
strcpy(newPath,argv[1]);
}
ret=chdir(newPath);
if( ret < 0 )
{
printf("Problem switching to :%s\n", newPath);
perror("chdir");
exit(ret);
}
if(pid==0){ //child has been forked! //child process created
int ret;
printf("Child PID=%d\n", getpid());
getcwd(cwd, sizeof(cwd));
printf(" My current working directory is: %s\n", cwd);
ret= execl("/bin/ls", "ls", "-a", "-l", "-h", NULL);
printf("%d\n", ret); //why isn't this printed out?
}
else {
int status;
//parent process
//wait for child to complete
printf("Parent PID=%d\n", getpid());
if (waitpid(pid, &status, 0) == -1) {
printf("ERROR");
} else
{
printf("Child done. stat=%d\n", status);
getcwd(cwd, sizeof(cwd));
printf("Parent cwd:%s\n", cwd);
printf("0");
exit(0);
}
}
}
Your understanding is not quite correct. When you execute a program from the command line on a terminal, the shell forks and does an execute of the process you are running. Normally, the parent shell process waits until the child process is done. You created a child process and did another exec and another wait. The parent shell patiently waits for you to finish, forked processes and all.
Let's see:
I put the chdir in your main program to show the child follows the parent.
A child process is not going to change the working directory of the parent. Unix and Linux don't work that way.
When you run execl, that is it. There is no return unless it can't do the execute. The point of exec* is you are blowing away your currently running program with a new executable. There is nothing to return to.
If you want to see the return code of the child, look at the status returned by the wait. In this case, the ls ran fine so the return code is zero. If you added a last argument of "baddir" (that is not there) you would see the ls non-zero return code, in this case, 512.

Exec function in c is not running

This is not running on my Mac for some reasons that I can't figure out. the output I am getting is only from the main.c
the output is
Parent PID 4066
Child PID 4067
Process 4067 exited with status 5
I need the main.c to execute counter.c and pass the argument 5 which I will then have to use it inside the for a loop, but I can't get exec to run at all no matter what path I put.
//main.c
int main(int argc, char *argv[]) {
pid_t childOrZero = fork();
if (childOrZero < 0){
perror("Error happened while trying to fork\n");
exit(-1);
}
if (childOrZero == 0){
printf("Child PID %d\n", (int)getpid());
execl("./counter", "5", NULL);
exit(5);
}
// THis must be parent
printf("Parent PID %d\n", (int)getpid());
int status = 0;
pid_t childpid = wait(&status);
int childReturnedValue = WEXITSTATUS(status);
printf("Process %d exited with status %d\n", (int)childOrZero, childReturnedValue);
return 0;
}
counter.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main (int argc, char *argv[]){
for (int i = 0; i<5; i++){
printf("Process: %d %d\n", (int)getpid(),i);
//sleep(3);
}
}
In a comment, you mention you compile counter.c into an executable called a.out. This is the default executable name when you do not provide an output name explicitly to the compiler. Thus, if you compile both counter.c and main.c, only one of them will be the a.out.
You can provide gcc an option to name your executable different from the default:
gcc -o counter counter.c
Also, your invocation of execl is not quite correct. The first argument is the path to the executable, but the remaining arguments will become argv[0], argv[1], etc. Thus, you really want to invoke execl this way:
execl("./counter", "counter", "5", NULL);
Read documentation of execl for MacOSX and the POSIX specification of execl
It can fail (and that is the only case when it is returning). So code:
if (childOrZero == 0){
printf("Child PID %d\n", (int)getpid());
execl("./counter", "5", NULL);
perror("execl counter");
exit(5);
}

In c, linux , about kill and raise

I run my C program on debian-linux ,the program is expected to output
$ ./kill_raise
Child(pid : 4877) is waiting for any signal
Parent kill 4877
but I just got
Parent kill 4877
the string in subprocess (Child(pid : %d) is waiting for any signal) is not print out,why?
and this is my program
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
pid_t pid;
int res;
pid = fork();
if (pid < 0)
{
perror ("Fork error\n");
exit(0);
}
if (pid == 0)
{
printf ("child(pid:%d) is waiting for signal\n", getpid());
raise(SIGSTOP);
exit(0);
}
else
{
if ((waitpid(pid, NULL, WNOHANG)) == 0)
{
if ((res = kill(pid, SIGKILL)) == 0)
{
printf ("parent kill %d\n", pid);
}
}
waitpid(pid, NULL, 0);
exit(0);
}
}
You're hitting a race condition. The parent is executing the kill before the child can execute its printf. Remove WNOHANG so the parent actually waits for the child to stop. Also, you need to add WUNTRACED so that waitpid reports the stop (by default, it will only report termination). Finally, you shouldn't be testing wait()'s return value against 0 in this case.

Getting return value of external program [duplicate]

I know that it is possible to read commands output with a pipe? But what about getting return value ? For example i want to execute:
execl("/bin/ping", "/bin/ping" , "-c", "1", "-t", "1", ip_addr, NULL);
How can i get returned value of ping command to find out if it returned 0 or 1?
Here is an example I wrote long time ago. Basically, after you fork a child process and you wait its exit status, you check the status using two Macros. WIFEXITED is used to check if the process exited normally, and WEXITSTATUS checks what the returned number is in case it returned normally:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
int number, statval;
printf("%d: I'm the parent !\n", getpid());
if(fork() == 0)
{
number = 10;
printf("PID %d: exiting with number %d\n", getpid(), number);
exit(number) ;
}
else
{
printf("PID %d: waiting for child\n", getpid());
wait(&statval);
if(WIFEXITED(statval))
printf("Child's exit code %d\n", WEXITSTATUS(statval));
else
printf("Child did not terminate with exit\n");
}
return 0;
}
You can use waitpid to get the exit status of you child process as:
int childExitStatus;
waitpid( pID, &childExitStatus, 0); // where pID is the process ID of the child.
exec function familly does not return, the return int is here only when an error occurs at launch time (like not finding file to exec).
You have to catch return value from the signal sent to the process that forked before calling exec.
call wait() or waitpid() in your signal handler (well, you can also call wait() in your process without using any signal handler if it has nothing else to do).
Had trouble understanding and applying the existing answers.
In AraK's answer, if the application has more than one child process running, it is not possible to know which specific child process produced the exit status obtained. According the man page,
wait() and waitpid()
The wait() system call suspends execution of the calling process until one of its children terminates. The call wait(&status)
is equivalent to:
waitpid(-1, &status, 0);
The **waitpid()** system call suspends execution of the calling process until a **child specified by pid** argument has changed state.
So, to obtain the exit status of a specific child process we should rewrite the answer as :
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
int number, statval;
int child_pid;
printf("%d: I'm the parent !\n", getpid());
child_pid = fork();
if(child_pid == -1)
{
printf("could not fork! \n");
exit( 1 );
}
else if(child_pid == 0)
{
execl("/bin/ping", "/bin/ping" , "-c", "1", "-t", "1", ip_addr, NULL);
}
else
{
printf("PID %d: waiting for child\n", getpid());
waitpid( child_pid, &statval, WUNTRACED
#ifdef WCONTINUED /* Not all implementations support this */
| WCONTINUED
#endif
);
if(WIFEXITED(statval))
printf("Child's exit code %d\n", WEXITSTATUS(statval));
else
printf("Child did not terminate with exit\n");
}
return 0;
}
Feel free to turn this answer to an edit of AraK's answer.
You can wait on the child process and get its exit status.
The system call is wait(pid), try to read about it.
Instead of exec family, you can use popen. Then read the output using fgets or fscanf.
char buff[MAX_BUFFER_SIZE];
FILE *pf = popen("your command", "r");
fscanf(pf, "%s", buff);
pclose(pf);

Resources