chiark / gitweb /
core: fix running jobs counters after reload/reexec
[elogind.git] / src / core / unit.c
index 1194c524bfdc77f8fa2a840165fafdfe55fc0c2f..2f0ac00fcf1fe9ab26b1529d43da0085368fd38c 100644 (file)
@@ -45,6 +45,9 @@
 #include "cgroup-util.h"
 #include "missing.h"
 #include "cgroup-attr.h"
+#include "mkdir.h"
+#include "label.h"
+#include "fileio-label.h"
 
 const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
         [UNIT_SERVICE] = &service_vtable,
@@ -613,9 +616,11 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
         /* If syslog or kernel logging is requested, make sure our own
          * logging daemon is run first. */
 
-        if (u->manager->running_as == SYSTEMD_SYSTEM)
-                if ((r = unit_add_two_dependencies_by_name(u, UNIT_REQUIRES, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true)) < 0)
+        if (u->manager->running_as == SYSTEMD_SYSTEM) {
+                r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true);
+                if (r < 0)
                         return r;
+        }
 
         return 0;
 }
@@ -744,15 +749,13 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
                                 prefix, b->controller, b->path);
 
                 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
-                        char *v = NULL;
+                        _cleanup_free_ char *v = NULL;
 
-                        if (a->map_callback)
-                                a->map_callback(a->controller, a->name, a->value, &v);
+                        if (a->semantics && a->semantics->map_write)
+                                a->semantics->map_write(a->semantics, a->value, &v);
 
                         fprintf(f, "%s\tControlGroupAttribute: %s %s \"%s\"\n",
                                 prefix, a->controller, a->name, v ? v : a->value);
-
-                        free(v);
                 }
 
                 if (UNIT_VTABLE(u)->dump)
@@ -991,7 +994,7 @@ static void unit_status_print_starting_stopping(Unit *u, JobType t) {
         if (!format)
                 return;
 
-        unit_status_printf(u, "", format, unit_description(u));
+        unit_status_printf(u, "", format);
 }
 
 #pragma GCC diagnostic push
@@ -1022,9 +1025,9 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
               t == JOB_STOP  ? SD_MESSAGE_UNIT_STOPPING :
                                SD_MESSAGE_UNIT_RELOADING;
 
-        log_struct(LOG_INFO,
+        log_struct_unit(LOG_INFO,
+                   u->id,
                    MESSAGE_ID(mid),
-                   "UNIT=%s", u->id,
                    "MESSAGE=%s", buf,
                    NULL);
 }
