Custom implementation of daemon(). This implements the same order of operations as glibc. * Opens a pipe to the child process to be able to wait for an event to occur. * * @returns 0 if successful, and in child process. * >0 if successful, and in parent process. * -1 in case of error (in parent process). * * In case of success, endpoint will be one end of a pipe f
| 48 | * which can be used with TokenWrite (in the child) or TokenRead (in the parent). |
| 49 | */ |
| 50 | int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd& endpoint) |
| 51 | { |
| 52 | // communication pipe with child process |
| 53 | std::optional<TokenPipe> umbilical = TokenPipe::Make(); |
| 54 | if (!umbilical) { |
| 55 | return -1; // pipe or pipe2 failed. |
| 56 | } |
| 57 | |
| 58 | int pid = fork(); |
| 59 | if (pid < 0) { |
| 60 | return -1; // fork failed. |
| 61 | } |
| 62 | if (pid != 0) { |
| 63 | // Parent process gets read end, closes write end. |
| 64 | endpoint = umbilical->TakeReadEnd(); |
| 65 | umbilical->TakeWriteEnd().Close(); |
| 66 | |
| 67 | int status = endpoint.TokenRead(); |
| 68 | if (status != 0) { // Something went wrong while setting up child process. |
| 69 | endpoint.Close(); |
| 70 | return -1; |
| 71 | } |
| 72 | |
| 73 | return pid; |
| 74 | } |
| 75 | // Child process gets write end, closes read end. |
| 76 | endpoint = umbilical->TakeWriteEnd(); |
| 77 | umbilical->TakeReadEnd().Close(); |
| 78 | |
| 79 | #if HAVE_DECL_SETSID |
| 80 | if (setsid() < 0) { |
| 81 | exit(1); // setsid failed. |
| 82 | } |
| 83 | #endif |
| 84 | |
| 85 | if (!nochdir) { |
| 86 | if (chdir("/") != 0) { |
| 87 | exit(1); // chdir failed. |
| 88 | } |
| 89 | } |
| 90 | if (!noclose) { |
| 91 | // Open /dev/null, and clone it into STDIN, STDOUT and STDERR to detach |
| 92 | // from terminal. |
| 93 | int fd = open("/dev/null", O_RDWR); |
| 94 | if (fd >= 0) { |
| 95 | bool err = dup2(fd, STDIN_FILENO) < 0 || dup2(fd, STDOUT_FILENO) < 0 || dup2(fd, STDERR_FILENO) < 0; |
| 96 | // Don't close if fd<=2 to try to handle the case where the program was invoked without any file descriptors open. |
| 97 | if (fd > 2) close(fd); |
| 98 | if (err) { |
| 99 | exit(1); // dup2 failed. |
| 100 | } |
| 101 | } else { |
| 102 | exit(1); // open /dev/null failed. |
| 103 | } |
| 104 | } |
| 105 | endpoint.TokenWrite(0); // Success |
| 106 | return 0; |
| 107 | } |
no test coverage detected