2

I am a little confused about how to handle errors from execvp(). My code so far looks like this:

int pid = fork();
if (pid < 0) {
    // handle error.
}
else if (pid == 0) {
    int status = execvp(myCommand,myArgumentVector); // status should be -1 because execvp
                                                     // only returns when an error occurs
    // We only reach this point as a result of failure from execvp
    exit(/* What goes here? */);
}
else {
    int status;
    int waitedForPid = waitpid(pid,&status,0);
    //...
}

There are three cases I'm trying to address:

  1. myCommand,myArgumentVector are valid and the command executes correctly.
  2. myCommand,myArgumentVector are valid parameters, but something goes wrong in the execution of myCommand.
  3. myCommand,myArgumentVector are invalid parameters (e.g. myCommand cannot be found) and the execvp() call fails.

My primary concern is that the parent process will have all the information it needs in order to handle the child's error correctly, and I'm not entirely sure how to do that.

In the first case, the program presumably ended with an exit status of 0. This means that if I were to call WIFEXITED(status) in the macro, I should get true. I think this should work fine.

In the second case, the program presumably ended with an exit status other than 0. This means that if I were to call WEXITSTATUS(status) I should get the specific exit status of the child invocation of myCommand (please advise if this is incorrect).

The third case is causing me a lot of confusion. So if execvp() fails then the error is stored in the global variable errno. But this global variable is only accessible from the child process; the parent as an entirely separate process I don't think can see it. Does this mean that I should be calling exit(errno)? Or am I supposed to be doing something else here? Also, if I call exit(errno) how can I get the value of errno back from status in the parent?

My grasp is still a little tenuous so what I'm looking for is either confirmation or correction in my understanding of how to handle these three cases.

Kvass
  • 8,294
  • 12
  • 65
  • 108

4 Answers4

3

Here is a simple code that I've tried.

