if (! working_dir.empty()) working_dir_c = working_dir.c_str();
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);
+ socket_fd, notify_pipe[1], force_notification_fd, nullptr, run_as_uid, run_as_gid, rlimits);
}
else {
// Parent process
#include <sys/types.h>
+#include <sys/resource.h>
#include "baseproc-sys.h"
#include "service.h"
std::vector<const char *> separate_args(std::string &s,
const std::list<std::pair<unsigned,unsigned>> &arg_indices);
+struct service_rlimits
+{
+ int resource_id; // RLIMIT_xxx identifying resource
+ bool soft_set : 1;
+ bool hard_set : 1;
+ struct rlimit limits;
+
+ service_rlimits(int id) : resource_id(id), soft_set(0), hard_set(0), limits({0,0}) { }
+};
+
class base_process_service;
// A timer for process restarting. Used to ensure a minimum delay between process restarts (and
string working_dir; // working directory (or empty)
+ std::vector<service_rlimits> rlimits; // resource limits
+
service_child_watcher child_listener;
exec_status_pipe_watcher child_status_listener;
process_restart_timer restart_timer;
bool reserved_child_watch : 1;
bool tracking_child : 1; // whether we expect to see child process status
+ // Run a child process (call after forking). Note that some arguments specify file descriptors,
+ // but in general file descriptors may be moved before the exec call.
+ // - args specifies the program arguments including the executable (argv[0])
+ // - working_dir specifies the working directory; may be null
+ // - logfile specifies the logfile (where stdout/stderr are directed)
+ // - 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)
+ // - notify_fd: the readiness notification fd; process should write to this descriptor when
+ // is is ready
+ // - force_notify_fd: if not -1, specifies the file descriptor that notify_fd should be moved
+ // to (via dup2 and close of the original).
+ // - notify_var: the name of an environment variable which will be set to contain the notification
+ // fd
+ // - 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 *working_dir, const char *logfile,
+ bool on_console, int wpipefd, int csfd, int socket_fd, int notify_fd, int force_notify_fd,
+ const char *notify_var,uid_t uid, gid_t gid, const std::vector<service_rlimits> &rlimits) noexcept;
+
// Launch the process with the given arguments, return true on success
bool start_ps_process(const std::vector<const char *> &args, bool on_console) noexcept;
stop_arg_parts = separate_args(stop_command, stop_command_offsets);
}
+ void set_rlimits(std::vector<service_rlimits> &&rlimits_p)
+ {
+ rlimits = std::move(rlimits_p);
+ }
+
void set_restart_interval(timespec interval, int max_restarts) noexcept
{
restart_interval = interval;
// dep_failed: whether failure is recorded due to a dependency failing
// immediate_stop: whether to set state as STOPPED and handle complete stop.
void failed_to_start(bool dep_failed = false, bool immediate_stop = true) noexcept;
-
- // Run a child process (call after forking). Note that some arguments specify file descriptors,
- // but in general file descriptors may be moved before the exec call.
- // - args specifies the program arguments including the executable (argv[0])
- // - working_dir specifies the working directory; may be null
- // - logfile specifies the logfile (where stdout/stderr are directed)
- // - 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)
- // - notify_fd: the readiness notification fd; process should write to this descriptor when
- // is is ready
- // - force_notify_fd: if not -1, specifies the file descriptor that notify_fd should be moved
- // to (via dup2 and close of the original).
- // - notify_var: the name of an environment variable which will be set to contain the notification
- // fd
- // - 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 *working_dir, const char *logfile,
- bool on_console, int wpipefd, int csfd, int socket_fd, int notify_fd, int force_notify_fd,
- const char *notify_var,uid_t uid, gid_t gid) noexcept;
// A dependency has reached STARTED state
void dependency_started() noexcept;
// Parse a time, specified as a decimal number of seconds (with optional fractional component after decimal
// point or decimal comma).
-//
static void parse_timespec(const std::string ¶mval, const std::string &servicename,
const char * paramname, timespec &ts)
{
ts.tv_nsec = insec;
}
+// In a vector, find or create rlimits for a particular resource type.
+static service_rlimits &find_rlimits(std::vector<service_rlimits> all_rlimits, int resource_id)
+{
+ for (service_rlimits &limits : all_rlimits) {
+ if (limits.resource_id == resource_id) {
+ return limits;
+ }
+ }
+
+ all_rlimits.emplace_back(resource_id);
+ return all_rlimits.back();
+}
+
+// Parse resource limits setting (can specify both hard and soft limit).
+static void parse_rlimit(const std::string &line, const std::string &service_name, const char *param_name,
+ service_rlimits &rlimit)
+{
+ // Examples:
+ // 4:5 - soft:hard limits both set
+ // 4:- soft set, hard set to unlimited
+ // 4: soft set, hard limit unchanged
+ // 4 soft and hard limit set to same limit
+
+ if (line.empty()) {
+ throw service_description_exc(service_name, std::string("Bad value for ") + param_name);
+ }
+
+ const char *cline = line.c_str();
+ rlimit.hard_set = rlimit.soft_set = false;
+
+ try {
+ char * index;
+ errno = 0;
+ if (cline[0] != ':') {
+ rlimit.soft_set = true;
+ if (cline[0] == '-') {
+ rlimit.limits.rlim_cur = RLIM_INFINITY;
+ }
+ else {
+ unsigned long long limit = std::strtoull(cline, &index, 0);
+ if (errno == ERANGE || limit > std::numeric_limits<rlim_t>::max()) throw std::out_of_range("");
+ if (index == cline) throw std::invalid_argument("");
+ rlimit.limits.rlim_cur = limit;
+ }
+
+ if (*index == 0) {
+ rlimit.hard_set = true;
+ rlimit.limits.rlim_max = rlimit.limits.rlim_cur;
+ return;
+ }
+ }
+
+ if (*index != ':') {
+ throw service_description_exc(service_name, std::string("Bad value for ") + param_name);
+ }
+
+ index++;
+ if (*index == 0) return;
+
+ if (*index == '-') {
+ rlimit.limits.rlim_max = RLIM_INFINITY;
+ }
+ else {
+ char *hard_start = index;
+ unsigned long long limit = std::strtoull(cline, &index, 0);
+ if (errno == ERANGE || limit > std::numeric_limits<rlim_t>::max()) throw std::out_of_range("");
+ if (index == hard_start) throw std::invalid_argument("");
+ rlimit.limits.rlim_max = limit;
+ }
+ }
+ catch (std::invalid_argument &exc) {
+ throw service_description_exc(service_name, std::string("Bad value for ") + param_name);
+ }
+ catch (std::out_of_range &exc) {
+ throw service_description_exc(service_name, std::string("Too-large value for ") + param_name);
+ }
+}
+
// Perform environment variable substitution on a command line, if specified.
// line - the string storing the command and arguments
// offsets - the [start,end) pair of offsets of the command and each argument within the string
timespec restart_delay = { .tv_sec = 0, .tv_nsec = 200000000 };
timespec stop_timeout = { .tv_sec = 10, .tv_nsec = 0 };
timespec start_timeout = { .tv_sec = 60, .tv_nsec = 0 };
+ std::vector<service_rlimits> rlimits;
int readiness_fd = -1; // readiness fd in service process
std::string readiness_var; // environment var to hold readiness fd
strncpy(inittab_line, inittab_setting.c_str(), sizeof(inittab_line));
#endif
}
+ else if (setting == "rlimit-nofile") {
+ string nofile_setting = read_setting_value(i, end, nullptr);
+ service_rlimits &nofile_limits = find_rlimits(rlimits, RLIMIT_NOFILE);
+ parse_rlimit(line, name, "rlimit-nofile", nofile_limits);
+ }
+ else if (setting == "rlimit-core") {
+ string nofile_setting = read_setting_value(i, end, nullptr);
+ service_rlimits &nofile_limits = find_rlimits(rlimits, RLIMIT_CORE);
+ parse_rlimit(line, name, "rlimit-core", nofile_limits);
+ }
+ else if (setting == "rlimit-data") {
+ string nofile_setting = read_setting_value(i, end, nullptr);
+ service_rlimits &nofile_limits = find_rlimits(rlimits, RLIMIT_DATA);
+ parse_rlimit(line, name, "rlimit-data", nofile_limits);
+ }
+ else if (setting == "rlimit-addrspace") {
+ string nofile_setting = read_setting_value(i, end, nullptr);
+ service_rlimits &nofile_limits = find_rlimits(rlimits, RLIMIT_AS);
+ parse_rlimit(line, name, "rlimit-addrspace", nofile_limits);
+ }
else {
throw service_description_exc(name, "Unknown setting: " + setting);
}
auto rvalps = new process_service(this, string(name), std::move(command),
command_offsets, depends);
rvalps->set_workding_dir(working_dir);
+ rvalps->set_rlimits(std::move(rlimits));
rvalps->set_restart_interval(restart_interval, max_restarts);
rvalps->set_restart_delay(restart_delay);
rvalps->set_stop_timeout(stop_timeout);
rvalps->set_start_timeout(start_timeout);
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);
rvalps->set_notification_fd(readiness_fd);
rvalps->set_notification_var(std::move(readiness_var));
#if USE_UTMPX
auto rvalps = new bgproc_service(this, string(name), std::move(command),
command_offsets, depends);
rvalps->set_workding_dir(working_dir);
+ rvalps->set_rlimits(std::move(rlimits));
rvalps->set_pid_file(std::move(pid_file));
rvalps->set_restart_interval(restart_interval, max_restarts);
rvalps->set_restart_delay(restart_delay);
command_offsets, depends);
rvalps->set_stop_command(stop_command, stop_command_offsets);
rvalps->set_workding_dir(working_dir);
+ rvalps->set_rlimits(std::move(rlimits));
rvalps->set_stop_timeout(stop_timeout);
rvalps->set_start_timeout(start_timeout);
rvalps->set_extra_termination_signal(term_signal);
#include <termios.h>
#include "service.h"
+#include "proc-service.h"
// Move an fd, if necessary, to another fd. The destination fd must be available (not open).
// if fd is specified as -1, returns -1 immediately. Returns 0 on success.
return 0;
}
-void service_record::run_child_proc(const char * const *args, const char *working_dir,
+void base_process_service::run_child_proc(const char * const *args, const char *working_dir,
const char *logfile, bool on_console, int wpipefd, int csfd, int socket_fd,
int notify_fd, int force_notify_fd, const char *notify_var,
- uid_t uid, gid_t gid) noexcept
+ uid_t uid, gid_t gid, const std::vector<service_rlimits> &rlimits) noexcept
{
// Child process. Must not risk throwing any uncaught exception from here until exit().
tcsetpgrp(0, getpgrp());
}
+ // Resource limits
+ for (auto &limit : rlimits) {
+ rlimit setlimits;
+ if (!limit.hard_set || !limit.soft_set) {
+ // if either hard or soft limit is not set, use current:
+ if (getrlimit(limit.resource_id, &setlimits) != 0) goto failure_out;
+ }
+ if (limit.hard_set) setlimits.rlim_max = limit.limits.rlim_max;
+ if (limit.soft_set) setlimits.rlim_cur = limit.limits.rlim_cur;
+ if (setrlimit(limit.resource_id, &setlimits) != 0) goto failure_out;
+ }
+
if (uid != uid_t(-1)) {
if (setreuid(uid, uid) != 0) goto failure_out;
if (setregid(gid, gid) != 0) goto failure_out;
-#include "service.h"
+#include <vector>
+
+#include "proc-service.h"
// Stub out run_child_proc function, for testing purposes.
-void service_record::run_child_proc(const char * const *args, const char *working_dir,
+void base_process_service::run_child_proc(const char * const *args, const char *working_dir,
const char *logfile, bool on_console, int wpipefd, int csfd, int socket_fd,
- int notify_fd, int forced_notify_fd, const char * notify_var, uid_t uid, gid_t gid) noexcept
+ int notify_fd, int forced_notify_fd, const char * notify_var, uid_t uid, gid_t gid,
+ const std::vector<service_rlimits> &rlimits) noexcept
{
}