chiark / gitweb /
service: always unwatch PIDs before forgetting old ones
[elogind.git] / src / core / service.c
index 61b150cba8e55fff42109bce0bea6e520d4e6754..df49ce18539929db861df1991b0499900ac3ef54 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);
 
@@ -190,6 +191,8 @@ static int service_set_main_pid(Service *s, pid_t pid) {
         if (pid == getpid())
                 return -EINVAL;
 
+        service_unwatch_main_pid(s);
+
         s->main_pid = pid;
         s->main_pid_known = true;
 
@@ -220,7 +223,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 +238,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 +252,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,7 +286,8 @@ static void service_done(Unit *u) {
         free(s->status_text);
         s->status_text = NULL;
 
-        exec_context_done(&s->exec_context);
+        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;
         s->main_command = NULL;
@@ -331,9 +335,6 @@ static char *sysv_translate_name(const char *name) {
         if (endswith(name, ".sh"))
                 /* Drop .sh suffix */
                 strcpy(stpcpy(r, name) - 3, ".service");
-        if (startswith(name, "rc."))
-                /* Drop rc. prefix */
-                strcpy(stpcpy(r, name + 3), ".service");
         else
                 /* Normal init script name */
                 strcpy(stpcpy(r, name), ".service");
@@ -765,7 +766,7 @@ static int service_load_sysv_path(Service *s, const char *path) {
                                                 continue;
 
                                         if (unit_name_to_type(m) == UNIT_SERVICE)
-                                                r = unit_add_name(u, m);
+                                                r = unit_merge_by_name(u, m);
                                         else
                                                 /* NB: SysV targets
                                                  * which are provided
@@ -984,10 +985,8 @@ static int service_load_sysv_name(Service *s, const char *name) {
         assert(s);
         assert(name);
 
-        /* For SysV services we strip the rc.* and *.sh
-         * prefixes/suffixes. */
-        if (startswith(name, "rc.") ||
-            endswith(name, ".sh.service"))
+        /* For SysV services we strip the *.sh suffixes. */
+        if (endswith(name, ".sh.service"))
                 return -ENOENT;
 
         STRV_FOREACH(p, UNIT(s)->manager->lookup_paths.sysvinit_path) {
@@ -1010,19 +1009,6 @@ static int service_load_sysv_name(Service *s, const char *name) {
                 }
                 free(path);
 
-                if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
-                        /* Try rc.* init scripts */
-
-                        path = strjoin(*p, "/rc.", name, NULL);
-                        if (!path)
-                                return -ENOMEM;
-
-                        /* Drop .service suffix */
-                        path[strlen(path)-8] = 0;
-                        r = service_load_sysv_path(s, path);
-                        free(path);
-                }
-
                 if (r < 0)
                         return r;
 
@@ -1166,6 +1152,16 @@ static int service_add_default_dependencies(Service *s) {
                                                       SPECIAL_SOCKETS_TARGET, NULL, true);
                 if (r < 0)
                         return r;
+
+                r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES,
+                                                      SPECIAL_TIMERS_TARGET, NULL, true);
+                if (r < 0)
+                        return r;
+
+                r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES,
+                                                      SPECIAL_PATHS_TARGET, NULL, true);
+                if (r < 0)
+                        return r;
         }
 
         /* Second, activate normal shutdown */
@@ -1199,27 +1195,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;
 
@@ -1233,7 +1234,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;
 
@@ -1283,7 +1284,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);
 
@@ -1381,7 +1382,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;
 
@@ -1461,7 +1462,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;
 
@@ -1474,6 +1475,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;
@@ -1583,7 +1587,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,
@@ -1615,6 +1619,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;
 
@@ -1748,15 +1753,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 ||
@@ -1812,7 +1820,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;
@@ -1824,6 +1832,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,
@@ -1833,9 +1847,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);
@@ -1870,7 +1883,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,
@@ -1883,7 +1896,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;
@@ -1894,7 +1907,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;
 
@@ -1932,6 +1948,9 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
 
         s->forbid_restart = false;
 
+        /* we want fresh tmpdirs in case service is started again immediately */
+        exec_context_tmp_dirs_done(&s->exec_context);
+
         return;
 
 fail:
@@ -1941,8 +1960,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);
@@ -1972,7 +1989,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) {
 
                 service_set_state(s, SERVICE_STOP_POST);
         } else
