Allow setting working directory for services.
authorDavin McCall <davmac@davmac.org>
Fri, 23 Feb 2018 18:40:45 +0000 (18:40 +0000)
committerDavin McCall <davmac@davmac.org>
Fri, 23 Feb 2018 18:49:47 +0000 (18:49 +0000)
doc/manpages/dinit-service.5
src/baseproc-service.cc
src/includes/proc-service.h
src/includes/service.h
src/load-service.cc
src/run-child-proc.cc
src/tests/test-run-child-proc.cc

index 72a6af43e177f0f3187d1d5ef2f3035382236569..3c64513581a461e80893aa66b3222dca572bc26f 100644 (file)
@@ -84,6 +84,10 @@ services.
 Specifies the command to stop the service. Applicable only to \fBscripted\fR
 services.
 .TP
+\fBworking-dir\fR = \fIdirectory\fR
+Specifies the working directory for this service. For a scripted service, this
+affects both the start command and the stop command.
+.TP
 \fBrun-as\fR = \fIuser-id\fR
 Specifies which user to run the process(es) for this service as. The group id
 for the process will also be set to the primary group of the specified user.
index 79950870e462287d16eb7953ee336a0b175250d3..ae5288e66c7a8fa33cdbdebc326220cdee8af6ba 100644 (file)
@@ -123,8 +123,10 @@ bool base_process_service::start_ps_process(const std::vector<const char *> &cmd
     }
 
     if (forkpid == 0) {
-        run_child_proc(cmd.data(), logfile, on_console, pipefd[1], control_socket[1], socket_fd,
-                run_as_uid, run_as_gid);
+        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, run_as_uid, run_as_gid);
     }
     else {
         // Parent process
index bd78ae76646ee643cd2ab210502c2a5b71fef9b5..d734c7ed6a2486c8d62cffc8ab5f6a0ea45e4f0a 100644 (file)
@@ -45,6 +45,8 @@ class base_process_service : public service_record
     string stop_command;          // storage for stop program/script and arguments
     std::vector<const char *> stop_arg_parts; // pointer to each argument/part of the stop_command, and nullptr
 
+    string working_dir;       // working directory (or empty)
+
     service_child_watcher child_listener;
     exec_status_pipe_watcher child_status_listener;
     process_restart_timer restart_timer;
@@ -192,6 +194,12 @@ class base_process_service : public service_record
         run_as_gid = gid;
     }
 
+    // Set the working directory
+    void set_workding_dir(string working_dir_p)
+    {
+        working_dir = working_dir_p;
+    }
+
     // The restart/stop timer expired.
     void timer_expired() noexcept;
 };
index f80d4c10c14460cc4dd02e23f9990720564f45ed..017e1afea03380ca62ca112ef34596e10fbe9485 100644 (file)
@@ -338,6 +338,7 @@ class service_record
 
     // Run a child process (call after forking).
     // - args specifies the program arguments including the executable (argv[0])
+    // - working_dir specifies the working directory; may be null
     // - logfile specifies the logfile
     // - on_console: if true, process is run with access to console
     // - wpipefd: if the exec is unsuccessful, or another error occurs beforehand, the
@@ -345,8 +346,8 @@ class service_record
     // - csfd: the control socket fd; may be -1 to inhibit passing of control socket
     // - socket_fd: the pre-opened socket file descriptor (may be -1)
     // - uid/gid: the identity to run the process as (may be both -1, otherwise both must be valid)
-    void run_child_proc(const char * const *args, const char *logfile, bool on_console, int wpipefd,
-            int csfd, int socket_fd, uid_t uid, gid_t gid) noexcept;
+    void run_child_proc(const char * const *args, const char *working_dir, const char *logfile,
+            bool on_console, int wpipefd, int csfd, int socket_fd, uid_t uid, gid_t gid) noexcept;
     
     // A dependency has reached STARTED state
     void dependency_started() noexcept;
index b6d72f5859a80ef61b45ad063f3100f93cd6567b..af6b453b55b119873ccf607b6371fdd81660f9bf 100644 (file)
@@ -388,6 +388,7 @@ service_record * dirload_service_set::load_service(const char * name)
     list<pair<unsigned,unsigned>> command_offsets;
     string stop_command;
     list<pair<unsigned,unsigned>> stop_command_offsets;
+    string working_dir;
     string pid_file;
 
     service_type_t service_type = service_type_t::PROCESS;
