7 #include <sys/socket.h>
14 void service_record::run_child_proc(const char * const *args, const char *working_dir,
15 const char *logfile, bool on_console, int wpipefd, int csfd, int socket_fd,
16 uid_t uid, gid_t gid) noexcept
18 // Child process. Must not allocate memory (or otherwise risk throwing any exception)
19 // from here until exit().
21 // If the console already has a session leader, presumably it is us. On the other hand
22 // if it has no session leader, and we don't create one, then control inputs such as
23 // ^C will have no effect.
24 bool do_set_ctty = (tcgetsid(0) == -1);
26 // Copy signal mask, but unmask signals that we masked on startup. For the moment, we'll
27 // also block all signals, since apparently dup() can be interrupted (!!! really, POSIX??).
30 sigfillset(&sigall_set);
31 sigprocmask(SIG_SETMASK, &sigall_set, &sigwait_set);
32 sigdelset(&sigwait_set, SIGCHLD);
33 sigdelset(&sigwait_set, SIGINT);
34 sigdelset(&sigwait_set, SIGTERM);
35 sigdelset(&sigwait_set, SIGQUIT);
37 constexpr int bufsz = ((CHAR_BIT * sizeof(pid_t)) / 3 + 2) + 11;
38 // "LISTEN_PID=" - 11 characters; the expression above gives a conservative estimate
39 // on the maxiumum number of bytes required for LISTEN=nnn, including nul terminator,
40 // where nnn is a pid_t in decimal (i.e. one decimal digit is worth just over 3 bits).
43 // "DINIT_CS_FD=" - 12 bytes. (we -1 from sizeof(int) in account of sign bit).
44 constexpr int csenvbufsz = ((CHAR_BIT * sizeof(int) - 1) / 3 + 2) + 12;
45 char csenvbuf[csenvbufsz];
47 int minfd = (socket_fd == -1) ? 3 : 4;
49 // Move wpipefd/csfd to another fd if necessary
50 if (wpipefd < minfd) {
51 wpipefd = fcntl(wpipefd, F_DUPFD_CLOEXEC, minfd);
52 if (wpipefd == -1) goto failure_out;
55 if (csfd != -1 && csfd < minfd) {
56 csfd = fcntl(csfd, F_DUPFD, minfd);
57 if (csfd == -1) goto failure_out;
60 if (socket_fd != -1) {
61 // If we passing a pre-opened socket, it has to be fd number 3. (Thanks, systemd).
62 if (dup2(socket_fd, 3) == -1) goto failure_out;
67 if (putenv(const_cast<char *>("LISTEN_FDS=1"))) goto failure_out;
68 snprintf(nbuf, bufsz, "LISTEN_PID=%jd", static_cast<intmax_t>(getpid()));
69 if (putenv(nbuf)) goto failure_out;
73 snprintf(csenvbuf, csenvbufsz, "DINIT_CS_FD=%d", csfd);
74 if (putenv(csenvbuf)) goto failure_out;
77 if (working_dir != nullptr && *working_dir != 0) {
78 if (chdir(working_dir) == -1) {
84 // Re-set stdin, stdout, stderr
85 close(0); close(1); close(2);
87 if (open("/dev/null", O_RDONLY) == 0) {
88 // stdin = 0. That's what we should have; proceed with opening
90 if (open(logfile, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR) != 1) {
93 if (dup2(1, 2) != 2) {
97 else goto failure_out;
99 // We have the option of creating a session and process group, or just a new process
100 // group. If we just create a new process group, the child process cannot make itself
101 // a session leader if it wants to do that (eg getty/login will generally want this).
102 // If we do neither, and we are running with a controlling terminal, a ^C or similar
103 // will also affect the child process (which probably isn't so bad, though since we
104 // will handle the shutdown ourselves it's not necessary). Creating a new session
105 // (and a new process group as part of that) seems like a safe bet, and has the
106 // advantage of letting us signal the process as part of a process group.
110 // "run on console" - run as a foreground job on the terminal/console device
112 // if do_set_ctty is false, we are the session leader; we are probably running
113 // as a user process. Don't create a new session leader in that case, and run
114 // as part of the parent session. Otherwise, the new session cannot claim the
115 // terminal as a controlling terminal (it is already claimed), meaning that it
116 // will not see control signals from ^C etc.
119 // Disable suspend (^Z) (and on some systems, delayed suspend / ^Y)
120 signal(SIGTSTP, SIG_IGN);
122 // Become session leader
124 ioctl(0, TIOCSCTTY, 0);
127 tcsetpgrp(0, getpgrp());
130 if (uid != uid_t(-1)) {
131 if (setreuid(uid, uid) != 0) goto failure_out;
132 if (setregid(gid, gid) != 0) goto failure_out;
135 sigprocmask(SIG_SETMASK, &sigwait_set, nullptr);
137 execvp(args[0], const_cast<char **>(args));
139 // If we got here, the exec failed:
141 int exec_status = errno;
142 write(wpipefd, &exec_status, sizeof(int));