diff options
-rw-r--r-- | src/dbus-service.c | 7 | ||||
-rw-r--r-- | src/load-fragment-gperf.gperf.m4 | 3 | ||||
-rw-r--r-- | src/load-fragment.c | 1 | ||||
-rw-r--r-- | src/load-fragment.h | 1 | ||||
-rw-r--r-- | src/service.c | 67 | ||||
-rw-r--r-- | src/service.h | 16 |
6 files changed, 89 insertions, 6 deletions
diff --git a/src/dbus-service.c b/src/dbus-service.c index fedfc1d523..780916419b 100644 --- a/src/dbus-service.c +++ b/src/dbus-service.c @@ -46,6 +46,9 @@ " <property name=\"WatchdogUSec\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"WatchdogTimestamp\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"WatchdogTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \ + " <property name=\"StartLimitInterval\" type=\"t\" access=\"read\"/>\n" \ + " <property name=\"StartLimitBurst\" type=\"u\" access=\"read\"/>\n" \ + " <property name=\"StartLimitAction\" type=\"s\" access=\"read\"/>\n" \ BUS_EXEC_COMMAND_INTERFACE("ExecStartPre") \ BUS_EXEC_COMMAND_INTERFACE("ExecStart") \ BUS_EXEC_COMMAND_INTERFACE("ExecStartPost") \ @@ -101,6 +104,7 @@ static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_type, service_type, Se static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_restart, service_restart, ServiceRestart); static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_notify_access, notify_access, NotifyAccess); static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_service_result, service_result, ServiceResult); +static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_start_limit_action, start_limit_action, StartLimitAction); static const BusProperty bus_exec_main_status_properties[] = { { "ExecMainStartTimestamp", bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.realtime) }, @@ -123,6 +127,9 @@ static const BusProperty bus_service_properties[] = { { "WatchdogUSec", bus_property_append_usec, "t", offsetof(Service, watchdog_usec) }, { "WatchdogTimestamp", bus_property_append_usec, "t", offsetof(Service, watchdog_timestamp.realtime) }, { "WatchdogTimestampMonotonic",bus_property_append_usec, "t", offsetof(Service, watchdog_timestamp.monotonic) }, + { "StartLimitInterval", bus_property_append_usec, "t", offsetof(Service, start_limit.interval) }, + { "StartLimitBurst", bus_property_append_uint32, "u", offsetof(Service, start_limit.burst) }, + { "StartLimitAction", bus_service_append_start_limit_action,"s", offsetof(Service, start_limit_action) }, BUS_EXEC_COMMAND_PROPERTY("ExecStartPre", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), true ), BUS_EXEC_COMMAND_PROPERTY("ExecStart", offsetof(Service, exec_command[SERVICE_EXEC_START]), true ), BUS_EXEC_COMMAND_PROPERTY("ExecStartPost", offsetof(Service, exec_command[SERVICE_EXEC_START_POST]), true ), diff --git a/src/load-fragment-gperf.gperf.m4 b/src/load-fragment-gperf.gperf.m4 index 9708ff8283..44ce4bbbc4 100644 --- a/src/load-fragment-gperf.gperf.m4 +++ b/src/load-fragment-gperf.gperf.m4 @@ -136,6 +136,9 @@ Service.ExecStopPost, config_parse_exec, SERVICE_EXE Service.RestartSec, config_parse_usec, 0, offsetof(Service, restart_usec) Service.TimeoutSec, config_parse_usec, 0, offsetof(Service, timeout_usec) Service.WatchdogSec, config_parse_usec, 0, offsetof(Service, watchdog_usec) +Service.StartLimitInterval, config_parse_usec, 0, offsetof(Service, start_limit.interval) +Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Service, start_limit.burst) +Service.StartLimitAction, config_parse_start_limit_action, 0, offsetof(Service, start_limit_action) Service.Type, config_parse_service_type, 0, offsetof(Service, type) Service.Restart, config_parse_service_restart, 0, offsetof(Service, restart) Service.PermissionsStartOnly, config_parse_bool, 0, offsetof(Service, permissions_start_only) diff --git a/src/load-fragment.c b/src/load-fragment.c index b2d43fb73b..b963d7f995 100644 --- a/src/load-fragment.c +++ b/src/load-fragment.c @@ -1648,6 +1648,7 @@ int config_parse_unit_condition_null( } DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier"); int config_parse_unit_cgroup_attr( const char *filename, diff --git a/src/load-fragment.h b/src/load-fragment.h index fbb31f9b9a..79fc76da92 100644 --- a/src/load-fragment.h +++ b/src/load-fragment.h @@ -76,6 +76,7 @@ int config_parse_unit_condition_string(const char *filename, unsigned line, cons int config_parse_unit_condition_null(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_kill_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_notify_access(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_start_limit_action(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_cgroup_attr(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/service.c b/src/service.c index 9ccb1b6bc1..e782421389 100644 --- a/src/service.c +++ b/src/service.c @@ -23,7 +23,9 @@ #include <signal.h> #include <dirent.h> #include <unistd.h> +#include <sys/reboot.h> +#include "manager.h" #include "unit.h" #include "service.h" #include "load-fragment.h" @@ -125,7 +127,7 @@ static void service_init(Unit *u) { exec_context_init(&s->exec_context); - RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5); + RATELIMIT_INIT(s->start_limit, 10*USEC_PER_SEC, 5); s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; } @@ -2324,8 +2326,56 @@ fail: service_enter_stop(s, SERVICE_FAILURE_RESOURCES); } +static int service_start_limit_test(Service *s) { + assert(s); + + if (ratelimit_test(&s->start_limit)) + return 0; + + switch (s->start_limit_action) { + + case SERVICE_START_LIMIT_NONE: + log_warning("%s start request repeated too quickly, refusing to start.", UNIT(s)->id); + break; + + case SERVICE_START_LIMIT_REBOOT: { + DBusError error; + int r; + + dbus_error_init(&error); + + log_warning("%s start request repeated too quickly, rebooting.", UNIT(s)->id); + + r = manager_add_job_by_name(UNIT(s)->manager, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, true, &error, NULL); + if (r < 0) { + log_error("Failed to reboot: %s.", bus_error(&error, r)); + dbus_error_free(&error); + } + + break; + } + + case SERVICE_START_LIMIT_REBOOT_FORCE: + log_warning("%s start request repeated too quickly, force rebooting.", UNIT(s)->id); + UNIT(s)->manager->exit_code = MANAGER_REBOOT; + break; + + case SERVICE_START_LIMIT_REBOOT_IMMEDIATE: + log_warning("%s start request repeated too quickly, rebooting immediately.", UNIT(s)->id); + reboot(RB_AUTOBOOT); + break; + + default: + log_error("start limit action=%i", s->start_limit_action); + assert_not_reached("Unknown StartLimitAction."); + } + + return -ECANCELED; +} + static int service_start(Unit *u) { Service *s = SERVICE(u); + int r; assert(s); @@ -2348,10 +2398,9 @@ static int service_start(Unit *u) { assert(s->state == SERVICE_DEAD || s->state == SERVICE_FAILED || s->state == SERVICE_AUTO_RESTART); /* Make sure we don't enter a busy loop of some kind. */ - if (!ratelimit_test(&s->ratelimit)) { - log_warning("%s start request repeated too quickly, refusing to start.", u->id); - return -ECANCELED; - } + r = service_start_limit_test(s); + if (r < 0) + return r; s->result = SERVICE_SUCCESS; s->reload_result = SERVICE_SUCCESS; @@ -3665,6 +3714,14 @@ static const char* const service_result_table[_SERVICE_RESULT_MAX] = { DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult); +static const char* const start_limit_action_table[_SERVICE_START_LIMIT_MAX] = { + [SERVICE_START_LIMIT_NONE] = "none", + [SERVICE_START_LIMIT_REBOOT] = "reboot", + [SERVICE_START_LIMIT_REBOOT_FORCE] = "reboot-force", + [SERVICE_START_LIMIT_REBOOT_IMMEDIATE] = "reboot-immediate" +}; +DEFINE_STRING_TABLE_LOOKUP(start_limit_action, StartLimitAction); + const UnitVTable service_vtable = { .suffix = ".service", .object_size = sizeof(Service), diff --git a/src/service.h b/src/service.h index 02726efe25..60b10516eb 100644 --- a/src/service.h +++ b/src/service.h @@ -100,6 +100,15 @@ typedef enum ServiceResult { _SERVICE_RESULT_INVALID = -1 } ServiceResult; +typedef enum StartLimitAction { + SERVICE_START_LIMIT_NONE, + SERVICE_START_LIMIT_REBOOT, + SERVICE_START_LIMIT_REBOOT_FORCE, + SERVICE_START_LIMIT_REBOOT_IMMEDIATE, + _SERVICE_START_LIMIT_MAX, + _SERVICE_START_LIMIT_INVALID = -1 +} StartLimitAction; + struct Service { Unit meta; @@ -169,7 +178,9 @@ struct Service { char *status_text; - RateLimit ratelimit; + RateLimit start_limit; + StartLimitAction start_limit_action; + UnitRef accept_socket; @@ -203,4 +214,7 @@ NotifyAccess notify_access_from_string(const char *s); const char* service_result_to_string(ServiceResult i); ServiceResult service_result_from_string(const char *s); +const char* start_limit_action_to_string(StartLimitAction i); +StartLimitAction start_limit_action_from_string(const char *s); + #endif |