Handling errors from execvp() - c

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:
myCommand,myArgumentVector are valid and the command executes correctly.
myCommand,myArgumentVector are valid parameters, but something goes wrong in the execution of myCommand.
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.

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
}

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.

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

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;
}

Related

`waitpid()' always returns -1

I'm executing the code below and the call to waitpid() always returns -1, thus the code bellow ends with an infinite loop. The call works if I replace WNOHANG with 0.
void execute(cmdLine* pCmdLine) {
int status = 0;
pid_t pid = fork();
if(pid == 0) {
if(execvp(pCmdLine->arguments[0], pCmdLine->arguments) == -1) {
if(strcmp(pCmdLine->arguments[0], "cd") != 0) {
perror("execute failed\n");
}
_exit(1);
}
} else {
if(pCmdLine->blocking == 1) {
waitpid(pid, &status, 0);
}
while(waitpid(pid, &status, WNOHANG) == -1) {
printf("still -1\n");
}
}
}
}
Well, you have misunderstood the workings of the wait system call.
As with malloc/free, you can only successfully waitpid() only once per fork()ed process... so the while loop is never necessary if you are going to wait for the exit code of the child, you have to call it only once. Wait will only return -1 in your case because of two reasons:
fork() didn't succeed, so you are waiting for an invalid pid. Indeed, you should be calling wait() for pid == -1, which is invalid. In case you wait() and there's no process to be waited for (in case the pid variable has a positive number, but of an already wait()ed subprocess, you also get -1), you get an error from any of the wait() family of system calls. The mission of zombie processes in UN*X systems is just this, to ensure that a wait() for an already finished child is still valid and the calling process gets the exit code signalled by the child on exit().
You expressely say you are not going to wait for the process to finish. It should be clear that if you are not going to wait for the process to terminate, this is what you are doing with the WNOHANG parameter, then the child process can be still running (which is your case) and had not yet done an exit() syscall. You only want the exit code in case the child process has already finished. If this is the case, then you had better to write:
while(waitpid(pid, &status, WNOHANG) == -1 && errno == EAGAIN)
do_whatever_you_want_because_you_decided_not_to_wait();
The wait system call has no way to tell you that the &status variable has not been filled with the exit code of the child process than signalling an error, and in that case, it always sets errno to EAGAIN.
but, from my point of view, if you have nothing to do in the meanwhile, then you had better not to use WNOHANG. That will save cpu cycles and a lot of heat energy thrown to the environment.
Here
while(waitpid(pid,&status,WNOHANG)==-1) { }
when if there is no more child process exists then waitpid returns -1 and it makes while(true) always and that cause infinite loop.
From the manual page of waitpid().
waitpid(): on success, returns the process ID of the child whose
state has changed; if WNOHANG was specified and one or more
child(ren) specified by pid exist, but have not yet changed state,
then 0 is returned. On error, -1 is returned.
That means, when there are no more child to wait for, it returns -1. So either make it like
if() { /* child process. can be multiple */
}
else { /* parent process */
while(waitpid(pid,&status,WNOHANG) != -1) { /* when there is no more child process exists then it terminate */
}
}
or
if() { /* child process. can be multiple */
}
else { /* parent process */
while(waitpid(pid,&status,WNOHANG) == -1); /* dummy while ..when there is no more child process exists then it terminate */
}

Get returned value of a child process without holding parent execution

I need to be able to get the returned value from a child process without having to hold the execution of the parent for it.
Notice the a runtime error could happen in the child process.
Here is my program that I'm trying to make:
//In parent process:
do
{
read memory usage from /proc/ID/status
if(max_child_memory_usage > memory_limit)
{
kill(proc, SIGKILL);
puts("Memory limit exceeded");
return -5; // MLE
}
getrusage(RUSAGE_SELF,&r_usage);
check time and memory consumption
if(memory limit exceeded || time limit exceeded)
{
kill(proc, SIGKILL);
return fail;
}
/*
need to catch the returned value from the child somehow with
this loop working.
Notice the a runtime error could happen in the child process.
*/
while(child is alive);
The waitpid function has an option called WNOHANG which causes it to return immediately if the given child has not yet returned:
pid_t rval;
int status;
do {
...
rval = waitpid(proc, &status, WNOHANG);
} while (rval == 0);
if (rval == proc) {
if (WIFEXITED(status)) {
printf("%d exited normal with status %d\n", WEXITSTATUS(status));
} else {
printf("%d exited abnormally\n");
}
}
See the man page for waitpid for more details on checking various abnormal exit conditions.
The solutions using the WNOHANG flag would work only if you need to check just once for the exit status of the child. If however you would like to procure the exit status when the child exits, no matter how late that is, a better solution is to set a signal handler for the SIGCHLD signal.
When the child process terminates whether normally or abnormally, SIGCHLD will be sent to the parent process. Within this signal handler, you can call wait to reap the exit status of the child.
void child_exit_handler(int signo){
int exit_status;
int pid = wait(&exit_status);
// Do things ...
}
// later in the code, before forking and creating the child
signal(SIGCHLD, child_exit_handler);
Depending on other semantics of your program, you may want to use waitpid instead. (SIGCHLD may also be called if the program was stopped, not terminated. The man page for wait(2) describes macros to check for this.)

waitpid - In which cases both WIFEXITED and WIFSIGNALED will be false?

I'm running a Java program as a daemon on Linux using Apache commons-daemon's jsvc.
The daemon "randomly" crashes with only message:
jsvc.exec error: Service did not exit cleanly
This is the relevant part of the code in jsvc (in jsvc-unix.c line 1142):
while (waitpid(pid, &status, 0) != pid) {
/* Waith for process */
}
/* The child must have exited cleanly */
if (WIFEXITED(status)) {
status = WEXITSTATUS(status);
// Clean exit code...
}
else {
if (WIFSIGNALED(status)) {
log_error("Service killed by signal %d", WTERMSIG(status));
/* prevent looping */
if (laststart + 60 > time(NULL)) {
log_debug("Waiting 60 s to prevent looping");
sleep(60);
}
continue;
}
log_error("Service did not exit cleanly", status);
return 1;
}
In which case can WIFEXITED and WIFSIGNALED both be false ?
Is it guaranteed that the process was not killed in this case (by a process or Linux OOM killer) ?
WIFSTOPPED exists too, but it's only possible if the parent is ptrace:ing the child process (or with different flags to waitpid).
I think your best bet is to print the status and the look at the bits in sys/wait.h. It's quite hard to get it right though. Much information is being stuffed into that int and it's hard to figure it out. It looks like the code you pasted already tries to do that, but forgot the %d in the format string.

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.
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.

What does the second parameter of waitpid() mean?

From a existing question here, someone gave this example code:
int status;
child_pid = fork();
if (child_pid == 0) {
// in child; do stuff including perhaps exec
} else if (child_pid == -1) {
// failed to fork
} else {
if (waitpid(child_pid, &status, 0) == child_pid) {
// child exited or interrupted; now you can do something with status
} else {
// error etc
}
}
Could anyone explain to me what the second parameter of waitpid() is used for?
from man pages :
If status is not NULL, wait() and waitpid() store status infor-
mation in the int to which it points. This integer can be
inspected with the following macros (which take the integer
itself as an argument, not a pointer to it, as is done in wait()
and waitpid()!):
WIFEXITED(status)
returns true if the child terminated normally, that is,
by calling exit(3) or _exit(2), or by returning from
main().
WEXITSTATUS(status)
returns the exit status of the child. This consists of
the least significant 8 bits of the status argument that
the child specified in a call to exit(3) or _exit(2) or
as the argument for a return statement in main(). This
macro should only be employed if WIFEXITED returned true.
WIFSIGNALED(status)
returns true if the child process was terminated by a
signal.
WTERMSIG(status)
returns the number of the signal that caused the child
process to terminate. This macro should only be employed
if WIFSIGNALED returned true.
WCOREDUMP(status)
returns true if the child produced a core dump. This
macro should only be employed if WIFSIGNALED returned
true. This macro is not specified in POSIX.1-2001 and is
not available on some Unix implementations (e.g., AIX,
SunOS). Only use this enclosed in #ifdef WCOREDUMP ...
#endif.
WIFSTOPPED(status)
returns true if the child process was stopped by delivery
of a signal; this is only possible if the call was done
using WUNTRACED or when the child is being traced (see
ptrace(2)).
WSTOPSIG(status)
returns the number of the signal which caused the child
to stop. This macro should only be employed if WIF-
STOPPED returned true.
WIFCONTINUED(status)
(since Linux 2.6.10) returns true if the child process
was resumed by delivery of SIGCONT.
So it stores status of the "how the child terminated".
You can use the macros to investigate how exactly the child terminated, and you can define some actions depending to the child's termination status.
It is a bit-field for options, the only one available is WNOWAIT, which means to leave the child in a waitable state; a later wait call can be used to again retrieve the child status information.
See: http://linux.die.net/man/2/waitpid
pid = fork();
if(pid < 0)
{
printf("fork failed\n");
return -1;
}
else if(pid == 0)
{
sleep(5);
printf("Child process\n");
return 2;
}
else
{
printf("Parent process\n");
kill(pid, SIGKILL);
waitpid(pid, &ret, 0);
if(WIFEXITED(ret))
printf("Child process returned normally\n");
if(WIFSIGNALED(ret))
printf("Child process terminated by signal\n");
return 1;
}
As you can see that the return value can be used to check how a particular process terminated and take actions on the basis of that.
If you comment the kill line from the code, the child process will terminate properly.

Resources