From: Davin McCall Date: Mon, 26 Nov 2018 22:11:46 +0000 (+0000) Subject: Add full utmp (utmpx) support. X-Git-Tag: v0.5.0~15 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=e404e2cf6f6806f7272acc4a46c1727e7a0ab9c6;p=oweals%2Fdinit.git Add full utmp (utmpx) support. --- diff --git a/src/baseproc-service.cc b/src/baseproc-service.cc index 22a61a2..43b3b75 100644 --- a/src/baseproc-service.cc +++ b/src/baseproc-service.cc @@ -154,8 +154,15 @@ bool base_process_service::start_ps_process(const std::vector &cmd 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 diff --git a/src/includes/dinit-utmp.h b/src/includes/dinit-utmp.h index b270a8b..ea1064e 100644 --- a/src/includes/dinit-utmp.h +++ b/src/includes/dinit-utmp.h @@ -4,7 +4,11 @@ #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 @@ -58,9 +62,67 @@ inline bool log_boot() 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 diff --git a/src/includes/proc-service.h b/src/includes/proc-service.h index be01767..4b6f5ab 100644 --- a/src/includes/proc-service.h +++ b/src/includes/proc-service.h @@ -2,6 +2,7 @@ #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. @@ -144,6 +145,10 @@ class base_process_service : public service_record // 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; @@ -293,6 +298,22 @@ class process_service : public base_process_service 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 { @@ -308,6 +329,22 @@ class process_service : public base_process_service { } +#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 { } diff --git a/src/load-service.cc b/src/load-service.cc index 318e7b4..97d8b7a 100644 --- a/src/load-service.cc +++ b/src/load-service.cc @@ -17,6 +17,7 @@ #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; @@ -380,6 +381,11 @@ service_record * dirload_service_set::load_service(const char * name) 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); @@ -599,6 +605,24 @@ service_record * dirload_service_set::load_service(const char * name) + 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); } @@ -632,8 +656,10 @@ service_record * dirload_service_set::load_service(const char * name) 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) { diff --git a/src/proc-service.cc b/src/proc-service.cc index 9cb94cd..cfe86d6 100644 --- a/src/proc-service.cc +++ b/src/proc-service.cc @@ -190,6 +190,10 @@ void process_service::handle_exit_status(bp_sys::exit_status exit_status) noexce } } + 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.