if (forkpid == 0) {
const char * working_dir_c = nullptr;
if (! working_dir.empty()) working_dir_c = working_dir.c_str();
- run_child_proc(cmd.data(), working_dir_c, logfile, on_console, pipefd[1], control_socket[1],
- socket_fd, notify_pipe[1], force_notification_fd, nullptr, run_as_uid, run_as_gid);
+ if (after_fork(getpid())) {
+ run_child_proc(cmd.data(), working_dir_c, logfile, on_console, pipefd[1], control_socket[1],
+ socket_fd, notify_pipe[1], force_notification_fd, nullptr, run_as_uid, run_as_gid);
+ }
+ else {
+ int exec_status = errno;
+ write(pipefd[1], &exec_status, sizeof(int));
+ _exit(0);
+ }
}
else {
// Parent process
#define DINIT_UTMP_H_INCLUDED
#ifndef USE_UTMPX
+#if __linux__ || __FreeBSD__ || __DragonFly__
#define USE_UTMPX 1
+#else
+#define USE_UTMPX 0
+#endif
#endif
#ifndef USE_UPDWTMPX
return success;
}
+// Create a utmp entry for the specified process, with the given id and tty line.
+inline bool create_utmp_entry(const char *utmp_id, const char *utmp_line, pid_t pid)
+{
+ struct utmpx record;
+ memset(&record, 0, sizeof(record));
+
+ record.ut_type = INIT_PROCESS;
+ record.ut_pid = pid;
+ set_current_time(&record);
+ strncpy(record.ut_id, utmp_id, sizeof(record.ut_id));
+ strncpy(record.ut_line, utmp_line, sizeof(record.ut_line));
+
+ setutxent();
+ bool success = (pututxline(&record) != NULL);
+ endutxent();
+
+ return success;
+}
+
+// Clear the utmp entry for the given id/line/process.
+inline void clear_utmp_entry(const char *utmp_id, const char *utmp_line)
+{
+ struct utmpx record;
+ memset(&record, 0, sizeof(record));
+
+ record.ut_type = DEAD_PROCESS;
+ set_current_time(&record);
+ strncpy(record.ut_id, utmp_id, sizeof(record.ut_id));
+ strncpy(record.ut_line, utmp_line, sizeof(record.ut_line));
+
+ struct utmpx *result;
+
+ setutxent();
+
+ // Try to find an existing entry by id/line and copy the process ID:
+ if (*utmp_id) {
+ result = getutxid(&record);
+ }
+ else {
+ result = getutxline(&record);
+ }
+
+ if (result) {
+ record.ut_pid = result->ut_pid;
+ }
+
+ pututxline(&record);
+ endutxent();
+}
+
#else // Don't update databases:
-static inline void log_boot() { }
+static inline bool log_boot()
+{
+ return true;
+}
+
+static inline bool create_utmp_entry(const char *utmp_id, const char *utmp_line)
+{
+ return true;
+}
#endif
#include "baseproc-sys.h"
#include "service.h"
+#include "dinit-utmp.h"
// This header defines base_proc_service (base process service) and several derivatives, as well as some
// utility functions and classes. See service.h for full details of services.
// Start the process, return true on success
virtual bool bring_up() noexcept override;
+ // Called after forking (before executing remote process). Returns true to continue
+ // execution, otherwise sets errno and returns false.
+ virtual bool after_fork(pid_t child_pid) noexcept { return true; }
+
// Called when the process exits. The exit_status is the status value yielded by
// the "wait" system call.
virtual void handle_exit_status(bp_sys::exit_status exit_status) noexcept = 0;
ready_notify_watcher readiness_watcher;
+#if USE_UTMPX
+
+ char inittab_id[sizeof(utmpx().ut_id)];
+ char inittab_line[sizeof(utmpx().ut_line)];
+
+ protected:
+ bool after_fork(pid_t child_pid) noexcept override
+ {
+ if (*inittab_id || *inittab_line) {
+ return create_utmp_entry(inittab_id, inittab_line, child_pid);
+ }
+ return true;
+ }
+
+#endif
+
protected:
ready_notify_watcher *get_ready_watcher() noexcept override
{
{
}
+#if USE_UTMPX
+
+ // Set the id of the process in utmp (the "inittab" id)
+ void set_utmp_id(const char *id)
+ {
+ strncpy(inittab_id, id, sizeof(inittab_id));
+ }
+
+ // Set the device line of the process in utmp database
+ void set_utmp_line(const char *line)
+ {
+ strncpy(inittab_line, line, sizeof(inittab_line));
+ }
+
+#endif
+
~process_service() noexcept
{
}
#include "proc-service.h"
#include "dinit-log.h"
#include "dinit-util.h"
+#include "dinit-utmp.h"
using string = std::string;
using string_iterator = std::string::iterator;
string chain_to_name;
+ #if USE_UTMPX
+ char inittab_id[sizeof(utmpx().ut_id)] = {0};
+ char inittab_line[sizeof(utmpx().ut_line)] = {0};
+ #endif
+
string line;
service_file.exceptions(ios::badbit | ios::failbit);
+ notify_setting);
}
}
+ else if (setting == "inittab-id") {
+ string inittab_setting = read_setting_value(i, end, nullptr);
+ #if USE_UTMPX
+ if (inittab_setting.length() > sizeof(inittab_id)) {
+ throw service_description_exc(name, "inittab-id setting is too long");
+ }
+ strncpy(inittab_id, inittab_setting.c_str(), sizeof(inittab_id));
+ #endif
+ }
+ else if (setting == "inittab-line") {
+ string inittab_setting = read_setting_value(i, end, nullptr);
+ #if USE_UTMPX
+ if (inittab_setting.length() > sizeof(inittab_line)) {
+ throw service_description_exc(name, "inittab-line setting is too long");
+ }
+ strncpy(inittab_line, inittab_setting.c_str(), sizeof(inittab_line));
+ #endif
+ }
else {
throw service_description_exc(name, "Unknown setting: " + setting);
}
rvalps->set_workding_dir(working_dir);
rvalps->set_notification_fd(readiness_fd);
rvalps->set_notification_var(std::move(readiness_var));
- // process service start / run on console must be the same:
- onstart_flags.starts_on_console = onstart_flags.runs_on_console;
+ #if USE_UTMPX
+ rvalps->set_utmp_id(inittab_id);
+ rvalps->set_utmp_line(inittab_line);
+ #endif
rval = rvalps;
}
else if (service_type == service_type_t::BGPROCESS) {
}
}
+ if (*inittab_id || *inittab_line) {
+ clear_utmp_entry(inittab_id, inittab_line);
+ }
+
if (service_state == service_state_t::STARTING) {
// If state is STARTING, we must be waiting for readiness notification; the process has
// terminated before becoming ready.