Invalid commands for child process in background in C

278 views Asked by At

I have the following code in C:

        if ((childpid = fork()) == 0) {
            if (execvp(argv[0], argv) < 0) {
                //execute failed
                exit(1);
            }
        } else if (childpid < 0) {
            //fork failed
        } else {
            //if execvp failed don't do anything here
            //else do something
        }

What I want is:

I enter a command.

If it is not executable it should not do anything but wait for my next entered command.

If it is executable it should do some things in the parent process.

If I enter e.g. sleep 1m it should execute it in my child process, do things in the parent process and should be still able to execute more jobs (this works fine). But when I execute something like abcdef (invalid command) it does the stuff in my parent process anyway.

Can someone tell me how the code should look like?

I also tried the following:

void signalHandler(int signal)
{
    if (signal==SIGCHLD) {
        printf("Child ended\n");
        wait(NULL);
    }
}
//in main
signal(SIGCHLD,signalHandler);
//...
    if ((childpid = fork()) == 0) {
        if (execvp(t_argv[0], t_argv) < 0) {
            kill(getppid(),SIGCHLD);
        }
    }

Is this correct? This way I get an error afterwards (when it's finished).

waitpid(childpid, &status, WNOHANG)

tells me it finished with an error (-1).

2

There are 2 answers

3
Some programmer dude On

One possible solution is to use a pair of anonymous pipes, where the child process writes in the write-end of the pipe any status it needs to pass on to the parent. Then in the parent you check the read-end of the pipe, if you don't receive anything before the child-process exits then everything was okay and the child process successfully executed the program.

If the parent does receive anything before the child process exits, then it means that the exec call failed.

5
Filipe Gonçalves On

One possible solution is to terminate abnormally with a signal (for example, SIGUSR1) and check for that in the parent. This assumes that whatever program you execute in the child never terminates with SIGUSR1 - a reasonable assumption in most cases, I'd say. The parent can then check the termination status of the child.

#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {

    pid_t childpid;

        if ((childpid = fork()) == 0) {
            if (execvp(argv[1], &argv[1]) < 0) {
                raise(SIGUSR1);
            }
        } else if (childpid < 0) {
            perror("fork()");
            exit(EXIT_FAILURE);
        } else {
            int term_status;
            if (wait(&term_status) < 0) {
                perror("wait()");
                exit(EXIT_FAILURE);
            }

            if (WIFSIGNALED(term_status) && WTERMSIG(term_status) == SIGUSR1) {
                printf("execvp failed\n");
            } else {
                printf("success\n");
            }
        }

    return 0;
}

Side note: you probably want execvp(argv[1], &argv[1]), because execvp(argv[0], argv) will execute this same program over and over.

Again, this works as long as the process executed by execvp(2) never terminates with SIGUSR1. Notice that if the process executed by execvp(2) terminates with SIGSEGV or other abnormal termination condition, it is still seen as success by the parent.