1

I created multiple child processes by fork() and had them run executable file by execl().

I want to check if there is any execl() that is failed (ex: try to execute non-exist file). By, try to execl() all the programs and if one of them is failed then return 1 before start communicates with any programs.

here is reference code, (assume all the setups are correct)

#DEFINE NUMBEROFCHILD 4
char** exeFile = {"./p1", "./p2", "./p3", "nonexist"); //"nonexist" is a non-exist program
int main(int argc, char** argv){
     pid_t childPID [numberOfChild];

     for (int i = 0; i<numberOfChild; i++) {
          //setting up pipes
          pid_t childPID[i] = fork();

          if(childPID[i] < 0) {
               //close all pipes and quit
          }else if (childPID[i] == 0) {
               //redirect pipe
               execl(exeFile[i],"args",(char*)0;
               return 1; 
               //I'm expecting this to return when it try to execute "nonexist"
               //but it didn't and keep running successed execl()
          }else {
               //close un-use pipes
          }
     }

     while(true) {
          for (int i = 0; i<numberOfChild; i++) {
               //communicate with childs through pipe
          }
     }

     for (int i = 0; i<numberOfChild; i++) {
          //close reminded pipes
          if (waitpid(childPID[i], NULL, 0) == -1){
               return 1;
          }
     }
     return 0;
}

This program still sent message to "nonexist" (but didn't receive anything back from it as expect).

Are there anyways to achieve my goal? Thank you!

Tran Van Boi
  • 27
  • 1
  • 7
  • Put a `printf` before the `return 1` to see that the `execl` failed. Keep in mind that you're in the child process at that point, so you'll need to use the pipes to communicate back to the parent, or send a signal like `kill(getppid(), SIG_USER1)`, or use any number of other interprocess communication facilities. – user3386109 Sep 29 '16 at 19:41
  • The `exec*` family of functions return on error -1, and `errno` is set to indicate the error. Check the return value of `execl ` (basically `execl` will only return on error). – Pablo Sep 29 '16 at 19:41
  • When the executable file is not found, `execl` will return 2 (`ENOENT`) – Pablo Sep 29 '16 at 19:48
  • Please post compilable code (`execl(exeFile[i],"args",(char*)0;` is not correct, is it?). The `execl()` function only returns if the exec process fails. Without setup work, the only ways for the parent to know that the child failed are to wait for the process (perhaps with `waitpid(pid, &status, WNOHANG)`) or to try signalling it (`kill(0, pid)` sees whether the pid is available). Both these could run into scheduling false positives: the child hasn't yet tried the `execl()` but it will fail when it does. – Jonathan Leffler Sep 29 '16 at 19:48
  • @Jonathan: I wrote a 2 line test code `printf("errno = %d (%s)\n", errno, strerror(errno));` to get the error number. – Pablo Sep 29 '16 at 19:51
  • 1
    @Pablo: there's no need to check the return value from `execl()` or any of the `exec*()` functions. If it returns at all, it has failed (and the return value is `-1`). Simply write the error reporting code after the call to `exec*()`. – Jonathan Leffler Sep 29 '16 at 19:51
  • @Pablo: that's different and matches what I said and what you meant to say (but didn't actually say in your second comment) — the value from `execl()` itself is `-1` and `errno` is set to `2` or `ENOENT`. – Jonathan Leffler Sep 29 '16 at 19:52
  • Yeah that's true. I wanted to make an emphasis on the fact, that `execl` will only return if it fails. It seems I didn't express myself correctly. – Pablo Sep 29 '16 at 19:52
  • user3386109: the printf is print out in terminal and exit through return, but all the childs still communicate on the first turn (which it hit return1) – Tran Van Boi Sep 29 '16 at 19:52
  • @Jonathan: it doesn't matter if execl(exeFile[i],"args",(char*)0 is correct or not, I just want this program to return immediately (no communications happen) if one of exeFile[i] is not executable. – Tran Van Boi Sep 29 '16 at 20:09
  • It does matter — or it should matter as an issue of (your personal) professional pride. This question may be around for years. If you don't fix that, it shows you don't care about the quality of your work. That's silly. – Jonathan Leffler Sep 29 '16 at 20:11
  • I understand that you want to know if all the children are present and working before sending any data to any of them. There isn't a trivial way to do that, as I outlined in another comment. If you have bidirectional pipe communication (one pipe to each child, one pipe from each child), then you could design the children to write an agreed-upon byte (or word or other message) to the incoming pipe, and your parent program doesn't continue until it has received the correct response from each child. If the children read a message from the parent process after writing, they can exit on EOF. – Jonathan Leffler Sep 29 '16 at 20:16
  • @Jonathan: oh I got you now, I think I know how to move on from here; thank for your help. btw I'm C/Linux learner and this is part of my assignment. – Tran Van Boi Sep 29 '16 at 20:27
  • I should have separated my 'castigation' comment about non-compilable code into a separate comment from the content that followed; I'm sorry I didn't. – Jonathan Leffler Sep 29 '16 at 20:32
  • @TranVanBoid, do you mean that you want *the child process* to "return 1" to the parent in the event that `execl()` fails, or do you mean you want *the parent* to abort the whole thing and return 1 to its caller? – John Bollinger Sep 29 '16 at 21:30
  • @JohnBollinger the "the parent to abort the whole thing and return 1 to its caller" one. I just realise how dumb to put "return 1; " in the child process. – Tran Van Boi Sep 29 '16 at 22:02
  • The `return 1;` after the `execl()` — or, in a function other than `main()`, a call to `exit(1);` or one of its variants — is wholly necessary. There should be an error message printed to standard error too. But you can't afford to have the child continue executing after it fails to exec — doing so would lead to chaos and confusion. The problem is how the parent recognizes that the child failed to exec. (Incidentally, POSIX recommends `exit(127);` when the command is not found — [Exit status for commands](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_08_02).) – Jonathan Leffler Sep 29 '16 at 22:12
  • @JonathanLeffler my codes work when I replace return with exit... – Tran Van Boi Sep 29 '16 at 22:51

2 Answers2

2

You can arrange give the child one end of a pipe(), set to close-on-exec.

After the execl() fails (i.e. if the call returns), the child will write() to the pipe. If the parent receives anything on its end of the pipe (checking with poll() or similar), then it knows the child has failed.

If the message from the child contains an identifier of the child, then the pipe can be shared across all the children. Try to keep the write() small enough to be atomic, though!

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
  • Keeping the write small enough isn't hard; you'd have to be writing more than 4 KiB — possibly more than 64 KiB — to exceed the limit for atomic writes on a pipe. – Jonathan Leffler Sep 29 '16 at 20:52
  • Yes, I should have said that it would be pretty wild to write that much! – Toby Speight Sep 29 '16 at 20:53
  • How robust is this in the presence of arbitrary scheduling delays? How long does the parent process wait before deciding that all the children did succeed? It's not as powerful as a positive message from the children "I'm here". But we don't know whether the child processes are modifiable so that they could send a positive message. We don't know what pipes are set up either — whether there's a pipe from the parent to the child, or from the child to the parent, or a pair of pipes, one each way. It's a tad tricky. – Jonathan Leffler Sep 29 '16 at 20:59
  • I tried to do something similar by write to child's stdin_fileno and using read() to get the value in another end of pipe, but was unable to receive anything. – Tran Van Boi Sep 29 '16 at 21:57
  • @Jon: it's not that tricky. The call to `read()` will return EOF when all of the children have exec'd or exited. Completely deterministic, if you do it that way. – Dietrich Epp Sep 29 '16 at 22:31
  • Oh, of course! I missed the obvious. If you get any data, at least one of the children failed; if you get no data, they all successfully exec'd. – Jonathan Leffler Sep 29 '16 at 22:33
0

The only robust (yet quite exotic, an maybe not very portable) way to achieve your goal is to use ptrace. The parent sets PTRACE_TRACEEXEC option, forks all children, and wait() in the loop. The child does

    } else if () {
        ptrace(PTRACE_TRACEME, ...);
        execl(...);
    }

The parent shall keep looping wait until all children report either PTRACE_EVENT_EXEC or an exit. In first case, the exec was successful, and the child is stopped. When you know that every child reported, and you are satisfied with their state, do ptrace(PTRACE_DETACH, ...) on all of them and proceed as desired.

user58697
  • 7,808
  • 1
  • 14
  • 28