tags:

views:

353

answers:

3

I'm trying to solve a problem I've got where a child process runs execvp() and needs to let the parent know if it returns. So, after the execvp() returns (because there's been an error), how can I tell the parent that this particular event has happened so it can handle it.

There's one method of writing a string of text through the pipe I'm using and then reading that from the parent.. but it seems a bit sloppy. Is there a better way?

Thanks!

Edit: Here is some code I'm trying where I can't seem to get the read to return.

int errorPipe[2];
signal( SIGPIPE, SIG_IGN );

int oldflags = fcntl (errorPipe[0], F_GETFD, 0);
oldflags |= FD_CLOEXEC;
fcntl (errorPipe[0], F_SETFD, oldflags);
oldflags = fcntl (errorPipe[1], F_GETFD, 0);
oldflags |= FD_CLOEXEC;
fcntl (errorPipe[1], F_SETFD, oldflags);

pipe( errorPipe );

// in the child..
char *error_message = "exec failed";
write( errorPipe[1], error_message, strlen(error_message)+1 );
exit(-1);

// in the parent
printf("read gives: %d\n", read(errorPipe[0], error_read, MAX_LINE_LENGTH) );
+2  A: 

execvp() never returns, except when it fails to even start the executable at all. If it can start the executable, it will not return, no matter what the executable does (i.e. regardless to whether the executable succeeds at its task or not).

Your parent process will receive a SIGCHLD signal, for which you can install a signal handler.

Or you can wait(2) for the child process.

int child_pid = fork();
if (child_pid == 0) {
    execvp("/path/to/executable", ...);
    exit(123); /* this happens only if execvp() fails to invoke executable */
}

/* ... */

int status = 0;
int exit_pid = waitpid(-1, &status, WNOHANG);
if (exit_pid == child_pid && WIFEXITED(status)) {
    if (WEXITSTATUS(status) == 0) {
        /* child process exited fine */
    } else if (WEXITSTATUS(status) == 123)
        /* execvp() itself failed */
    } else {
        /* executed child process failed */
    }
}
digitalarbeiter
I'm well aware that execvp() never returns when it is successful. I want to handle *only* the case that execvp fails. Not if a process runs and then exits.
Gary
exit() with a non-zero return code, as you will have to check the return code of the child in any case, i.e. also when execvp() works.
digitalarbeiter
what if another process could exit with that code? how will the parent tell the difference though?
Gary
digitalarbeiter
A: 

Cache the pid for the (child) process for which you want to examine the status in the parent. Add a handler for SIGCHLD in the parent. In the child call exit with some status value of your choosing to denote that execvp failed. On receiving the signal in the parent you now have 2 options
a) Call waitpid with a pid of -1 (i.e. wait for any child), examine the return value, if that matches your cached pid, examine the status using macros like WEXITSTATUS.
b) Call waitpid with your cached pid , then on return examine the exit status.

To make this robust you should call WIFEXITED(status) before examining the exit status via WEXITSTATUS. WIFEXITED returns true if the child terminated normally i.e. by calling exit or _exit and not as a result of seg fault, un handled signal etc. Also see man wait(2).

Jasmeet
+4  A: 

The easiest way is a pipe with the FD_CLOEXEC flag set, as then you can detect a successful exec as easily as a failure. In the event of a failure, I'd write whole the error message back to the parent over the pipe, but you could just write the status code or anything else that is meaningful. (Definitely write something though; nothing written has got to be a sign of a successful start of the other executable.)

[EDIT]: How to make use of this:

If the parent needs to wait until it knows whether the child successfully ran execve() (the unlying syscall) then it should do a blocking read() on the pipe. A zero result from that indicates success. (Make sure you've got SIGPIPE ignored.)

If the parent has some kind of event handling framework based on non-blocking IO and select() (or poll() or kqueue() or …) then wait for the pipe to become readable before trying to read the message (which will be zero-length if the child did the execve() correctly).

Donal Fellows
Somewhat hack-ish but pretty elegant. Not always possible, but +1 from me anyway.
jweyrich
Yep, elegant and actually answers the question.
caf
The interesting thing is that the questioner was already almost on top of the solution. And no, I don't know of a more elegant way; detecting success is the hard part, and I do know that the trick with a pipe is used in some production libraries.
Donal Fellows
thanks - this is very helpful :) just wondering though, what would be the best way to check if the pipe is closed in my parent? I could do a read from it, but if the pipe is open I would discard data that I want to get later.
Gary
@Gary: You can't check if the pipe is open as such. All you can do is wait (in one of the ways described in my edit) until something happens, at which point you've got to deal with it. Keep your message short in the error case (and don't use the pipe for anything else!) and you should be able to have either a complete read of an error message, or a zero-length read for success.
Donal Fellows
I've tried to do what you've said but read is still not returning when there is no error - can you take a look back at the top?
Gary
@Gary: You need to set *both* ends of the pipe to be `CLOEXEC`, not just the end you're reading from.
Donal Fellows
that didn't seem to do it yet. updated code is above. any other ideas?
Gary
No. I've checked against some production code that does this sort of thing, and that does exactly what I described.
Donal Fellows
Gary