From 880fa93b35e731c27808e1db816a448726c98723 Mon Sep 17 00:00:00 2001 From: Davin McCall Date: Fri, 23 Feb 2018 18:40:45 +0000 Subject: [PATCH] Allow setting working directory for services. --- doc/manpages/dinit-service.5 | 4 ++++ src/baseproc-service.cc | 6 ++++-- src/includes/proc-service.h | 8 ++++++++ src/includes/service.h | 5 +++-- src/load-service.cc | 8 ++++++++ src/run-child-proc.cc | 11 +++++++++-- src/tests/test-run-child-proc.cc | 5 +++-- 7 files changed, 39 insertions(+), 8 deletions(-) diff --git a/doc/manpages/dinit-service.5 b/doc/manpages/dinit-service.5 index 72a6af4..3c64513 100644 --- a/doc/manpages/dinit-service.5 +++ b/doc/manpages/dinit-service.5 @@ -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. diff --git a/src/baseproc-service.cc b/src/baseproc-service.cc index 7995087..ae5288e 100644 --- a/src/baseproc-service.cc +++ b/src/baseproc-service.cc @@ -123,8 +123,10 @@ bool base_process_service::start_ps_process(const std::vector &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 diff --git a/src/includes/proc-service.h b/src/includes/proc-service.h index bd78ae7..d734c7e 100644 --- a/src/includes/proc-service.h +++ b/src/includes/proc-service.h @@ -45,6 +45,8 @@ class base_process_service : public service_record string stop_command; // storage for stop program/script and arguments std::vector 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; }; diff --git a/src/includes/service.h b/src/includes/service.h index f80d4c1..017e1af 100644 --- a/src/includes/service.h +++ b/src/includes/service.h @@ -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; diff --git a/src/load-service.cc b/src/load-service.cc index b6d72f5..af6b453 100644 --- a/src/load-service.cc +++ b/src/load-service.cc @@ -388,6 +388,7 @@ service_record * dirload_service_set::load_service(const char * name) list> command_offsets; string stop_command; list> 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); diff --git a/src/run-child-proc.cc b/src/run-child-proc.cc index f08ee5b..9ee2860 100644 --- a/src/run-child-proc.cc +++ b/src/run-child-proc.cc @@ -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); diff --git a/src/tests/test-run-child-proc.cc b/src/tests/test-run-child-proc.cc index 2817cc6..bcad2d7 100644 --- a/src/tests/test-run-child-proc.cc +++ b/src/tests/test-run-child-proc.cc @@ -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 { } -- 2.25.1