-                service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_SUCCESS);
+                service_enter_dead(s, SERVICE_SUCCESS, true);
 
         return;
 
@@ -2123,25 +2140,33 @@ 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);
 
         assert(s->exec_command[SERVICE_EXEC_START]);
         assert(!s->exec_command[SERVICE_EXEC_START]->command_next || s->type == SERVICE_ONESHOT);
 
-        if (s->type == SERVICE_FORKING)
-                service_unwatch_control_pid(s);
-        else
-                service_unwatch_main_pid(s);
+        service_unwatch_control_pid(s);
+        service_unwatch_main_pid(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;
@@ -2217,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;
 
@@ -2572,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);
@@ -2638,6 +2661,12 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
                 dual_timestamp_serialize(f, "watchdog-timestamp",
                                          &s->watchdog_timestamp);
 
+        if (s->exec_context.tmp_dir)
+                unit_serialize_item(u, f, "tmp-dir", s->exec_context.tmp_dir);
+
+        if (s->exec_context.var_tmp_dir)
+                unit_serialize_item(u, f, "var-tmp-dir", s->exec_context.var_tmp_dir);
+
         return 0;
 }
 
@@ -2756,13 +2785,29 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
                 dual_timestamp_deserialize(value, &s->main_exec_status.exit_timestamp);
         else if (streq(key, "watchdog-timestamp"))
                 dual_timestamp_deserialize(value, &s->watchdog_timestamp);
-        else
+        else if (streq(key, "tmp-dir")) {
+                char *t;
+
+                t = strdup(value);
+                if (!t)
+                        return log_oom();
+
+                s->exec_context.tmp_dir = t;
+        } else if (streq(key, "var-tmp-dir")) {
+                char *t;
+
+                t = strdup(value);
+                if (!t)
+                        return log_oom();
+
+                s->exec_context.var_tmp_dir = t;
+        } else
                 log_debug_unit(u->id, "Unknown serialization key '%s'", key);
 
         return 0;
 }
 
-static UnitActiveState service_active_state(Unit *u) {
+_pure_ static UnitActiveState service_active_state(Unit *u) {
         const UnitActiveState *table;
 
         assert(u);
@@ -2798,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);
@@ -3023,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) {
@@ -3044,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 &&
@@ -3274,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) {
 
@@ -3427,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;
@@ -3440,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;
@@ -3711,65 +3751,8 @@ static void service_reset_failed(Unit *u) {
 
 static int service_kill(Unit *u, KillWho who, int signo, DBusError *error) {
         Service *s = SERVICE(u);
-        int r = 0;
-        Set *pid_set = NULL;
-
-        assert(s);
 
-        if (s->main_pid <= 0 && who == KILL_MAIN) {
-                dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill");
-                return -ESRCH;
-        }
-
-        if (s->control_pid <= 0 && who == KILL_CONTROL) {
-                dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
-                return -ESRCH;
-        }
-
-        if (who == KILL_CONTROL || who == KILL_ALL)
-                if (s->control_pid > 0)
-                        if (kill(s->control_pid, signo) < 0)
-                                r = -errno;
-
-        if (who == KILL_MAIN || who == KILL_ALL)
-                if (s->main_pid > 0)
-                        if (kill(s->main_pid, signo) < 0)
-                                r = -errno;
-
-        if (who == KILL_ALL) {
-                int q;
-
-                pid_set = set_new(trivial_hash_func, trivial_compare_func);
-                if (!pid_set)
-                        return -ENOMEM;
-
-                /* Exclude the control/main pid from being killed via the cgroup */
-                if (s->control_pid > 0) {
-                        q = set_put(pid_set, LONG_TO_PTR(s->control_pid));
-                        if (q < 0) {
-                                r = q;
-                                goto finish;
-                        }
-                }
-
-                if (s->main_pid > 0) {
-                        q = set_put(pid_set, LONG_TO_PTR(s->main_pid));
-                        if (q < 0) {
-                                r = q;
-                                goto finish;
-                        }
-                }
-
-                q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, false, pid_set, NULL);
-                if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
-                        r = q;
-        }
-
-finish:
-        if (pid_set)
-                set_free(pid_set);
-
-        return r;
+        return unit_kill_common(u, who, signo, s->main_pid, s->control_pid, error);
 }
 
 static const char* const service_state_table[_SERVICE_STATE_MAX] = {
@@ -3861,8 +3844,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,
@@ -3895,7 +3879,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,
@@ -3904,6 +3888,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,