chiark / gitweb /
unit: fix the race in deserialization.
[elogind.git] / src / core / unit.c
index f7d00b6030d2f9a690c8367da86506447347e3e7..601be60ed0595311910218a2b916f0ae2112bbf5 100644 (file)
@@ -46,6 +46,8 @@
 #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,
@@ -614,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;
 }
@@ -745,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)
@@ -992,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
@@ -1318,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);
@@ -1330,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);
@@ -1352,6 +1357,21 @@ 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)) {
+                        /* XXX The counter may get out of sync if the admin edits
+                         * TTY-related unit file properties and issues a daemon-reload
+                         * while the unit is active. No big deal though, because
+                         * it influences only the printing of boot/shutdown
+                         * status messages. */
+                        if (UNIT_IS_INACTIVE_OR_FAILED(ns))
+                                m->n_on_console--;
+                        else
+                                m->n_on_console++;
+                }
+        }
+
         if (u->job) {
                 unexpected = false;
 
@@ -1418,7 +1438,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
@@ -1454,18 +1474,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 {
 
@@ -1475,29 +1495,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);
@@ -1896,30 +1917,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;
 }
@@ -2105,7 +2108,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);
@@ -2149,10 +2152,10 @@ 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;
@@ -2160,48 +2163,67 @@ int unit_add_cgroup_attribute(
         int r;
 
         assert(u);
-        assert(name);
         assert(value);
 
+        if (semantics) {
+                /* Semantics always take precedence */
+                if (semantics->name)
+                        name = semantics->name;
+
+                if (semantics->controller)
+                        controller = semantics->controller;
+        }
+
+        if (!name)
+                return -EINVAL;
+
         if (!controller) {
                 r = cg_controller_from_attr(name, &c);
                 if (r < 0)
                         return -EINVAL;
 
                 controller = c;
-        } else {
-                if (!filename_is_safe(name))
-                        return -EINVAL;
-
-                if (!filename_is_safe(controller))
-                        return -EINVAL;
         }
 
         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);
@@ -2222,11 +2244,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);
@@ -2533,21 +2554,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) {
@@ -2759,52 +2767,77 @@ ExecContext *unit_get_exec_context(Unit *u) {
         return (ExecContext*) ((uint8_t*) u + offset);
 }
 
-int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data) {
-        _cleanup_free_ char *p = NULL, *q = NULL;
+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_SYSTEM)
+        if (u->manager->running_as == SYSTEMD_USER && runtime)
                 return -ENOTSUP;
 
         if (!filename_is_safe(name))
                 return -EINVAL;
 
-        p = strjoin(runtime ? "/run/systemd/system/" : "/etc/systemd/system/", u->id, ".d", NULL);
+        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)
+        if (!q) {
+                free(p);
                 return -ENOMEM;
+        }
 
-        mkdir_p(p, 0755);
-        return write_one_line_file_atomic(q, data);
+        *_p = p;
+        *_q = q;
+        return 0;
 }
 
-int unit_remove_drop_in(Unit *u, bool runtime, const char *name) {
+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);
 
-        if (u->manager->running_as != SYSTEMD_SYSTEM)
-                return -ENOTSUP;
+        r = drop_in_file(u, runtime, name, &p, &q);
+        if (r < 0)
+                return r;
 
-        if (!filename_is_safe(name))
-                return -EINVAL;
+        mkdir_p(p, 0755);
+        return write_one_line_file_atomic_label(q, data);
+}
 
-        p = strjoin(runtime ? "/run/systemd/system/" : "/etc/systemd/system/", u->id, ".d", NULL);
-        if (!p)
-                return -ENOMEM;
+int unit_remove_drop_in(Unit *u, bool runtime, const char *name) {
+        _cleanup_free_ char *p = NULL, *q = NULL;
+        int r;
 
-        q = strjoin(p, "/50-", name, ".conf", NULL);
-        if (!q)
-                return -ENOMEM;
+        assert(u);
 
+        r = drop_in_file(u, runtime, name, &p, &q);
         if (unlink(q) < 0)
-                return -errno;
+                r = -errno;
+        else
+                r = 0;
 
         rmdir(p);
-        return 0;
+        return r;
 }
 
 int unit_kill_context(