@@ -1317,6 +1320,7 @@ void unit_trigger_on_failure(Unit *u) {
 }
 
 void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) {
+        Manager *m;
         bool unexpected;
 
         assert(u);
@@ -1329,7 +1333,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
          * behavior here. For example: if a mount point is remounted
          * this function will be called too! */
 
-        if (u->manager->n_reloading <= 0) {
+        m = u->manager;
+
+        if (m->n_reloading <= 0) {
                 dual_timestamp ts;
 
                 dual_timestamp_get(&ts);
@@ -1351,6 +1357,16 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
         if (UNIT_IS_INACTIVE_OR_FAILED(ns))
                 cgroup_bonding_trim_list(u->cgroup_bondings, true);
 
+        if (UNIT_IS_INACTIVE_OR_FAILED(os) != UNIT_IS_INACTIVE_OR_FAILED(ns)) {
+                ExecContext *ec = unit_get_exec_context(u);
+                if (ec && exec_context_may_touch_console(ec)) {
+                        if (UNIT_IS_INACTIVE_OR_FAILED(ns))
+                                m->n_on_console--;
+                        else
+                                m->n_on_console++;
+                }
+        }
+
         if (u->job) {
                 unexpected = false;
 
@@ -1417,7 +1433,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
         } else
                 unexpected = true;
 
-        if (u->manager->n_reloading <= 0) {
+        if (m->n_reloading <= 0) {
 
                 /* If this state change happened without being
                  * requested by a job, then let's retroactively start
@@ -1438,9 +1454,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
                         check_unneeded_dependencies(u);
 
                 if (ns != os && ns == UNIT_FAILED) {
-                        log_struct(LOG_NOTICE,
+                        log_struct_unit(LOG_NOTICE,
+                                   u->id,
                                    "MESSAGE=Unit %s entered failed state", u->id,
-                                   "UNIT=%s", u->id,
                                    NULL);
                         unit_trigger_on_failure(u);
                 }
@@ -1453,18 +1469,18 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
                         /* The bus just might have become available,
                          * hence try to connect to it, if we aren't
                          * yet connected. */
-                        bus_init(u->manager, true);
+                        bus_init(m, true);
 
                 if (u->type == UNIT_SERVICE &&
                     !UNIT_IS_ACTIVE_OR_RELOADING(os) &&
-                    u->manager->n_reloading <= 0) {
+                    m->n_reloading <= 0) {
                         /* Write audit record if we have just finished starting up */
-                        manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, true);
+                        manager_send_unit_audit(m, u, AUDIT_SERVICE_START, true);
                         u->in_audit = true;
                 }
 
                 if (!UNIT_IS_ACTIVE_OR_RELOADING(os))
-                        manager_send_unit_plymouth(u->manager, u);
+                        manager_send_unit_plymouth(m, u);
 
         } else {
 
@@ -1474,29 +1490,30 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
                 if (u->type == UNIT_SERVICE &&
                     UNIT_IS_INACTIVE_OR_FAILED(ns) &&
                     !UNIT_IS_INACTIVE_OR_FAILED(os) &&
-                    u->manager->n_reloading <= 0) {
+                    m->n_reloading <= 0) {
 
                         /* Hmm, if there was no start record written
                          * write it now, so that we always have a nice
                          * pair */
                         if (!u->in_audit) {
-                                manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE);
+                                manager_send_unit_audit(m, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE);
 
                                 if (ns == UNIT_INACTIVE)
-                                        manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, true);
+                                        manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, true);
                         } else
                                 /* Write audit record if we have just finished shutting down */
-                                manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE);
+                                manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE);
 
                         u->in_audit = false;
                 }
         }
 
-        manager_recheck_journal(u->manager);
+        manager_recheck_journal(m);
 
         /* Maybe we finished startup and are now ready for being
          * stopped because unneeded? */
-        unit_check_unneeded(u);
+        if (u->manager->n_reloading <= 0)
+                unit_check_unneeded(u);
 
         unit_add_to_dbus_queue(u);
         unit_add_to_gc_queue(u);
@@ -1895,30 +1912,12 @@ finish:
 }
 
 int set_unit_path(const char *p) {
-        char *cwd, *c;
-        int r;
+        _cleanup_free_ char *c = NULL;
 
         /* This is mostly for debug purposes */
-
-        if (path_is_absolute(p)) {
-                if (!(c = strdup(p)))
-                        return -ENOMEM;
-        } else {
-                if (!(cwd = get_current_dir_name()))
-                        return -errno;
-
-                r = asprintf(&c, "%s/%s", cwd, p);
-                free(cwd);
-
-                if (r < 0)
-                        return -ENOMEM;
-        }
-
-        if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0) {
-                r = -errno;
-                free(c);
-                return r;
-        }
+        c = path_make_absolute_cwd(p);
+        if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0)
+                return -errno;
 
         return 0;
 }
