Search code examples
cexecforksystem

C - using exec() instead of system()


In the following code:

int main ( int argc, char *argv[] ) {
    int i, pid, status;

    for(i = 0; i < atoi(argv[1]); i++) {
        pid = fork();
        if(pid < 0) {
            printf("Error occured");
            exit(1);
        } else if (pid == 0) {
            status = system("./frun test.txt 1");
            if (status != 0)
                printf("ERROR, exited with %d\n", status);
            else
                printf("SUCCESS, exited with %d\n", status); // 0
            exit(0); 
        } else  {
            wait(NULL);
        }
    }
    return 0;
}

I am using system() to run a program called 'frun' My question is - how would I change the code to use one of the exec() family functions instead?

My main problem is that I need to get the exit code of the program and I can't find a way to do it with exec(), while system() just returns the exit status.


Solution

  • Read the manual pages for wait and exec.

    You wait on the process created from forking and execing (recall that exec replaces the current process, so you must fork to get its exit code). This is from the man page for wait:

    Regardless of its value, this information may be interpreted using the following macros, which are defined in <sys/wait.h> and evaluate to integral expressions; the stat_val argument is the integer value pointed to by stat_loc.

    • WIFEXITED(stat_val)
      Evaluates to a non-zero value if status was returned for a child process that terminated normally.
    • WEXITSTATUS(stat_val)
      If the value of WIFEXITED(stat_val) is non-zero, this macro evaluates to the low-order 8 bits of the status argument that the child process passed to _exit() or exit(), or the value the child process returned from main().
    • WIFSIGNALED(stat_val)
      Evaluates to a non-zero value if status was returned for a child process that terminated due to the receipt of a signal that was not caught (see <signal.h>).
    • WTERMSIG(stat_val)
      If the value of WIFSIGNALED(stat_val) is non-zero, this macro evaluates to the number of the signal that caused the termination of the child process.
    • WIFSTOPPED(stat_val)
      Evaluates to a non-zero value if status was returned for a child process that is currently stopped.
    • WSTOPSIG(stat_val)
      If the value of WIFSTOPPED(stat_val) is non-zero, this macro evaluates to the number of the signal that caused the child process to stop.
    • WIFCONTINUED(stat_val)
      Evaluates to a non-zero value if status was returned for a child process that has continued from a job control stop.

    In your case you'd probably want to use WEXITSTATUS(stat_val) to get the 8 bit exit code.

    You call waitpid(pid, stat_loc, options) where you'd pass the pid returned from fork(), a pointer to a local int (where the status is stored), and your options flags (or 0). You would do this is in the branch where pid != 0, since this is the original process. The original process calling fork() (where pid == 0) would call exec and thus be replaced by the exec'd command.

    Here is a pseudo-example that you can adapt to your code:

    pid_t pid;
    int status;
    pid = fork();
    if (pid == 0)
    {
            exec(/*look up the different functions in the exec family and how to use them*/);
    }
    else if (pid < 0)
    {
            //uh oh
    }
    else
    {
            waitpid(pid, &status, 0);
            printf("Exited with %d\n", WEXITSTATUS(status));
    }
    

    Though you should check the result of waitpid.