Use $HOME/dinit.d for default service directory if not system init.
authorDavin McCall <davmac@davmac.org>
Fri, 15 Jan 2016 11:49:46 +0000 (11:49 +0000)
committerDavin McCall <davmac@davmac.org>
Fri, 15 Jan 2016 11:49:46 +0000 (11:49 +0000)
Add command line options to specify service directory and control
socket path. Add command line option to run as system init (default
when pid == 1).

TODO
src/dinit.cc

diff --git a/TODO b/TODO
index b33e495a1b700ec6e34f9c07e1d8d7d5ec21ba8b..7aeb2d89189ba4db98b227e80c65d7d6e12b5449 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,3 +1,6 @@
+* When a PROCESS service process dies, and smooth_recovery is false, probably
+  need to force-stop dependents even if the process itself was stopped
+  deliberately.
 * Complete control socket handling and protocol
   - support for pinned-start and pinned-stop
   - support for listing all services
@@ -16,11 +19,6 @@ For version 1.0:
   (eg there's no need to mount filesystems twice; there might be various other
    system initialisations that can't or shouldn't really be "undone" and so do
    not need to be re-done).
-* Add command line arg to start in "PID 1" mode (even if PID != 1).
-  Basically, allow running as a system service monitor, without
-  requiring that dinit runs as PID 1.
-* if PID != 1, choose a more sensible service definition directory
-  (something like $HOME/dinit.d)
 * Documentation including sample service definitions
 * Better error handling, logging of errors (largely done, still some patches
   of code where it may be missing).
@@ -43,7 +41,8 @@ For later:
 * Investigate using cn_proc netlink connector (cn_proc.h) to receive process
   termination events even when running with PID != 1 (Linux only).
   Also, there is the possibility of having a small, simple PID-1 init which
-  sends terminated process IDs over a pipe to Dinit.
+  sends terminated process IDs over a pipe to Dinit. Finally, it may be possible
+  to run dinit (and subprocesses) in a new PID namespace (again linux-only).
 * Allow logging tasks to memory (growing or circular buffer) and later
   switching to disk logging (allows for filesystem mounted readonly on boot)
 * Rate control on process respawn
index 9b26b3a1c277c1e3de3f24c1bcc71e1a86117f7e..6a2e78cef6f2674144f1dce63f3a91e47d2084e7 100644 (file)
@@ -78,61 +78,42 @@ static bool am_system_init = false; // true if we are the system init process
 static bool control_socket_open = false;
 int active_control_conns = 0;
 
+// Control socket path. We maintain a string (control_socket_str) in case we need
+// to allocate storage, but control_socket_path is the authoritative value.
 static const char *control_socket_path = "/dev/dinitctl";
 static std::string control_socket_str;
 
+static const char *user_home_path = nullptr;
 
