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);
}
Related
I have tried to find solution all over the internate, but forums keep saying it is impossible, so here I a with my question. How does a shell (any shell like bash) keep track of exit codes. Is it by tracking their child process? (if so how could I go about implementing such a thing in a program where I am creating and killing many children processes) Or Is there a global variable that is equivalent to $? that I can access in c? Or do they store it in a file?
Here is an example of executing grep on a path that doesn't exist and getting the return code in the parent:
code:
#include <errno.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
pid_t childPid;
switch(childPid = fork()) {
case -1:
fprintf(stderr, "Error forking");
return 1;
case 0:
printf("CHILD: my pid is: %d\n", getpid());
int ret = execlp(argv[1], argv[1], argv[2], argv[3], (char *) NULL);
if (ret == -1) {
printf("CHILD: execv returned: %d\n", errno);
return errno;
}
break;
default:
printf("I am the parent with a child: %d\n", childPid);
int childRet;
wait(&childRet);
printf("PARENT, child returned: %d\n", childRet >> 8);
}
return 0;
}
Execution:
# Example of Failure execution:
[ttucker#zim c]$ cc -o stackoverflow so.c && ./stackoverflow grep test /does/not/exists
I am the parent with a child: 166781
CHILD: my pid is: 166781
grep: /does/not/exists: No such file or directory
PARENT, child returned: 2
# Example of a successful execution:
[ttucker#zim c]$ cc -o stackoverflow so.c && ./stackoverflow echo foo bar
I am the parent with a child: 166809
CHILD: my pid is: 166809
foo bar
PARENT, child returned: 0
'wait' or 'waitpid'.
With that, you can keep track of child processes and how they were terminated.
You can check the 'wstatus' with the macro 'WEXITSTATUS', which is the return value of the 'main' function (if terminated with 'exit').
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);
#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.
I created a test file to see if I could run a second program, but the code doesn't run the actual file even though it seems to compile. Is my syntax for exec incorrect?
coordinator.c
int main(int argc, char *argv[])
{
// Creates 2^n processes for n amount of values.
pid_t child = fork();
if(child < 0) //parent process
{
perror("fork() system call failed.");
exit(-1);
}
else if(child == 0) //Child Process, worker will be called here.
{
execl("/worker", "worker", "Hello", NULL);
printf("I'm the child %d, my parent is %d\n", getpid(), getpid());
}
else
{
printf("I'm the parent %d, my child is %d\n", getpid(), child);
wait(NULL); // wait for child process to catch up
}
}
worker.c
int main(int argc, char *argv[])
{
printf("Hi, I'm the worker file!");
return 0;
}
The problem is in the PATH argument you're passing to execl().
In fact, if you do insert a / at the beginning of the string passed as the first argument, the function is going to seek the program at the root of your file system.
To let it look for the worker executable in your current directory, just specify the name of it, thus execl("worker", ... ), or execl("./worker", ... )
Take a look here to understand how the function works https://www.systutorials.com/docs/linux/man/3-execl/
Let's say worker executable is in the same directory where you are running the main(coordinator) process then in child process while doing exec you should do ./worker instead of /worker, that shows current working directory.
see then man pages of exec() for other argument, it says
int execl(const char *path, const char *arg, ...);
child process should be like below
else if(child == 0) //Child Process, worker will be called here.
{
printf("I'm the child %d, my parent is %d\n", getpid(), getpid());
//execl("/worker", "worker", "Hello", NULL);/** It's wrong, check the below one **/
execl("./worker", "./worker", NULL);
}
IF worker is in different directory then set the PATH variable, it seems it's in same directory because you are trying to do /worker instead of ./worker.
EDIT :
How to compile & execute :
coordinator.c
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char *argv[])
{
pid_t child = fork();
if(child < 0){
perror("fork() system call failed.");
exit(-1);
}
else if(child == 0) {
printf("I'm the child %d, my parent is %d\n", getpid(), getpid());
execl("./worker", "./worker", NULL);
}
else {
printf("I'm the parent %d, my child is %d\n", getpid(), child);
wait(NULL); // wait for child process to catch up
}
}
worker.c
int main(int argc, char *argv[])
{
printf("Hi, I'm the worker file!");
return 0;
}
First create the worker executable/binary as
gcc -Wall worker.c -o worker
Next, create the main executable and run it
gcc -Wall coordinator.c
./a.out
//code for foo (run executable as ./a.out)
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/wait.h>
int main (int argc, char **argv) {
pid_t pid;
pid = fork();
int i = 1;
char *parms[] = {"test2", "5", NULL}; //test executable named test2
if(pid < 0) {
fprintf(stderr, "Fork failed");
return 1;
}
else if(pid == 0) {
printf("Child pid is %d\n", pid);
i = execv("test2", parms); //exec call to test with a param of 5
}
else {
wait(NULL);
}
printf("I is now %d\n", i); //i is still 1 here, why?
return 0;
}
Hey everybody, I am trying to learn a little bit about fork and execv() calls. I make my foo.c program above make a call to a file I have named test.c. I fork a child and have the child make a call to execv, which will just add 10 to the parameter read in. I am unsure of why the variable does not change, at the bottom of my foo.c function. Does the call need to be a pointer or return an address? Any help would be greatly appreciated. Thanks
Code for test.c (executable named test2)
#include <stdio.h>
int main(int argc, char ** argv[]) {
int i = atoi(argv[1]);
i = i +10;
printf("I in test is %d\n", i);
return i;
}
You only call execv() in the child process. The exec() family functions never return if it runs successfully. See evec(3):
The exec() functions only return if an error has occurred. The return value is -1, and errno is set to indicate the error.
You printed the value of i in the parent process, it never changed in the parent process.
To get the exit status from the child process, you can make use of wait() or waitpid():
else {
int waitstatus;
wait(&waitstatus);
i = WEXITSTATUS(waitstatus);
}