C - program is terminated (fork() and exec()) - c

I have the following code:
int main(int argc, char **argv)
{
char *program;
char stringa[1000] = "";
int num = 123;
char snum[5];
program = argv[1];
sprintf(stringa, "./%s", program);
pid_t pid = fork();
if (pid < 0 ) {
perror("fork failed.");
exit(1); }
else if (pid == 0) {
char* args[] = {stringa, NULL};
execv(args[0], args);
}
else {
char procmon_str[] = "./procmon ";
num = pid;
sprintf(snum, "%d",num);
printf("PID of child is %s", snum);
char* args2[] = {procmon_str, snum, NULL};
execv(args2[0], args2);
sleep(20);
kill(num, SIGTERM);
sleep(2);
int parent_pid = getpid();
printf("PID of parent is %d", parent_pid);
kill(parent_pid, SIGTERM);
}
wait(NULL);
return 0;
}
The idea is to call with program with 1 command line argument which is a name of another compiled C program in the same folder.
I want to execute that program from within the C code (hence the use of fork()), and at the same time i want to launch another program from within the parent part of the fork().
The part that is in the child part of fork() works perfectly, but when i run it through the shell it says Terminated right after and does not execute the code in the parent part of the fork().
Why is that?

Your program call fork(). Now the execution of the parent process and the child process proceeds in parallel.
The child:
Builds the argument array args[].
Calls execv() and is replaced by the program supplied as argument.
The parent, in parallel with the child:
Builds the argument array args2[].
Calls execv() and is replaced by ./procmon.
The code from sleep(20) onwards in not reached unless the execv() fails (which you did not check for).
Read the manual page for fork() again, and redo the logic of the program.

Related

xv6 Start new process and exec user program

I'm trying to fork() a new process and exec() the the user program in newly created process.
This code works in user space. I'm not sure how to do the similar thing in kernel space. As per my debugging, seems like fork() never returns 0 in kernel space.
Additional question: How can I execute the user program from kernel space. Is it possible to do that with exec()?
int
main(int argc, char *argv[])
{
int pid = fork();
if(pid == 0){
// child
char *argv[2];
argv[0] = "echo";
argv[1] = "CHILD";
exec("/bin/echo", argv);
exit();
}
if(pid > 0) {
wait();
printf("parent");
}
else {
printf("ERROR");
}
exit();
}

Execvp linux: trying to make my shell work in C

