non-collapsing whitespace, double-quote marks, and backslashes in the
parameter value.
-Parameters are:
+Some available parameters are:
type = process | bgprocess | scripted | internal
command = ...
+stop-command = ...
+run-as = (user-id)
+restart = (boolean)
+smooth-recovery = (boolean)
logfile = ...
+pid-file = ...
options = ...
depends-on = (service name)
depends-ms = (service name)
stop-command = (external script or executable, and arguments)
For a 'scripted' service, this command is run to stop the service.
+run-as = (user-id)
+ 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.
+
restart = yes | true | no | false
Specifies whether the service should automatically restart if it becomes
stopped (for any reason, including being explicitly requested to stop).
the service does not reach the stopped state when the process terminates
unexpectedly).
-restart-delay = XXX.YYYY
- Specifies the minimum time in seconds between automatic restarts. The
- default is 0.2 (i.e. 200ms). This prevents Dinit from consuming processor
- cycles when a process continuously fails immediately after it starts.
-
-restart-limit-interval = XXX.YYYY
- Specifies the interval, in seconds, over which restarts are limited. If a
- process automatically restarts more than a certain number of times (default
- 3) in this time interval, it will not restart again. The default value is
- 10 seconds. Use this to prevent broken services from continuously
- restarting ad infinitum.
-
-restart-limit-count = NNN
- Specifies the maximum number of times that a service can automatically
- restart over the interval specified by restart-limit-interval (default of
- 10 seconds). Specify a value of 0 to disable the restart limit.
-
-stop-timeout = XXX.YYYY (or XXX,YYYY)
- Specifies the time in seconds allowed for the service to stop. If the
- service takes longer than this, its process group is sent a SIGKILL signal
- which should cause it to terminate immediately. The timeout period begins
- only when all dependent services have already stopped. The default stop
- timeout is 10 seconds.
-
-start-timeout = XXX.YYYY (or XXX,YYYY)
- Specifies the time in seconds allowed for the service to start. IF the
- service startup takes longer than this, its process group is sent a
- SIGINT signal and transitions to the "stopping" state. if it fails to stop
- within the period specified by the stop-timeout setting, it is sent a
- SIGKILL signal.
+logfile = (log file path)
+ Specifies the log file for the service. Output from the service process
+ will go this file.
pid-file = (path to file)
For "bgprocess" type services only; specifies the path of the file where
for this service. Starting this service will automatically start
the named service.
-socket-listen = (socket path)
- Pre-open a socket for the service and pass it to the service using the
- Systemd activation protocol. This by itself does not give so called
- "socket activation", but does allow that any process trying to connect
- to the specified socket will be able to do so, even before the service
- is properly prepared to accept connections.
-
-socket-permissions = (octal permissions mask)
- Gives the permissions for the socket specified using socket-listen.
- Normally this will be 600 (user access only), 660 (user and group
- access), or 666 (all users).
-
-socket-uid = (numeric user id or username)
- Specifies the user that should own the activation socket. If socket-uid
- is specified without also specifying socket-gid, then the socket group
- is the primary group of the specified user (as found in the system user
- database, normally /etc/passwd). If the socket owner is not specified,
- the socket will be owned by the user id of the Dinit process.
-
-socket-gid = (numeric group id or group name)
- Specifies the group of the activation socket. See discussion of
- socket-uid.
-
-termsignal = HUP | INT | QUIT | USR1 | USR2
- Specifies an additional signal to send to the process when requesting it
- to terminate (applies to 'process' services only). SIGTERM is always
- sent along with the specified signal, unless the 'nosigterm' setting is
- set true.
-
options = ( runs-on-console | nosigterm | starts-rwfs | starts-log ) ...
Specifies various options for this service:
startup. This is meaningful only for scripted and bgprocess
services.
-logfile = (log file path)
- Specifies the log file for the service. Output from the service process
- will go this file.
+Please see the manual page for a full list of service parameters and options.
Controlling services
Specifies the command to stop the service. Applicable only to \fBscripted\fR
services.
.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.
+.TP
\fBrestart\fR = {yes | true | no | false}
Indicates whether the service should automatically restart if it stops for
any reason (including unexpected process termination, service dependency
}
if (forkpid == 0) {
- run_child_proc(cmd.data(), logfile, on_console, pipefd[1], control_socket[1], socket_fd);
+ run_child_proc(cmd.data(), logfile, on_console, pipefd[1], control_socket[1], socket_fd,
+ run_as_uid, run_as_gid);
}
else {
// Parent process
+#include <sys/types.h>
+
#include "service.h"
// Given a string and a list of pairs of (start,end) indices for each argument in that string,
dasynq::rearm timer_expiry(eventloop_t &, int expiry_count);
};
+// Base class for process-based services.
class base_process_service : public service_record
{
friend class service_child_watcher;
// <stop_timeout>). 0 to disable.
time_val start_timeout = {60, 0}; // default of 1 minute
+ uid_t run_as_uid = -1;
+ gid_t run_as_gid = -1;
+
pid_t pid = -1; // PID of the process. If state is STARTING or STOPPING,
// this is PID of the service script; otherwise it is the
// PID of the process itself (process service).
this->term_signal = signo;
}
+ // Set the uid/gid that the service process will be run as
+ void set_run_as_uid_gid(uid_t uid, gid_t gid) noexcept
+ {
+ run_as_uid = uid;
+ run_as_gid = gid;
+ }
+
// The restart/stop timer expired.
void timer_expired() noexcept;
};
+// Standard process service.
class process_service : public base_process_service
{
virtual void handle_exit_status(int exit_status) noexcept override;
}
};
+// Bgproc (self-"backgrounding", i.e. double-forking) process service
class bgproc_service : public base_process_service
{
virtual void handle_exit_status(int exit_status) noexcept override;
}
};
+// Service which is started and stopped via separate commands
class scripted_service : public base_process_service
{
virtual void handle_exit_status(int exit_status) noexcept override;
// dep_failed: whether failure is recorded due to a dependency failing
void failed_to_start(bool dep_failed = false) noexcept;
+ // Run a child process (call after forking).
+ // - args specifies the program arguments including the executable (argv[0])
+ // - 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
+ // error number (errno) is written to this file descriptor
+ // - 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) noexcept;
+ int csfd, int socket_fd, uid_t uid, gid_t gid) noexcept;
// A dependency has reached STARTED state
void dependency_started() noexcept;
timespec stop_timeout = { .tv_sec = 10, .tv_nsec = 0 };
timespec start_timeout = { .tv_sec = 60, .tv_nsec = 0 };
+ uid_t run_as_uid = -1;
+ gid_t run_as_gid = -1;
+
string line;
ifstream service_file;
service_file.exceptions(ios::badbit | ios::failbit);
string starttimeout_str = read_setting_value(i, end, nullptr);
parse_timespec(starttimeout_str, name, "start-timeout", start_timeout);
}
+ else if (setting == "run-as") {
+ string run_as_str = read_setting_value(i, end, nullptr);
+ run_as_uid = parse_uid_param("run-as", name, &run_as_gid);
+ }
else {
throw service_description_exc(name, "Unknown setting: " + setting);
}
rvalps->set_start_timeout(start_timeout);
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);
rval = rvalps;
}
else if (service_type == service_type_t::BGPROCESS) {
rvalps->set_start_timeout(start_timeout);
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);
rval = rvalps;
}
else if (service_type == service_type_t::SCRIPTED) {
rvalps->set_start_timeout(start_timeout);
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);
rval = rvalps;
}
else {
#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) noexcept
+ 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().
}
if (socket_fd != -1) {
-
+ // If we passing a pre-opened socket, it has to be fd number 3. (Thanks, systemd).
if (dup2(socket_fd, 3) == -1) goto failure_out;
if (socket_fd != 3) {
close(socket_fd);
tcsetpgrp(0, getpgrp());
}
+ if (uid != -1) {
+ if (setreuid(uid, uid) != 0) goto failure_out;
+ if (setregid(gid, gid) != 0) goto failure_out;
+ }
+
sigprocmask(SIG_SETMASK, &sigwait_set, nullptr);
execvp(args[0], const_cast<char **>(args));
// 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) noexcept
+ int wpipefd, int csfd, int socket_fd, uid_t uid, gid_t gid) noexcept
{
}