if(fork() == 0){
   //do child stuff here
   execvp(cmd,arguments); /*since you want to return errno to parent
                            do a simple exit call with the errno*/
   exit(errno);
}
else{                
    //parent stuff
    int status;
    wait(&status);       /*you made a exit call in child you 
                           need to wait on exit status of child*/
    if(WIFEXITED(status))
         printf("child exited with = %d\n",WEXITSTATUS(status));
                              //you should see the errno here
}
Raju Kunde
  • 982
  • 1
  • 8
  • 18
  • 2
    This was tested in the case that `cmd` is invalid and results in an `execvp` failure? And does this mean that if I `exit(errno)` that I can access the value of errno by `WEXITSTATUS(status)`? – Kvass Nov 19 '13 at 05:58
  • `WIFEXITED` macro returns a nonzero value if the child process terminated normally with `exit` or `_exit`. – Raju Kunde Nov 19 '13 at 06:05
  • Thanks, but not what I asked. – Kvass Nov 19 '13 at 06:06
  • Okay.If `WIFEXITED` is true of status, `WEXITED(status)` macro returns the low-order 8 bits of the `exit` status value from the child process. – Raju Kunde Nov 19 '13 at 06:08
  • 1
    is that where errno would have been stored? If the child runs `exit(errno)` what exactly will the value of status be, broken down into its different bit components? – Kvass Nov 19 '13 at 06:11
  • Go through [this](http://www.gnu.org/software/libc/manual/html_node/Process-Completion-Status.html) – Raju Kunde Nov 19 '13 at 06:11
  • From the link given by @RajuKunde, I still can't figure out how you would retrieve errno if it were a number larger than 8 bits (>=256) – Pro Q Nov 24 '18 at 13:01
  • Actually, the exit status bits are normally the high-order 8 bits of a 16-bit exit value (dating back to an era when 16-bit `int` was normal — that's a _long_ time ago). You can use `printf("0x%.4X\n", status);` to see the numbers. The low-order 8 bits capture the signal number (usually). – Jonathan Leffler Aug 19 '19 at 16:32
  • See [ExitCodes bigger than 255 — Possible?](https://stackoverflow.com/questions/179565/exitcodes-bigger-than-255-possible) — and also take a close look at [`sigaction()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html) and the discussion of [`sigaction()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html) and [Signal Actions](https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03). – Jonathan Leffler Aug 19 '19 at 16:43
2

In case 1, the execvp() does not return. The status returned to the parent process will be the exit status of the child — what it supplies to exit() or what it returns from main(), or it may be that the child dies from a signal in which case the exit status is different but detectably so (WIFSIGNALED, etc). Note that this means that the status need not be zero.

It isn't entirely clear (to me) what you are thinking of with case 2. If the command starts but rejects the options it is called with, it is actually case 1, but the chances of the exit status being zero should be small (though it has been known for programs to exit with status 0 on error). Alternatively, the command can't be found, or is found but is not executable, in which case execvp() returns and you have case 3.

In case 3, the execvp() call fails. You know that because it returns; a successful execvp() never returns. There is no point in testing the return value of execvp(); the mere fact that it returns means it failed. You can tell why it failed from the setting of errno. POSIX uses the exit statuses of 126 and 127 — see xargs and system() for example. You can look at the error codes from execvp() to determine when you should return either of those or some other non-zero value.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • An example of case 2 would be something like the command `cd foobar` where `foobar` is not a valid directory. Exec finds the command `cd` and runs it (the arguments are valid), but then an error occurs in the execution of the `cd` program. An example of case 3 would be something like `foobar` where `foobar` is simply not a valid program, and so the actual `execvp` call itself fails. – Kvass Nov 19 '13 at 06:26
  • Essentially what I'm wondering with #3 is in the event that execvp fails, how can I let the parent know why? Am I supposed to exit with errno from the child? – Kvass Nov 19 '13 at 06:30
  • Hmmm...well, to the extent that you can find a command `cd` (it is a shell built-in for very good reasons, but there is a `/usr/bin/cd` on Mac OS X), your example of case 2 is really case 1: the command was executed but the execution failed. The `execvp()` does not return; the executed command exits with some (non-zero) status. Case 3 is readily understandable; you can identify it because `execvp()` might return ENOENT. In case 3, you exit with an appropriate non-zero exit status. Most of my code prints an error message but exits with status 1, the generic 'something went wrong' status. – Jonathan Leffler Nov 19 '13 at 06:31
1

In the third case, errno IS accessible from the parent as well, so you could just exit(errno). However, that is not the best thing to do, since the value of errno could change by the time you exit.

To be more sure that you don't lose errno if you have code between your exec() and exit() calls, assign errno to an int:

execvp(<args>);

int errcode=errno;

/* other code */

exit(errcode);

As for your other question, exit status is not directly comparable to the errno, and you shouldn't be trying to retrieve errno from anything but errno (as above) anyway.

This documentation may help: http://www.gnu.org/software/libc/manual/html_node/Exit-Status.html

laalto
  • 150,114
  • 66
  • 286
  • 303
achan
  • 13
  • 4
  • So say I do `exit(errcode)` with the errno value that resulted from execvp. Now in the parent `waitpid(pid,&status,0)` has assigned to the `status` variable some value. Is that value equivalent to `errcode`? Or if not in what way is it related? – Kvass Nov 19 '13 at 05:56
  • Also when you say errno is accessible from the parent, you mean only by doing `exit(errcode)`, right? Or are global variables shared across processes...? – Kvass Nov 19 '13 at 06:12
  • They're unrelated in a bash system call. An exec call could have multiple errors, with the errno changing many times, and the final errno does not have to relate to the exit status. This is evident from the range of return values- 8 bit for status, and a modifiable lvalue of type int for errno. When execvp terminates (presumably with an exit() or equivalent, it does not return exit(errno)). So the status does not correspond to the errno. – achan Nov 19 '13 at 06:21
0

Here is my code: it works fine. Child status is returned in waitpid hence it tells either child process is executed successfully or not. //declaration of a process id variable pid_t pid, ret_pid;

//fork a child process is assigned 
//to the process id
pid=fork();

DFG_STATUS("Forking and executing process in dfgrunufesimulator %d \n", pid);

//code to show that the fork failed
//if the process id is less than 0
if(pid<0)
{
    return DFG_FAILURE;
}
else if(pid==0)
{
    //this statement creates a specified child process
    exec_ret = execvp(res[0],res);  //child process

DFG_STATUS("Process failed ALERT exec_ret = %d\n", exec_ret);
exit(errno);

}
//code that exits only once a child 
//process has been completed
else
{
  ret_pid = waitpid(pid, &status, 0);
  if ( ret_pid == -1) 
  { 
    perror("waitpid"); 
    return DFG_FAILURE; 
  }

   DFG_STATUS("ret_pid = %d, pid = %d, child status = %d\n",ret_pid,pid,status);


  if (WIFEXITED(status)) {
        DFG_STATUS("child exited, status=%d\n", WEXITSTATUS(status));


    } else if (WIFSIGNALED(status)) {
        DFG_STATUS("child killed (signal %d)\n", WTERMSIG(status));


    } else if (WIFSTOPPED(status)) {
        DFG_STATUS("child stopped (signal %d)\n", WSTOPSIG(status));


#ifdef WIFCONTINUED     /* Not all implementations support this */
    } else if (WIFCONTINUED(status)) {
        DFG_STATUS("child continued\n");
#endif
    } else {    /* Non-standard case -- may never happen */
        DFG_STATUS("Unexpected status (0x%x)\n", status);
    }

  if(status != 0) /* Child process failed to execute */
    return DFG_FAILURE;

  result = DFG_SUCCESS;
}
usman1947
  • 9
  • 5