-int main(int argc, char **argv)
+
+// Get user home (and set user_home_path). (The return may become invalid after
+// changing the evironment (HOME variable) or using the getpwuid() function).
+const char * get_user_home()
 {
-    using namespace std;
-    
-    am_system_init = (getpid() == 1);
-    
-    if (am_system_init) {
-        // setup STDIN, STDOUT, STDERR so that we can use them
-        int onefd = open("/dev/console", O_RDONLY, 0);
-        dup2(onefd, 0);
-        int twofd = open("/dev/console", O_RDWR, 0);
-        dup2(twofd, 1);
-        dup2(twofd, 2);
-    }
-    
-    /* Set up signal handlers etc */
-    /* SIG_CHILD is ignored by default: good */
-    /* sigemptyset(&sigwait_set); */
-    /* sigaddset(&sigwait_set, SIGCHLD); */
-    /* sigaddset(&sigwait_set, SIGINT); */
-    /* sigaddset(&sigwait_set, SIGTERM); */
-    /* sigprocmask(SIG_BLOCK, &sigwait_set, NULL); */
-    
-    // Terminal access control signals - we block these so that dinit can't be
-    // suspended if it writes to the terminal after some other process has claimed
-    // ownership of it.
-    signal(SIGTSTP, SIG_IGN);
-    signal(SIGTTIN, SIG_IGN);
-    signal(SIGTTOU, SIG_IGN);
-    
-    /* list of services to start */
-    list<const char *> services_to_start;
-    
-    if (! am_system_init) {
-        char * userhome = getenv("HOME");
-        if (userhome == nullptr) {
+    if (user_home_path == nullptr) {
+        user_home_path = getenv("HOME");
+        if (user_home_path == nullptr) {
             struct passwd * pwuid_p = getpwuid(getuid());
             if (pwuid_p != nullptr) {
-                userhome = pwuid_p->pw_dir;
+                user_home_path = pwuid_p->pw_dir;
             }
         }
-        
-        if (userhome != nullptr) {
-            control_socket_str = userhome;
-            control_socket_str += "/.dinitctl";
-            control_socket_path = control_socket_str.c_str();
-        }
     }
+    return user_home_path;
+}
+
+
+int main(int argc, char **argv)
+{
+    using namespace std;
     
-    /* service directory name */
-    const char * service_dir = "/etc/dinit.d";
+    am_system_init = (getpid() == 1);
+    const char * service_dir = nullptr;
+    string service_dir_str; // to hold storage for above if necessary
+    bool control_socket_path_set = false;
+
+    // list of services to start
+    list<const char *> services_to_start;
     
     // Arguments, if given, specify a list of services to start.
     // If we are running as init (PID=1), the kernel gives us any command line
@@ -146,8 +127,7 @@ int main(int argc, char **argv)
             // An option...
             if (strcmp(argv[i], "--services-dir") == 0 ||
                     strcmp(argv[i], "-d") == 0) {
-                ++i;
-                if (i < argc) {
+                if (++i < argc) {
                     service_dir = argv[i];
                 }
                 else {
@@ -155,11 +135,31 @@ int main(int argc, char **argv)
                     return 1;
                 }
             }
+            else if (strcmp(argv[i], "--system") == 0 ||
+                    strcmp(argv[i], "-s") == 0) {
+                am_system_init = true;
+            }
+            else if (strcmp(argv[i], "--socket-path") == 0 ||
+                    strcmp(argv[i], "-p") == 0) {
+                if (++i < argc) {
+                    control_socket_path = argv[i];
+                    control_socket_path_set = true;
+                }
+                else {
+                    cerr << "dinit: '--socket-path' (-p) requires an argument" << endl;
+                    return 1;
+                }
+            }
             else if (strcmp(argv[i], "--help") == 0) {
                 cout << "dinit, an init with dependency management" << endl;
-                cout << " --help                         : display help" << endl;
-                cout << " --services-dir <dir>, -d <dir> : set base directory for service description files (-d <dir>)" << endl;
-                cout << " <service-name>                 : start service with name <service-name>" << endl;
+                cout << " --help                       display help" << endl;
+                cout << " --services-dir <dir>, -d <dir>" << endl;
+                cout << "                              set base directory for service description" << endl;
+                cout << "                              files (-d <dir>)" << endl;
+                cout << " --system, -s                 run as the system init process" << endl;
+                cout << " --socket-path <path>, -p <path>" << endl;
+                cout << "                              path to control socket" << endl;
+                cout << " <service-name>               start service with name <service-name>" << endl;
                 return 0;
             }
             else {
@@ -179,6 +179,56 @@ int main(int argc, char **argv)
       }
     }
     
+    if (am_system_init) {
+        // setup STDIN, STDOUT, STDERR so that we can use them
+        int onefd = open("/dev/console", O_RDONLY, 0);
+        dup2(onefd, 0);
+        int twofd = open("/dev/console", O_RDWR, 0);
+        dup2(twofd, 1);
+        dup2(twofd, 2);
+        
+        if (onefd > 2) close(onefd);
+        if (twofd > 2) close(twofd);
+    }
+    
+    /* Set up signal handlers etc */
+    /* SIG_CHILD is ignored by default: good */
+    /* sigemptyset(&sigwait_set); */
+    /* sigaddset(&sigwait_set, SIGCHLD); */
+    /* sigaddset(&sigwait_set, SIGINT); */
+    /* sigaddset(&sigwait_set, SIGTERM); */
+    /* sigprocmask(SIG_BLOCK, &sigwait_set, NULL); */
+    
+    // Terminal access control signals - we block these so that dinit can't be
+    // suspended if it writes to the terminal after some other process has claimed
+    // ownership of it.
+    signal(SIGTSTP, SIG_IGN);
+    signal(SIGTTIN, SIG_IGN);
+    signal(SIGTTOU, SIG_IGN);
+    
+    if (! am_system_init && ! control_socket_path_set) {
+        const char * userhome = get_user_home();
+        if (userhome != nullptr) {
+            control_socket_str = userhome;
+            control_socket_str += "/.dinitctl";
+            control_socket_path = control_socket_str.c_str();
+        }
+    }
+    
+    /* service directory name */
+    if (service_dir == nullptr && ! am_system_init) {
+        const char * userhome = get_user_home();
+        if (userhome != nullptr) {
+            service_dir_str = get_user_home();
+            service_dir_str += "/dinit.d";
+            service_dir = service_dir_str.c_str();
+        }
+    }
+    
+    if (service_dir == nullptr) {
+        service_dir = "/etc/dinit.d";
+    }
+    
     if (services_to_start.empty()) {
         services_to_start.push_back("boot");
     }
@@ -355,6 +405,8 @@ void open_control_socket(struct ev_loop *loop) noexcept
         name->sun_family = AF_UNIX;
         strcpy(name->sun_path, saddrname);
 
+        // OpenBSD and Linux both allow combining NONBLOCK/CLOEXEC flags with socket type, however
+        // it's not actually POSIX. (TODO).
         int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
         if (sockfd == -1) {
             log(LogLevel::ERROR, "Error creating control socket: ", strerror(errno));