views:

814

answers:

6

I do the regular thing:

  • fork()
  • execvp(cmd, ) in child

If execvp fails because no cmd is found, how can I notice this error in parent process?

+7  A: 

You terminate the child (by calling _exit()) and then the parent can notice this (through e.g. waitpid()). For instance, your child could exit with an exit status of -1 to indicate failure to exec. One caveat with this is that it is impossible to tell from your parent whether the child in its original state (i.e. before exec) returned -1 or if it was the newly executed process.

As suggested in the comments below, using an "unusual" return code would be appropiate to make it easier to distinguish between your specific error and one from the exec()'ed program. Common ones are 1, 2, 3 etc. while higher numbers 99, 100, etc. are more unusual. You should keep your numbers below 255 (unsigned) or 127 (signed) to increase portability.

Since waitpid blocks your application (or rather, the thread calling it) you will either need to put it on a background thread or use the signalling mechanism in POSIX to get information about child process termination. See the SIGCHLD signal and the sigaction function to hook up a listener.

You could also do some error checking before forking, such as making sure the executable exists.

If you use something like Glib, there are utility functions to do this, and they come with pretty good error reporting. Take a look at the "spawning processes" section of the manual.

Isak Savo
isn't the exit status 8 bit unsigned? (-1 -> 255)
roe
It's just 8 bits of data. It doesn't matter whether you interpret them as signed of unsigned. (just be consistent) I'm not sure what the POSIX standard says, but it's quite common to return negative values from main() to indicate error
Isak Savo
Won't waitpid() block my parent process?
Łukasz Lew
Yes it will, there's also a SIGCHLD signal that you can trap in your application. That will make the OS call your function whenever one of your child applications terminate. You can from there examine the return value. Use the signal or sigaction calls to listen to that signal (it's ignored by default)
Isak Savo
Naturally, signed 8bit will turn 255 into -1, but I guess most people would use an int, which commonly carries 32 bits turning your -1 unsigned into 255 signed.. Just a thought, maybe it doesn't happen.
roe
also you can speficy a timeout (which can be zero), which can be used for polling, if you don't want to rely on sigchld.
roe
Suggestion: use an unusual exit status - 99 or something along those lines - to indicate failure to exec. While the executed process might exit with that same exit status, it is pretty improbable. Using 0, 1, -1, 2 is a bit risky; those statuses are used rather regularly. Note that the shell maps the exit status of a process that dies as a result of a signal to '128+signal-number'. Try 'sleep 30' and interrupt it and then 'echo $?'; I get exit status 130 (which is expected because SIGINT = 2). So, avoid exit statuses 128..160 or thereabouts.
Jonathan Leffler
Good suggestion about unusal status Jonathan. Will update my post to include this.Regarding SIGINT, you can (and SHOULD!) avoid that by using the WIFEXITED() macro. This evaluates to TRUE if the process died a normal death and FALSE otherwise (e.g. killed by a signal)
Isak Savo
Don't use exit() after fork() - it can hang the process on some platforms - use _exit().
Douglas Leeder
Good call Douglas, updated my response
Isak Savo
A: 

Well, you could use the wait/waitpid functions in the parent process. You can specify a status variable that holds info about the status of the process that terminated. The downside is that the parent process is blocked until the child process finishes execution.

Alex Ciminian
have a look at sigchld if you don't want to block the execution.
roe
A: 

Not should you wonder how you can notice it in parent process, but also you should keep in mind that you must notice the error in parent process. That's especially true for multithreaded applications.

After execvp you must place a call to function that terminates the process in any case. You should not call any complex functions that interact with C library (such as stdio), since effects of them may mingle with pthreads of libc functionality of parent process. So you can't print a message with printf() in child process and have to inform parent about the error instead.

The easiest way, among the other, is passing return code. Supply nonzero argument to _exit() function (see note below) you used to terminate the child and then examine the return code in the parent. Here's the example:

int pid, stat;
pid = fork();
if (pid == 0){
   // Child process
   execvp(cmd);
   if (errno == ENOENT)
     _exit(-1);
   _exit(-2);
}

wait(&stat);
if (!WIFEXITED(stat)) { // Error happened 
...
}


Instead of _exit(), you might think of exit() function, but it's incorrect, since this function will do a part of the C-library cleanup that should be done only when parent process terminates. Instead, use _exit() function, that doesn't do such a cleanup.

Pavel Shved
Don't use exit() after fork, use _exit().
Douglas Leeder
@Douglas Leeder , thank you for pointing out this huge flaw. I fixed the post.
Pavel Shved
A: 

1) Use _exit() not exit() - see http://opengroup.org/onlinepubs/007908775/xsh/vfork.html - NB: applies to fork() as well as vfork().

2) The problem with doing more complicated IPC than the exit status, is that you have a shared memory map, and it's possible to get some nasty state if you do anything too complicated - e.g. in multithreaded code, one of the killed threads (in the child) could have been holding a lock.

Douglas Leeder
+4  A: 
ephemient
This solution, quite simply, rocks.
caf
A: 

Hi Pavel. It seems I may finally get help to one of the subtle questions disturbng me.

"After execvp you must place a call to function that terminates the process in any case".

Well, "execvp()" replaces the current program with the to-be-execed program (of course in the same process context). So, putting, say, any printf() calls after execvp() won't work. That is what the docs say, and I have verified it as well.

But then, why is _exit() needed..? Does it so happen that the control DOES return to statements post execvp() ?

I will be grateful for any pointers.

Thanks

Ajay Garg