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