chiark / gitweb /
core: add new "scope" unit type for making a unit of pre-existing processes
[elogind.git] / src / core / service.c
index 98266a5a9034699a8071226cc2f4ab3e5346813a..2bc0dc5877d94036819114e701527cb7d6a95401 100644 (file)
@@ -36,7 +36,7 @@
 #include "unit-printf.h"
 #include "dbus-service.h"
 #include "special.h"
-#include "bus-errors.h"
+#include "dbus-common.h"
 #include "exit-status.h"
 #include "def.h"
 #include "path-util.h"
@@ -141,6 +141,7 @@ static void service_init(Unit *u) {
 
         exec_context_init(&s->exec_context);
         kill_context_init(&s->kill_context);
+        cgroup_context_init(&s->cgroup_context);
 
         RATELIMIT_INIT(s->start_limit, 10*USEC_PER_SEC, 5);
 
@@ -220,7 +221,7 @@ static void service_close_socket_fd(Service *s) {
 static void service_connection_unref(Service *s) {
         assert(s);
 
-        if (!UNIT_DEREF(s->accept_socket))
+        if (!UNIT_ISSET(s->accept_socket))
                 return;
 
         socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket)));
@@ -235,7 +236,7 @@ static void service_stop_watchdog(Service *s) {
         s->watchdog_timestamp.monotonic = 0;
 }
 
-static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart);
+static void service_enter_signal(Service *s, ServiceState state, ServiceResult f);
 
 static void service_handle_watchdog(Service *s) {
         usec_t offset;
@@ -249,7 +250,7 @@ static void service_handle_watchdog(Service *s) {
         offset = now(CLOCK_MONOTONIC) - s->watchdog_timestamp.monotonic;
         if (offset >= s->watchdog_usec) {
                 log_error_unit(UNIT(s)->id, "%s watchdog timeout!", UNIT(s)->id);
-                service_enter_dead(s, SERVICE_FAILURE_WATCHDOG, true);
+                service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_WATCHDOG);
                 return;
         }
 
@@ -283,6 +284,7 @@ static void service_done(Unit *u) {
         free(s->status_text);
         s->status_text = NULL;
 
+        cgroup_context_done(&s->cgroup_context);
         exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager));
         exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
         s->control_command = NULL;