@@ -2090,7 +2089,7 @@ static int unit_add_one_default_cgroup(Unit *u, const char *controller) {
                 return -ENOMEM;
 
         b->controller = strdup(controller);
-        if (!b)
+        if (!b->controller)
                 goto fail;
 
         b->path = unit_default_cgroup_path(u);
@@ -2104,7 +2103,7 @@ static int unit_add_one_default_cgroup(Unit *u, const char *controller) {
         if (r < 0)
                 goto fail;
 
-        return 0;
+        return 1;
 
 fail:
         free(b->path);
@@ -2148,58 +2147,78 @@ CGroupBonding* unit_get_default_cgroup(Unit *u) {
 
 int unit_add_cgroup_attribute(
                 Unit *u,
+                const CGroupSemantics *semantics,
                 const char *controller,
                 const char *name,
                 const char *value,
-                CGroupAttributeMapCallback map_callback,
                 CGroupAttribute **ret) {
 
         _cleanup_free_ char *c = NULL;
         CGroupAttribute *a;
+        int r;
 
         assert(u);
-        assert(name);
         assert(value);
 
-        if (!controller) {
-                const char *dot;
+        if (semantics) {
+                /* Semantics always take precedence */
+                if (semantics->name)
+                        name = semantics->name;
 
-                dot = strchr(name, '.');
-                if (!dot)
-                        return -EINVAL;
+                if (semantics->controller)
+                        controller = semantics->controller;
+        }
 
-                c = strndup(name, dot - name);
-                if (!c)
-                        return -ENOMEM;
+        if (!name)
+                return -EINVAL;
+
+        if (!controller) {
+                r = cg_controller_from_attr(name, &c);
+                if (r < 0)
+                        return -EINVAL;
 
                 controller = c;
         }
 
-        if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
+        if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
+                return -EINVAL;
+
+        if (!filename_is_safe(name))
+                return -EINVAL;
+
+        if (!filename_is_safe(controller))
                 return -EINVAL;
 
+        /* Check if this attribute already exists. Note that we will
+         * explicitly check for the value here too, as there are
+         * attributes which accept multiple values. */
         a = cgroup_attribute_find_list(u->cgroup_attributes, controller, name);
         if (a) {
-                char *v;
-
                 if (streq(value, a->value)) {
+                        /* Exactly the same value is always OK, let's ignore this */
                         if (ret)
                                 *ret = a;
 
                         return 0;
                 }
 
-                v = strdup(value);
-                if (!v)
-                        return -ENOMEM;
+                if (semantics && !semantics->multiple) {
+                        char *v;
 
-                free(a->value);
-                a->value = v;
+                        /* If this is a single-item entry, we can
+                         * simply patch the existing attribute */
+
+                        v = strdup(value);
+                        if (!v)
+                                return -ENOMEM;
 
-                if (ret)
-                        *ret = a;
+                        free(a->value);
+                        a->value = v;
 
-                return 1;
+                        if (ret)
+                                *ret = a;
+                        return 1;
+                }
         }
 
         a = new0(CGroupAttribute, 1);
@@ -2220,11 +2239,10 @@ int unit_add_cgroup_attribute(
                 free(a->name);
                 free(a->value);
                 free(a);
-
                 return -ENOMEM;
         }
 
-        a->map_callback = map_callback;
+        a->semantics = semantics;
         a->unit = u;
 
         LIST_PREPEND(CGroupAttribute, by_unit, u->cgroup_attributes, a);
@@ -2423,6 +2441,9 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                                         return r;
                                 }
 
+                                if (j->state == JOB_RUNNING)
+                                        u->manager->n_running_jobs++;
+
                                 r = job_install_deserialized(j);
                                 if (r < 0) {
                                         hashmap_remove(u->manager->jobs, UINT32_TO_PTR(j->id));
@@ -2531,21 +2552,8 @@ int unit_coldplug(Unit *u) {
         return 0;
 }
 
-void unit_status_printf(Unit *u, const char *status, const char *format, ...) {
-        va_list ap;
-
-        assert(u);
-        assert(format);
-
-        if (!manager_get_show_status(u->manager))
-                return;
-
-        if (!manager_is_booting_or_shutting_down(u->manager))
-                return;
-
-        va_start(ap, format);
-        status_vprintf(status, true, format, ap);
-        va_end(ap);
+void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) {
+        manager_status_printf(u->manager, false, status, unit_status_msg_format, unit_description(u));
 }
 
 bool unit_need_daemon_reload(Unit *u) {
@@ -2757,6 +2765,155 @@ ExecContext *unit_get_exec_context(Unit *u) {
         return (ExecContext*) ((uint8_t*) u + offset);
 }
 
+static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char **_q) {
+        char *p, *q;
+        int r;
+
+        assert(u);
+        assert(name);
+        assert(_p);
+        assert(_q);
+
+        if (u->manager->running_as == SYSTEMD_USER && runtime)
+                return -ENOTSUP;
+
+        if (!filename_is_safe(name))
+                return -EINVAL;
+
+        if (u->manager->running_as == SYSTEMD_USER) {
+                _cleanup_free_ char *c = NULL;
+
+                r = user_config_home(&c);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        return -ENOENT;
+
+                p = strjoin(c, "/", u->id, ".d", NULL);
+        } else  if (runtime)
+                p = strjoin("/run/systemd/system/", u->id, ".d", NULL);
+        else
+                p = strjoin("/etc/systemd/system/", u->id, ".d", NULL);
+        if (!p)
+                return -ENOMEM;
+
+        q = strjoin(p, "/50-", name, ".conf", NULL);
+        if (!q) {
+                free(p);
+                return -ENOMEM;
+        }
+
+        *_p = p;
+        *_q = q;
+        return 0;
+}
+
+int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data) {
+        _cleanup_free_ char *p = NULL, *q = NULL;
+        int r;
+
+        assert(u);
+
+        r = drop_in_file(u, runtime, name, &p, &q);
+        if (r < 0)
+                return r;
+
+        mkdir_p(p, 0755);
+        return write_one_line_file_atomic_label(q, data);
+}
+
+int unit_remove_drop_in(Unit *u, bool runtime, const char *name) {
+        _cleanup_free_ char *p = NULL, *q = NULL;
+        int r;
+
+        assert(u);
+
+        r = drop_in_file(u, runtime, name, &p, &q);
+        if (unlink(q) < 0)
+                r = -errno;
+        else
+                r = 0;
+
+        rmdir(p);
+        return r;
+}
+
+int unit_kill_context(
+                Unit *u,
+                KillContext *c,
+                bool sigkill,
+                pid_t main_pid,
+                pid_t control_pid,
+                bool main_pid_alien) {
+
+        int sig, wait_for_exit = 0, r;
+
+        assert(u);
+        assert(c);
+
+        if (c->kill_mode == KILL_NONE)
+                return 0;
+
+        sig = sigkill ? SIGKILL : c->kill_signal;
+
+        if (main_pid > 0) {
+                r = kill_and_sigcont(main_pid, sig);
+
+                if (r < 0 && r != -ESRCH) {
+                        _cleanup_free_ char *comm = NULL;
+                        get_process_comm(main_pid, &comm);
+
+                        log_warning_unit(u->id, "Failed to kill main process %li (%s): %s",
+                                         (long) main_pid, strna(comm), strerror(-r));
+                } else
+                        wait_for_exit = !main_pid_alien;
+        }
+
+        if (control_pid > 0) {
+                r = kill_and_sigcont(control_pid, sig);
+
+                if (r < 0 && r != -ESRCH) {
+                        _cleanup_free_ char *comm = NULL;
+                        get_process_comm(control_pid, &comm);
+
+                        log_warning_unit(u->id,
+                                         "Failed to kill control process %li (%s): %s",
+                                         (long) control_pid, strna(comm), strerror(-r));
+                } else
+                        wait_for_exit = true;
+        }
+
+        if (c->kill_mode == KILL_CONTROL_GROUP) {
+                _cleanup_set_free_ Set *pid_set = NULL;
+
+                pid_set = set_new(trivial_hash_func, trivial_compare_func);
+                if (!pid_set)
+                        return -ENOMEM;
+
+                /* Exclude the main/control pids from being killed via the cgroup */
+                if (main_pid > 0) {
+                        r = set_put(pid_set, LONG_TO_PTR(main_pid));
+                        if (r < 0)
+                                return r;
+                }
+
+                if (control_pid > 0) {
+                        r = set_put(pid_set, LONG_TO_PTR(control_pid));
+                        if (r < 0)
+                                return r;
+                }
+
+                r = cgroup_bonding_kill_list(u->cgroup_bondings, sig, true, false, pid_set, NULL);
+                if (r < 0) {
+                        if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
+                                log_warning_unit(u->id, "Failed to kill control group: %s", strerror(-r));
+                } else if (r > 0)
+                        wait_for_exit = true;
+        }
+
+        return wait_for_exit;
+}
+
 static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
         [UNIT_ACTIVE] = "active",
         [UNIT_RELOADING] = "reloading",