If a service does not consume CPU during some time(can be configured by ExitOnIdleSec=) and set to stopped on idle state(ExitOnIdle=), the service will be stopped. This can be useful if the service provides some of activation methods. --- src/core/load-fragment-gperf.gperf.m4 | 2 + src/core/service.c | 97 +++++++++++++++++++++++++++++++++++ src/core/service.h | 5 ++ src/core/unit.h | 1 + 4 files changed, 105 insertions(+)
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 5305984..60d573e 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -229,6 +229,8 @@ Service.BusName, config_parse_bus_name, 0, Service.FileDescriptorStoreMax, config_parse_unsigned, 0, offsetof(Service, n_fd_store_max) Service.NotifyAccess, config_parse_notify_access, 0, offsetof(Service, notify_access) Service.Sockets, config_parse_service_sockets, 0, 0 +Service.ExitOnIdle, config_parse_bool, 0, offsetof(Service, exit_on_idle) +Service.ExitOnIdleSec, config_parse_sec, 0, offsetof(Service, exit_on_idle_usec) m4_ifdef(`ENABLE_KDBUS', `Service.BusPolicy, config_parse_bus_endpoint_policy, 0, offsetof(Service, exec_context)', `Service.BusPolicy, config_parse_warn_compat, DISABLED_EXPERIMENTAL, 0') diff --git a/src/core/service.c b/src/core/service.c index fa818fc..c8752ae 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -91,6 +91,7 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] = static int service_dispatch_io(sd_event_source *source, int fd, uint32_t events, void *userdata); static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void *userdata); +static int service_dispatch_exit_on_idle_timer(sd_event_source *source, usec_t usec, void *userdata); static void service_enter_signal(Service *s, ServiceState state, ServiceResult f); static void service_enter_reload_by_notify(Service *s); @@ -108,6 +109,8 @@ static void service_init(Unit *u) { s->socket_fd = -1; s->bus_endpoint_fd = -1; s->guess_main_pid = true; + s->exit_on_idle = false; + s->exit_on_idle_usec = 0; RATELIMIT_INIT(s->start_limit, u->manager->default_start_limit_interval, u->manager->default_start_limit_burst); @@ -279,6 +282,56 @@ static void service_release_resources(Unit *u) { assert(s->n_fd_store == 0); } +static void service_stop_exit_on_idle_timer(Service *s) { + assert(s); + + s->exit_on_idle_event_source = sd_event_source_unref(s->exit_on_idle_event_source); + s->exit_on_idle_timestamp = DUAL_TIMESTAMP_NULL; +} + +static void service_start_exit_on_idle_timer(Service *s) { + int r; + + assert(s); + + if (s->exit_on_idle_usec <= 0) + return; + + if (s->exit_on_idle_event_source) { + r = sd_event_source_set_time(s->exit_on_idle_event_source, s->exit_on_idle_timestamp.monotonic + s->exit_on_idle_usec); + if (r < 0) { + log_unit_warning(UNIT(s)->id, "%s failed to reset exit-on-idle timer: %s", UNIT(s)->id, strerror(-r)); + return; + } + + r = sd_event_source_set_enabled(s->exit_on_idle_event_source, SD_EVENT_ON); + } else { + r = sd_event_add_time( + UNIT(s)->manager->event, + &s->exit_on_idle_event_source, + CLOCK_MONOTONIC, + s->exit_on_idle_timestamp.monotonic + s->exit_on_idle_usec, 0, + service_dispatch_exit_on_idle_timer, s); + if (r < 0) { + log_unit_warning(UNIT(s)->id, "%s failed to add exit-on-idle timer: %s", UNIT(s)->id, strerror(-r)); + return; + } + + r = sd_event_source_set_priority(s->exit_on_idle_event_source, SD_EVENT_PRIORITY_IDLE); + } + + if (r < 0) + log_unit_warning(UNIT(s)->id, "%s failed to install exit-on-idle timer: %s", UNIT(s)->id, strerror(-r)); + return; +} + +static void service_reset_exit_on_idle_timer(Service *s) { + assert(s); + + dual_timestamp_get(&s->exit_on_idle_timestamp); + service_start_exit_on_idle_timer(s); +} + static void service_done(Unit *u) { Service *s = SERVICE(u); @@ -518,6 +571,7 @@ static void service_fix_output(Service *s) { } static int service_add_extras(Service *s) { + CGroupContext *cc; int r; assert(s); @@ -571,6 +625,15 @@ static int service_add_extras(Service *s) { return r; } + if (s->exit_on_idle && s->exit_on_idle_usec <= 0) + s->exit_on_idle_usec = USEC_PER_MINUTE; + + if (s->exit_on_idle) { + cc = unit_get_cgroup_context(UNIT(s)); + if (cc) + cc->cpu_accounting = true; + } + if (UNIT(s)->default_dependencies) { r = service_add_default_dependencies(s); if (r < 0) @@ -850,6 +913,12 @@ static void service_set_state(Service *s, ServiceState state) { if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) service_stop_watchdog(s); + if (IN_SET(state, SERVICE_RUNNING)) + service_start_exit_on_idle_timer(s); + + if (IN_SET(state, SERVICE_DEAD)) + service_stop_exit_on_idle_timer(s); + /* For the inactive states unit_notify() will trim the cgroup, * but for exit we have to do that ourselves... */ if (state == SERVICE_EXITED && UNIT(s)->manager->n_reloading <= 0) @@ -2770,6 +2839,34 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void format_timespan(t, sizeof(t), s->watchdog_usec, 1)); service_enter_signal(s, SERVICE_STOP_SIGABRT, SERVICE_FAILURE_WATCHDOG); + return 0; +} + +static int service_dispatch_exit_on_idle_timer(sd_event_source *source, usec_t usec, void *userdata) { + Service *s = SERVICE(userdata); + nsec_t usage; + int r; + + assert(s); + assert(source == s->exit_on_idle_event_source); + + r = unit_get_cpu_usage(UNIT(s), &usage); + if (r < 0) { + log_unit_error_errno(UNIT(s)->id, r, "%s failed to get cpu usage: %m", UNIT(s)->id); + return 0; + } + + if (usage == UNIT(s)->cpuacct_usage) { + log_unit_info(UNIT(s)->id, "%s is in idle state, stopping...", UNIT(s)->id); + r = manager_add_job(UNIT(s)->manager, JOB_STOP, UNIT(s), JOB_FAIL, true, NULL, NULL); + if (r < 0) { + log_unit_error_errno(UNIT(s)->id, r, "%s failed to enqueue stopping job: %m", UNIT(s)->id); + return r; + } + } else { + UNIT(s)->cpuacct_usage = usage; + service_reset_exit_on_idle_timer(s); + } return 0; } diff --git a/src/core/service.h b/src/core/service.h index 7da0a93..909908e 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -212,6 +212,11 @@ struct Service { ServiceFDStore *fd_store; unsigned n_fd_store; unsigned n_fd_store_max; + + bool exit_on_idle; + dual_timestamp exit_on_idle_timestamp; + usec_t exit_on_idle_usec; + sd_event_source *exit_on_idle_event_source; }; extern const UnitVTable service_vtable; diff --git a/src/core/unit.h b/src/core/unit.h index 11242c2..84c0cd0 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -178,6 +178,7 @@ struct Unit { /* Where the cpuacct.usage cgroup counter was at the time the unit was started */ nsec_t cpuacct_usage_base; + nsec_t cpuacct_usage; /* Counterparts in the cgroup filesystem */ char *cgroup_path; -- 1.9.3 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel