Add full utmp (utmpx) support.
authorDavin McCall <davmac@davmac.org>
Mon, 26 Nov 2018 22:11:46 +0000 (22:11 +0000)
committerDavin McCall <davmac@davmac.org>
Mon, 26 Nov 2018 22:42:04 +0000 (22:42 +0000)
src/baseproc-service.cc
src/includes/dinit-utmp.h
src/includes/proc-service.h
src/load-service.cc
src/proc-service.cc

index 22a61a2800a30d024ecd7d49c98d31efe998002d..43b3b75fbbf9b0eb1c69dc3ca8969187dce3adb0 100644 (file)
@@ -154,8 +154,15 @@ bool base_process_service::start_ps_process(const std::vector<const char *> &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
index b270a8be3b02de74704440e6a75e0861fe62dbf3..ea1064e9eaa25ef34763aa0bb5f43f778d3af27e 100644 (file)
@@ -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
 
index be01767df5525a260a494a3baef89fbc2de3d45f..4b6f5ab04529ac425b8fa8f9766db10a5420630b 100644 (file)
@@ -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
     {
     }
index 318e7b4f3d077cd7f11c352de679c2f08896bd73..97d8b7a8d3614a0b0f27085166aa22491b433a89 100644 (file)
@@ -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) {
index 9cb94cdcde60a0278e04a8247114a5bea6df44e4..cfe86d6a343863b556bf9c2d1883f6b3ad6d6109 100644 (file)
@@ -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.