Safe application spawning with fork+exec


/ Published in: C++
Save to your folder(s)

With the typical fork+exec method of spawning an application, you are left in the dark about failures in the exec. Once the fork occurs, the "main" process doesn't have any communication channel except waitpid, so it's hard to tell what happened.

With this function, a pipe is opened for the forked child to communicate errors. If the exec succeeds, then the pipe is automatically closed, and the main process reports success. Otherwise, the errno from exec is communicated through the pipe.


Copy this code and paste it in your HTML
  1. #include <errno.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <unistd.h>
  5. #include <fcntl.h>
  6.  
  7. pid_t
  8. spawn(const char *file, char *const argv[])
  9. {
  10. pid_t pid;
  11. int forkpipe[2];
  12. int fd_flags, err, ret;
  13.  
  14. // set up a pipe for communication between forks
  15. ret = pipe(forkpipe);
  16. if (ret == -1)
  17. return -1;
  18.  
  19. pid = fork();
  20. if (pid == -1) {
  21. err = errno;
  22.  
  23. // close the write pipe
  24. close(forkpipe[1]);
  25.  
  26. goto parent_exit;
  27. }
  28.  
  29. if (pid == 0) {
  30. // forked child
  31.  
  32. // close the read pipe
  33. ret = close(forkpipe[0]);
  34. if (ret == -1)
  35. goto child_err;
  36.  
  37. // make the write end close-on-exec
  38. ret = fcntl(forkpipe[1], F_GETFD);
  39. if (ret == -1)
  40. goto child_err;
  41. fd_flags = ret | FD_CLOEXEC;
  42. ret = fcntl(forkpipe[1], F_SETFD, fd_flags);
  43. if (ret == -1)
  44. goto child_err;
  45.  
  46. // try to run the app
  47. execvp(file, argv);
  48. // if we continue executing here, an error occurred
  49.  
  50. child_err:
  51. // send the error code through the write pipe
  52. err = errno;
  53. write(forkpipe[1], &err, sizeof(err));
  54.  
  55. exit(1);
  56. }
  57.  
  58. // parent
  59.  
  60. // close the write pipe
  61. close(forkpipe[1]);
  62.  
  63. // get the error code from the read pipe
  64. ret = read(forkpipe[0], &err, sizeof(err));
  65. if (ret == -1) {
  66. err = errno;
  67. pid = -1;
  68. } else if (ret == sizeof(err))
  69. pid = -1;
  70. else
  71. err = 0;
  72.  
  73. parent_exit:
  74. // close the read pipe
  75. close(forkpipe[0]);
  76.  
  77. if (err)
  78. errno = err;
  79.  
  80. return pid;
  81. }

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.