@@ -454,6 +455,9 @@ service_record * dirload_service_set::load_service(const char * name)
                 if (setting == "command") {
                     command = read_setting_value(i, end, &command_offsets);
                 }
+                else if (setting == "working-dir") {
+                    working_dir = read_setting_value(i, end, nullptr);
+                }
                 else if (setting == "socket-listen") {
                     socket_path = read_setting_value(i, end, nullptr);
                 }
@@ -615,6 +619,7 @@ service_record * dirload_service_set::load_service(const char * name)
                 if (service_type == service_type_t::PROCESS) {
                     auto rvalps = new process_service(this, string(name), std::move(command),
                             command_offsets, depends);
+                    rvalps->set_workding_dir(working_dir);
                     rvalps->set_restart_interval(restart_interval, max_restarts);
                     rvalps->set_restart_delay(restart_delay);
                     rvalps->set_stop_timeout(stop_timeout);
@@ -622,6 +627,7 @@ service_record * dirload_service_set::load_service(const char * name)
                     rvalps->set_start_interruptible(start_is_interruptible);
                     rvalps->set_extra_termination_signal(term_signal);
                     rvalps->set_run_as_uid_gid(run_as_uid, run_as_gid);
+                    rvalps->set_workding_dir(working_dir);
                     // process service start / run on console must be the same:
                     onstart_flags.starts_on_console = onstart_flags.runs_on_console;
                     rval = rvalps;
@@ -629,6 +635,7 @@ service_record * dirload_service_set::load_service(const char * name)
                 else if (service_type == service_type_t::BGPROCESS) {
                     auto rvalps = new bgproc_service(this, string(name), std::move(command),
                             command_offsets, depends);
+                    rvalps->set_workding_dir(working_dir);
                     rvalps->set_pid_file(std::move(pid_file));
                     rvalps->set_restart_interval(restart_interval, max_restarts);
                     rvalps->set_restart_delay(restart_delay);
@@ -644,6 +651,7 @@ service_record * dirload_service_set::load_service(const char * name)
                     auto rvalps = new scripted_service(this, string(name), std::move(command),
                             command_offsets, depends);
                     rvalps->set_stop_command(stop_command, stop_command_offsets);
+                    rvalps->set_workding_dir(working_dir);
                     rvalps->set_stop_timeout(stop_timeout);
                     rvalps->set_start_timeout(start_timeout);
                     rvalps->set_start_interruptible(start_is_interruptible);
index f08ee5b41b6be17e998374e88d3a0a22d5b87d8a..9ee2860eff676e16bb7acda176a7db3c82bb462b 100644 (file)
@@ -11,8 +11,9 @@
 
 #include "service.h"
 
-void service_record::run_child_proc(const char * const *args, const char *logfile, bool on_console,
-        int wpipefd, int csfd, int socket_fd, uid_t uid, gid_t gid) noexcept
+void service_record::run_child_proc(const char * const *args, const char *working_dir,
+        const char *logfile, bool on_console, int wpipefd, int csfd, int socket_fd,
+        uid_t uid, gid_t gid) noexcept
 {
     // Child process. Must not allocate memory (or otherwise risk throwing any exception)
     // from here until exit().
@@ -73,6 +74,12 @@ void service_record::run_child_proc(const char * const *args, const char *logfil
         if (putenv(csenvbuf)) goto failure_out;
     }
 
+    if (working_dir != nullptr && *working_dir != 0) {
+        if (chdir(working_dir) == -1) {
+            goto failure_out;
+        }
+    }
+
     if (! on_console) {
         // Re-set stdin, stdout, stderr
         close(0); close(1); close(2);
index 2817cc64986072218a7d9174846e7f8f05f4ba62..bcad2d75dc0c4b5b1ef26620ffd6a7954d3b277a 100644 (file)
@@ -2,8 +2,9 @@
 
 // Stub out run_child_proc function, for testing purposes.
 
-void service_record::run_child_proc(const char * const *args, const char *logfile, bool on_console,
-        int wpipefd, int csfd, int socket_fd, uid_t uid, gid_t gid) noexcept
+void service_record::run_child_proc(const char * const *args, const char *working_dir,
+        const char *logfile, bool on_console, int wpipefd, int csfd, int socket_fd,
+        uid_t uid, gid_t gid) noexcept
 {
 
 }