I'm trying to make simple shell, but with a specific condition, I have to use the following structure:
typedef struct cmd_struct{
char cmd[80];
char args[10][80];
int nargs;
} cmd_type;
Inside cmd I will save de main command and arguments in args.
Then I read from a file, different commands, and save them into and array of cmd_type. My program or fake shell, ask for a number and should take it from this array.
My function that executes the command looks like:
void execCmd(cmd_type* cmds_arg, int idxCmd){
pid_t pid;
printf("Father: my pid is %d\n", getpid());
char* buff;
pid = fork();
if (pid == 0) {
printf("Child process: My pid is %d\n", getpid());
printf("-------------- Child doing exec: %s\n", cmds_arg[idxCmd].cmd);
execvp(cmds_arg[idxCmd].cmd,&cmds_arg[idxCmd].args);
_exit(2);
_exit(1);
}
printf("Father: Gonna wait for Child\n");
int status;
wait(&status);
printf("-------------- Father: Child finished\n");
// WIFEXITED, WEXITSTATUS Macro of the gnu lib POSIX standard to recover end status
if ( WIFEXITED(status) ) {
const int es = WEXITSTATUS(status);
printf("Father: Child Complete with exit status %d\n", es);
if(es == 1) printf("Father: Child didn't execute any command\n");
else if(es == 2) printf("Father: Child command was not found\n");
}
}
As you can see, when I'm calling the execvp() system call, I'm doing it wrong. First argument I think that it is right, second one it's totally wrong.
First of all, I have a conversion problem right there, and the second problem is that the array should contain "main command", "arg1", "arg2" ... and mine only has the arguments. Am I wrong?
Is there a way to add the "main command" using services like sscanf ()? And the most important part, do I have any chance to make it work this way?
With char args[10][80];, execvp(cmds_arg[idxCmd].cmd,&cmds_arg[idxCmd].args); won't work (it won't even compile), because execvp needs a const char* pointer to each argument, and your .args doesn't have any pointers.
Something like this may work:
const char *p[11]; /* Contains up to 10 pointers + trailing NULL. */
cmd_type *this_cmd = &cmds_arg[idxCmd];
for (int i = 0; i < this_cmd->nargs; ++i) {
p[i] = &this_cmd->args[i];
}
p[this_cmd->nargs] = NULL;
execvp(this_cmd->cmd, p);
In the same spirit of the answer from #pts, you can copy the parameters for execvp() in a dynamically allocated table:
void execCmd(cmd_type* cmds_arg, int idxCmd){
pid_t pid;
printf("Father: my pid is %d\n", getpid());
char* buff;
pid = fork();
if (pid == 0) {
int i;
char **args = (char **)malloc((1 + cmds_arg[idxCmd].nargs + 1) * sizeof(char *));
args[0] = cmds_arg[idxCmd].cmd;
for (i = 1; i < (cmds_arg[idxCmd].nargs + 1); i ++) {
args[i] = cmds_arg[idxCmd].args[i - 1];
}
args[i] = NULL;
printf("Child process: My pid is %d\n", getpid());
printf("-------------- Child doing exec: %s\n", cmds_arg[idxCmd].cmd);
execvp(cmds_arg[idxCmd].cmd, args);
_exit(2);
}
printf("Father: Gonna wait for Child\n");
int status;
wait(&status);
printf("-------------- Father: Child finished\n");
// WIFEXITED, WEXITSTATUS Macro of the gnu lib POSIX standard to recover end status
if ( WIFEXITED(status) ) {
const int es = WEXITSTATUS(status);
printf("Father: Child Complete with exit status %d\n", es);
if(es == 1) printf("Father: Child didn't execute any command\n");
else if(es == 2) printf("Father: Child command was not found\n");
}
}

Two way communication from the file that run with exec in child

I am creating a child process with a fork in program y. In that child, I run another program with exec, in which I want the function in that program (let's call it program x) to return something to me. Is there a way to pass this returned value to the parent?
I provided some sort of a pseudo-code that demonstrates what I want to do below.
program.x:
int main(int argc, char** argv)
{
if(argc != 2)
{
printf("argument count does not match\n");
return -1;
}
printf("task1!\n");
...
char *value = "want this"; // how to pass this to the parent in the program y?
...
}
program y:
int main(int argc, char *argv[])
{
int fd[2];
pipe(fd);
pid_t p;
p = fork();
if(p==-1)
{
printf("There is an error while calling fork()");
}
if(p==0)
{
printf("We are in the child process\n");
printf("Calling hello.c from child process\n");
char *args[] = {"Hello", "C", "Programming", NULL};
execv("./hello", args);
close(fd[0]);
write(fd[1], ???, ??);
close(fd[0]);
}
else
{
printf("We are in the parent process");
wait(NULL);
close(fd[1]);
read(fd[0], ???,???);
close(fd[0]);
}
return 0;
}
The only thing you can pass directly is the exit code of the child (via wait()).
To pass a string between two processes, you need an IPC data structure like pipes. See the pipe() function in unistd.h.
For the simple case where there is one-way communication from a child to a parent), you can use popen. It's high level, simple to use, and has little overhead (if any) over fork/exec
int main(...)
{
...
FILE *fp = popen("./hello 'Hello', 'C', 'Programming'", "r") ;
char resp[200] ;
if ( fgets(resp, sizeof(resp, fp) ) {
// Do something
}
int result = pclose(fp) ;
}
Note that the way to pass command line arguments follows shell rules - arguments may need to be quoted (usually, single quote) to pass any special characters.
The 'pclose' result is the exit code of the executed program.

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 won't call my second program

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

Resources