@@ -981,7 +983,7 @@ static int service_load_sysv_name(Service *s, const char *name) {
         assert(s);
         assert(name);
 
-       /* For SysV services we strip the *.sh suffixes. */
+        /* For SysV services we strip the *.sh suffixes. */
         if (endswith(name, ".sh.service"))
                 return -ENOENT;
 
@@ -1191,27 +1193,32 @@ static int service_load(Unit *u) {
         assert(s);
 
         /* Load a .service file */
-        if ((r = unit_load_fragment(u)) < 0)
+        r = unit_load_fragment(u);
+        if (r < 0)
                 return r;
 
 #ifdef HAVE_SYSV_COMPAT
         /* Load a classic init script as a fallback, if we couldn't find anything */
-        if (u->load_state == UNIT_STUB)
-                if ((r = service_load_sysv(s)) < 0)
+        if (u->load_state == UNIT_STUB) {
+                r = service_load_sysv(s);
+                if (r < 0)
                         return r;
+        }
 #endif
 
         /* Still nothing found? Then let's give up */
         if (u->load_state == UNIT_STUB)
                 return -ENOENT;
 
-        /* We were able to load something, then let's add in the
-         * dropin directories. */
-        if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
-                return r;
-
         /* This is a new unit? Then let's add in some extras */
         if (u->load_state == UNIT_LOADED) {
+
+                /* We were able to load something, then let's add in
+                 * the dropin directories. */
+                r = unit_load_dropin(u);
+                if (r < 0)
+                        return r;
+
                 if (s->type == _SERVICE_TYPE_INVALID)
                         s->type = s->bus_name ? SERVICE_DBUS : SERVICE_SIMPLE;
 
@@ -1225,7 +1232,7 @@ static int service_load(Unit *u) {
                 if (r < 0)
                         return r;
 
-                r = unit_add_default_cgroups(u);
+                r = unit_add_default_slice(u);
                 if (r < 0)
                         return r;
 
@@ -1275,7 +1282,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
         ServiceExecCommand c;
         Service *s = SERVICE(u);
         const char *prefix2;
-        char _cleanup_free_ *p2 = NULL;
+        _cleanup_free_ char *p2 = NULL;
 
         assert(s);
 
@@ -1373,7 +1380,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
 }
 
 static int service_load_pid_file(Service *s, bool may_warn) {
-        char _cleanup_free_ *k = NULL;
+        _cleanup_free_ char *k = NULL;
         int r;
         pid_t pid;
 
@@ -1453,7 +1460,7 @@ static int service_search_main_pid(Service *s) {
 
         assert(s->main_pid <= 0);
 
-        pid = cgroup_bonding_search_main_pid_list(UNIT(s)->cgroup_bondings);
+        pid = unit_search_main_pid(UNIT(s));
         if (pid <= 0)
                 return -ENOENT;
 
@@ -1466,6 +1473,9 @@ static int service_search_main_pid(Service *s) {
         r = unit_watch_pid(UNIT(s), pid);
         if (r < 0)
                 /* FIXME: we need to do something here */
+                log_warning_unit(UNIT(s)->id,
+                                 "Failed to watch PID %lu from service %s",
+                                 (unsigned long) pid, UNIT(s)->id);
                 return r;
 
         return 0;
@@ -1575,7 +1585,7 @@ static void service_set_state(Service *s, ServiceState state) {
         /* 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)
-                cgroup_bonding_trim_list(UNIT(s)->cgroup_bondings, true);
+                unit_destroy_cgroup(UNIT(s));
 
         if (old_state != state)
                 log_debug_unit(UNIT(s)->id,
@@ -1607,6 +1617,7 @@ static int service_coldplug(Unit *u) {
                     s->deserialized_state == SERVICE_FINAL_SIGTERM ||
                     s->deserialized_state == SERVICE_FINAL_SIGKILL ||
                     s->deserialized_state == SERVICE_AUTO_RESTART) {
+
                         if (s->deserialized_state == SERVICE_AUTO_RESTART || s->timeout_start_usec > 0) {
                                 usec_t k;
 
@@ -1740,15 +1751,18 @@ static int service_spawn(
         pid_t pid;
         int r;
         int *fds = NULL;
-        int _cleanup_free_ *fdsbuf = NULL;
+        _cleanup_free_ int *fdsbuf = NULL;
         unsigned n_fds = 0, n_env = 0;
-        char _cleanup_strv_free_
+        _cleanup_strv_free_ char
                 **argv = NULL, **final_env = NULL, **our_env = NULL;
+        const char *path;
 
         assert(s);
         assert(c);
         assert(_pid);
 
+        unit_realize_cgroup(UNIT(s));
+
         if (pass_fds ||
             s->exec_context.std_input == EXEC_INPUT_SOCKET ||
             s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
@@ -1804,7 +1818,7 @@ static int service_spawn(
                         goto fail;
                 }
 
-        if (s->meta.manager->running_as != SYSTEMD_SYSTEM)
+        if (UNIT(s)->manager->running_as != SYSTEMD_SYSTEM)
                 if (asprintf(our_env + n_env++, "MANAGERPID=%lu", (unsigned long) getpid()) < 0) {
                         r = -ENOMEM;
                         goto fail;
@@ -1816,6 +1830,12 @@ static int service_spawn(
                 goto fail;
         }
 
+        if (is_control && UNIT(s)->cgroup_path) {
+                path = strappenda(UNIT(s)->cgroup_path, "/control");
+                cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
+        } else
+                path = UNIT(s)->cgroup_path;
+
         r = exec_spawn(c,
                        argv,
                        &s->exec_context,
@@ -1825,9 +1845,8 @@ static int service_spawn(
                        apply_chroot,
                        apply_tty_stdin,
                        UNIT(s)->manager->confirm_spawn,
-                       UNIT(s)->cgroup_bondings,
-                       UNIT(s)->cgroup_attributes,
-                       is_control ? "control" : NULL,
+                       UNIT(s)->cgroup_mask,
+                       path,
                        UNIT(s)->id,
                        s->type == SERVICE_IDLE ? UNIT(s)->manager->idle_pipe : NULL,
                        &pid);
@@ -1862,7 +1881,7 @@ static int main_pid_good(Service *s) {
 
                 /* If it's an alien child let's check if it is still
                  * alive ... */
-                if (s->main_pid_alien)
+                if (s->main_pid_alien && s->main_pid > 0)
                         return kill(s->main_pid, 0) >= 0 || errno != ESRCH;
 
                 /* .. otherwise assume we'll get a SIGCHLD for it,
@@ -1875,7 +1894,7 @@ static int main_pid_good(Service *s) {
         return -EAGAIN;
 }
 
-static int control_pid_good(Service *s) {
+_pure_ static int control_pid_good(Service *s) {
         assert(s);
 
         return s->control_pid > 0;
@@ -1886,7 +1905,10 @@ static int cgroup_good(Service *s) {
 
         assert(s);
 
-        r = cgroup_bonding_is_empty_list(UNIT(s)->cgroup_bondings);
+        if (!UNIT(s)->cgroup_path)
+                return 0;
+
+        r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path, true);
         if (r < 0)
                 return r;
 
@@ -1936,8 +1958,6 @@ fail:
         service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false);
 }
 
-static void service_enter_signal(Service *s, ServiceState state, ServiceResult f);
-
 static void service_enter_stop_post(Service *s, ServiceResult f) {
         int r;
         assert(s);
@@ -2118,10 +2138,20 @@ fail:
         service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
 }
 
+static void service_kill_control_processes(Service *s) {
+        char *p;
+
+        if (!UNIT(s)->cgroup_path)
+                return;
+
+        p = strappenda(UNIT(s)->cgroup_path, "/control");
+        cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, p, SIGKILL, true, true, true, NULL);
+}
+
 static void service_enter_start(Service *s) {
+        ExecCommand *c;
         pid_t pid;
         int r;
-        ExecCommand *c;
 
         assert(s);
 
@@ -2136,7 +2166,7 @@ static void service_enter_start(Service *s) {
         /* We want to ensure that nobody leaks processes from
          * START_PRE here, so let's go on a killing spree, People
          * should not spawn long running processes from START_PRE. */
-        cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, true, NULL, "control");
+        service_kill_control_processes(s);
 
         if (s->type == SERVICE_FORKING) {
                 s->control_command_id = SERVICE_EXEC_START;
@@ -2212,11 +2242,9 @@ static void service_enter_start_pre(Service *s) {
 
         s->control_command = s->exec_command[SERVICE_EXEC_START_PRE];
         if (s->control_command) {
-
                 /* Before we start anything, let's clear up what might
                  * be left from previous runs. */
-                cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL,
-                                         true,true, NULL, "control");
+                service_kill_control_processes(s);
 
                 s->control_command_id = SERVICE_EXEC_START_PRE;
 
@@ -2567,7 +2595,7 @@ static int service_reload(Unit *u) {
         return 0;
 }
 
-static bool service_can_reload(Unit *u) {
+_pure_ static bool service_can_reload(Unit *u) {
         Service *s = SERVICE(u);
 
         assert(s);
@@ -2779,7 +2807,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
         return 0;
 }
 
-static UnitActiveState service_active_state(Unit *u) {
+_pure_ static UnitActiveState service_active_state(Unit *u) {
         const UnitActiveState *table;
 
         assert(u);
@@ -2815,7 +2843,7 @@ static bool service_check_gc(Unit *u) {
         return false;
 }
 
-static bool service_check_snapshot(Unit *u) {
+_pure_ static bool service_check_snapshot(Unit *u) {
         Service *s = SERVICE(u);
 
         assert(s);
@@ -3040,7 +3068,6 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                 }
 
         } else if (s->control_pid == pid) {
-
                 s->control_pid = 0;
 
                 if (s->control_command) {
@@ -3061,8 +3088,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                 /* Immediately get rid of the cgroup, so that the
                  * kernel doesn't delay the cgroup empty messages for
                  * the service cgroup any longer than necessary */
-                cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL,
-                                         true, true, NULL, "control");
+                service_kill_control_processes(s);
 
                 if (s->control_command &&
                     s->control_command->command_next &&
@@ -3291,13 +3317,12 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) {
         }
 }
 
-static void service_cgroup_notify_event(Unit *u) {
+static void service_notify_cgroup_empty_event(Unit *u) {
         Service *s = SERVICE(u);
 
         assert(u);
 
-        log_debug_unit(u->id,
-                       "%s: cgroup is empty", u->id);
+        log_debug_unit(u->id, "%s: cgroup is empty", u->id);
 
         switch (s->state) {
 
@@ -3444,10 +3469,10 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) {
 static int service_enumerate(Manager *m) {
         char **p;
         unsigned i;
-        DIR _cleanup_closedir_ *d = NULL;
-        char _cleanup_free_ *path = NULL, *fpath = NULL, *name = NULL;
-        Set *runlevel_services[ELEMENTSOF(rcnd_table)];
-        Set _cleanup_set_free_ *shutdown_services = NULL;
+        _cleanup_closedir_ DIR *d = NULL;
+        _cleanup_free_ char *path = NULL, *fpath = NULL, *name = NULL;
+        Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
+        _cleanup_set_free_ Set *shutdown_services = NULL;
         Unit *service;
         Iterator j;
         int r;
@@ -3457,8 +3482,6 @@ static int service_enumerate(Manager *m) {
         if (m->running_as != SYSTEMD_SYSTEM)
                 return 0;
 
-        zero(runlevel_services);
-
         STRV_FOREACH(p, m->lookup_paths.sysvrcnd_path)
                 for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
                         struct dirent *de;
@@ -3820,8 +3843,9 @@ const UnitVTable service_vtable = {
                 "Service\0"
                 "Install\0",
 
+        .private_section = "Service",
         .exec_context_offset = offsetof(Service, exec_context),
-        .exec_section = "Service",
+        .cgroup_context_offset = offsetof(Service, cgroup_context),
 
         .init = service_init,
         .done = service_done,
@@ -3854,7 +3878,7 @@ const UnitVTable service_vtable = {
 
         .reset_failed = service_reset_failed,
 
-        .cgroup_notify_empty = service_cgroup_notify_event,
+        .notify_cgroup_empty = service_notify_cgroup_empty_event,
         .notify_message = service_notify_message,
 
         .bus_name_owner_change = service_bus_name_owner_change,
@@ -3863,6 +3887,10 @@ const UnitVTable service_vtable = {
         .bus_interface = "org.freedesktop.systemd1.Service",
         .bus_message_handler = bus_service_message_handler,
         .bus_invalidating_properties =  bus_service_invalidating_properties,
+        .bus_set_property = bus_service_set_property,
+        .bus_commit_properties = bus_service_commit_properties,
+
+        .can_transient = true,
 
 #ifdef HAVE_SYSV_COMPAT
         .enumerate = service_enumerate,