chiark / gitweb /
unit: don't serialize job state, only unit state across switch-root
[elogind.git] / src / core / unit.c
index ed3f176393f1120aca47bd03335a71237c05a8bf..ae6f69183d284055ec624391c9670a80a1a8a3e8 100644 (file)
@@ -33,6 +33,7 @@
 #include "unit.h"
 #include "macro.h"
 #include "strv.h"
+#include "path-util.h"
 #include "load-fragment.h"
 #include "load-dropin.h"
 #include "log.h"
@@ -249,6 +250,9 @@ bool unit_check_gc(Unit *u) {
         if (u->job)
                 return true;
 
+        if (u->nop_job)
+                return true;
+
         if (unit_active_state(u) != UNIT_INACTIVE)
                 return true;
 
@@ -358,9 +362,20 @@ void unit_free(Unit *u) {
                 job_free(j);
         }
 
+        if (u->nop_job) {
+                Job *j = u->nop_job;
+                job_uninstall(j);
+                job_free(j);
+        }
+
         for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
                 bidi_set_free(u, u->dependencies[d]);
 
+        if (u->requires_mounts_for) {
+                LIST_REMOVE(Unit, has_requires_mounts_for, u->manager->has_requires_mounts_for, u);
+                strv_free(u->requires_mounts_for);
+        }
+
         if (u->type != _UNIT_TYPE_INVALID)
                 LIST_REMOVE(Unit, units_by_type, u->manager->units_by_type[u->type], u);
 
@@ -382,7 +397,9 @@ void unit_free(Unit *u) {
         cgroup_attribute_free_list(u->cgroup_attributes);
 
         free(u->description);
+        strv_free(u->documentation);
         free(u->fragment_path);
+        free(u->source_path);
         free(u->instance);
 
         set_free_free(u->names);
@@ -501,6 +518,9 @@ int unit_merge(Unit *u, Unit *other) {
         if (other->job)
                 return -EEXIST;
 
+        if (other->nop_job)
+                return -EEXIST;
+
         if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other)))
                 return -EEXIST;
 
@@ -606,7 +626,7 @@ const char *unit_description(Unit *u) {
 }
 
 void unit_dump(Unit *u, FILE *f, const char *prefix) {
-        char *t;
+        char *t, **j;
         UnitDependency d;
         Iterator i;
         char *p2;
@@ -654,12 +674,18 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
         SET_FOREACH(t, u->names, i)
                 fprintf(f, "%s\tName: %s\n", prefix, t);
 
+        STRV_FOREACH(j, u->documentation)
+                fprintf(f, "%s\tDocumentation: %s\n", prefix, *j);
+
         if ((following = unit_following(u)))
                 fprintf(f, "%s\tFollowing: %s\n", prefix, following->id);
 
         if (u->fragment_path)
                 fprintf(f, "%s\tFragment Path: %s\n", prefix, u->fragment_path);
 
+        if (u->source_path)
+                fprintf(f, "%s\tSource Path: %s\n", prefix, u->source_path);
+
         if (u->job_timeout > 0)
                 fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout));
 
@@ -679,6 +705,16 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
                         fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), other->id);
         }
 
+        if (!strv_isempty(u->requires_mounts_for)) {
+                fprintf(f,
+                        "%s\tRequiresMountsFor:", prefix);
+
+                STRV_FOREACH(j, u->requires_mounts_for)
+                        fprintf(f, " %s", *j);
+
+                fputs("\n", f);
+        }
+
         if (u->load_state == UNIT_LOADED) {
                 CGroupBonding *b;
                 CGroupAttribute *a;
@@ -729,6 +765,9 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
         if (u->job)
                 job_dump(u->job, f, prefix2);
 
+        if (u->nop_job)
+                job_dump(u->nop_job, f, prefix2);
+
         free(p2);
 }
 
@@ -854,6 +893,12 @@ int unit_load(Unit *u) {
                 if ((r = unit_add_default_dependencies(u)) < 0)
                         goto fail;
 
+        if (u->load_state == UNIT_LOADED) {
+                r = unit_add_mount_links(u);
+                if (r < 0)
+                        return r;
+        }
+
         if (u->on_failure_isolate &&
             set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) {
 
@@ -891,6 +936,21 @@ bool unit_condition_test(Unit *u) {
         return u->condition_result;
 }
 
+static void unit_status_print_starting_stopping(Unit *u, bool stopping) {
+        const UnitStatusMessageFormats *format_table;
+        const char *format;
+
+        format_table = &UNIT_VTABLE(u)->status_message_formats;
+        if (!format_table)
+                return;
+
+        format = format_table->starting_stopping[stopping];
+        if (!format)
+                return;
+
+        unit_status_printf(u, "", format, unit_description(u));
+}
+
 /* Errors:
  *         -EBADR:     This unit type does not support starting.
  *         -EALREADY:  Unit is already started.
@@ -930,6 +990,8 @@ int unit_start(Unit *u) {
                 return unit_start(following);
         }
 
+        unit_status_print_starting_stopping(u, false);
+
         /* If it is stopped, but we cannot start it, then fail */
         if (!UNIT_VTABLE(u)->start)
                 return -EBADR;
@@ -942,7 +1004,6 @@ int unit_start(Unit *u) {
 
         unit_add_to_dbus_queue(u);
 
-        unit_status_printf(u, NULL, "Starting %s...", unit_description(u));
         return UNIT_VTABLE(u)->start(u);
 }
 
@@ -979,12 +1040,13 @@ int unit_stop(Unit *u) {
                 return unit_stop(following);
         }
 
+        unit_status_print_starting_stopping(u, true);
+
         if (!UNIT_VTABLE(u)->stop)
                 return -EBADR;
 
         unit_add_to_dbus_queue(u);
 
-        unit_status_printf(u, NULL, "Stopping %s...", unit_description(u));
         return UNIT_VTABLE(u)->stop(u);
 }
 
@@ -1082,7 +1144,7 @@ static void retroactively_start_dependencies(Unit *u) {
                     !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
                         manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
 
-        SET_FOREACH(other, u->dependencies[UNIT_BIND_TO], i)
+        SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i)
                 if (!set_get(u->dependencies[UNIT_AFTER], other) &&
                     !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
                         manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
@@ -1147,7 +1209,7 @@ static void check_unneeded_dependencies(Unit *u) {
         SET_FOREACH(other, u->dependencies[UNIT_REQUISITE_OVERRIDABLE], i)
                 if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
                         unit_check_unneeded(other);
-        SET_FOREACH(other, u->dependencies[UNIT_BIND_TO], i)
+        SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i)
                 if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
                         unit_check_unneeded(other);
 }
@@ -1226,12 +1288,12 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
                 case JOB_VERIFY_ACTIVE:
 
                         if (UNIT_IS_ACTIVE_OR_RELOADING(ns))
-                                job_finish_and_invalidate(u->job, JOB_DONE);
+                                job_finish_and_invalidate(u->job, JOB_DONE, true);
                         else if (u->job->state == JOB_RUNNING && ns != UNIT_ACTIVATING) {
                                 unexpected = true;
 
                                 if (UNIT_IS_INACTIVE_OR_FAILED(ns))
-                                        job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE);
+                                        job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true);
                         }
 
                         break;
@@ -1241,12 +1303,12 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
 
                         if (u->job->state == JOB_RUNNING) {
                                 if (ns == UNIT_ACTIVE)
-                                        job_finish_and_invalidate(u->job, reload_success ? JOB_DONE : JOB_FAILED);
+                                        job_finish_and_invalidate(u->job, reload_success ? JOB_DONE : JOB_FAILED, true);
                                 else if (ns != UNIT_ACTIVATING && ns != UNIT_RELOADING) {
                                         unexpected = true;
 
                                         if (UNIT_IS_INACTIVE_OR_FAILED(ns))
-                                                job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE);
+                                                job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true);
                                 }
                         }
 
@@ -1257,10 +1319,10 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
                 case JOB_TRY_RESTART:
 
                         if (UNIT_IS_INACTIVE_OR_FAILED(ns))
-                                job_finish_and_invalidate(u->job, JOB_DONE);
+                                job_finish_and_invalidate(u->job, JOB_DONE, true);
                         else if (u->job->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) {
                                 unexpected = true;
-                                job_finish_and_invalidate(u->job, JOB_FAILED);
+                                job_finish_and_invalidate(u->job, JOB_FAILED, true);
                         }
 
                         break;
@@ -1507,6 +1569,7 @@ bool unit_job_is_applicable(Unit *u, JobType j) {
         case JOB_VERIFY_ACTIVE:
         case JOB_START:
         case JOB_STOP:
+        case JOB_NOP:
                 return true;
 
         case JOB_RESTART:
@@ -1532,11 +1595,11 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen
                 [UNIT_WANTS] = UNIT_WANTED_BY,
                 [UNIT_REQUISITE] = UNIT_REQUIRED_BY,
                 [UNIT_REQUISITE_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE,
-                [UNIT_BIND_TO] = UNIT_BOUND_BY,
+                [UNIT_BINDS_TO] = UNIT_BOUND_BY,
                 [UNIT_REQUIRED_BY] = _UNIT_DEPENDENCY_INVALID,
                 [UNIT_REQUIRED_BY_OVERRIDABLE] = _UNIT_DEPENDENCY_INVALID,
                 [UNIT_WANTED_BY] = _UNIT_DEPENDENCY_INVALID,
-                [UNIT_BOUND_BY] = UNIT_BIND_TO,
+                [UNIT_BOUND_BY] = UNIT_BINDS_TO,
                 [UNIT_CONFLICTS] = UNIT_CONFLICTED_BY,
                 [UNIT_CONFLICTED_BY] = UNIT_CONFLICTS,
                 [UNIT_BEFORE] = UNIT_AFTER,
@@ -1546,8 +1609,8 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen
                 [UNIT_REFERENCED_BY] = UNIT_REFERENCES,
                 [UNIT_TRIGGERS] = UNIT_TRIGGERED_BY,
                 [UNIT_TRIGGERED_BY] = UNIT_TRIGGERS,
-                [UNIT_PROPAGATE_RELOAD_TO] = UNIT_PROPAGATE_RELOAD_FROM,
-                [UNIT_PROPAGATE_RELOAD_FROM] = UNIT_PROPAGATE_RELOAD_TO
+                [UNIT_PROPAGATES_RELOAD_TO] = UNIT_RELOAD_PROPAGATED_FROM,
+                [UNIT_RELOAD_PROPAGATED_FROM] = UNIT_PROPAGATES_RELOAD_TO
         };
         int r, q = 0, v = 0, w = 0;
 
@@ -1631,7 +1694,7 @@ static const char *resolve_template(Unit *u, const char *name, const char*path,
         assert(name || path);
 
         if (!name)
-                name = file_name_from_path(path);
+                name = path_get_file_name(path);
 
         if (!unit_name_is_template(name)) {
                 *p = NULL;
@@ -1772,20 +1835,12 @@ int set_unit_path(const char *p) {
 }
 
 char *unit_dbus_path(Unit *u) {
-        char *p, *e;
-
         assert(u);
 
         if (!u->id)
                 return NULL;
 
-        if (!(e = bus_path_escape(u->id)))
-                return NULL;
-
-        p = strappend("/org/freedesktop/systemd1/unit/", e);
-        free(e);
-
-        return p;
+        return unit_dbus_path_from_name(u->id);
 }
 
 int unit_add_cgroup(Unit *u, CGroupBonding *b) {
@@ -1836,10 +1891,10 @@ static char *default_cgroup_path(Unit *u) {
                 if (!t)
                         return NULL;
 
-                p = join(u->manager->cgroup_hierarchy, "/", t, "/", u->instance, NULL);
+                p = strjoin(u->manager->cgroup_hierarchy, "/", t, "/", u->instance, NULL);
                 free(t);
         } else
-                p = join(u->manager->cgroup_hierarchy, "/", u->id, NULL);
+                p = strjoin(u->manager->cgroup_hierarchy, "/", u->id, NULL);
 
         return p;
 }
@@ -2140,7 +2195,7 @@ static char *specifier_cgroup_root(char specifier, void *data, void *userdata) {
         if (specifier == 'r')
                 return strdup(u->manager->cgroup_hierarchy);
 
-        if (parent_of_path(u->manager->cgroup_hierarchy, &p) < 0)
+        if (path_get_parent(u->manager->cgroup_hierarchy, &p) < 0)
                 return strdup("");
 
         if (streq(p, "/")) {
@@ -2166,6 +2221,72 @@ static char *specifier_runtime(char specifier, void *data, void *userdata) {
         return strdup("/run");
 }
 
+static char *specifier_user_name(char specifier, void *data, void *userdata) {
+        Service *s = userdata;
+        int r;
+        const char *username;
+
+        /* get USER env from our own env if set */
+        if (!s->exec_context.user)
+                return getusername_malloc();
+
+        /* fish username from passwd */
+        username = s->exec_context.user;
+        r = get_user_creds(&username, NULL, NULL, NULL, NULL);
+        if (r < 0)
+                return NULL;
+
+        return strdup(username);
+}
+
+static char *specifier_user_home(char specifier, void *data, void *userdata) {
+        Service *s = userdata;
+        int r;
+        const char *username, *home;
+
+        /* return HOME if set, otherwise from passwd */
+        if (!s->exec_context.user) {
+                char *h;
+
+                r = get_home_dir(&h);
+                if (r < 0)
+                        return NULL;
+
+                return h;
+        }
+
+        username = s->exec_context.user;
+        r = get_user_creds(&username, NULL, NULL, &home, NULL);
+        if (r < 0)
+               return NULL;
+
+        return strdup(home);
+}
+
+static char *specifier_user_shell(char specifier, void *data, void *userdata) {
+        Service *s = userdata;
+        int r;
+        const char *username, *shell;
+
+        /* return HOME if set, otherwise from passwd */
+        if (!s->exec_context.user) {
+                char *sh;
+
+                r = get_shell(&sh);
+                if (r < 0)
+                        return strdup("/bin/sh");
+
+                return sh;
+        }
+
+        username = s->exec_context.user;
+        r = get_user_creds(&username, NULL, NULL, NULL, &shell);
+        if (r < 0)
+                return strdup("/bin/sh");
+
+        return strdup(shell);
+}
+
 char *unit_name_printf(Unit *u, const char* format) {
 
         /*
@@ -2201,6 +2322,8 @@ char *unit_full_printf(Unit *u, const char *format) {
          * %r root cgroup path of this systemd instance (e.g. "/user/lennart/shared/systemd-4711")
          * %R parent of root cgroup path (e.g. "/usr/lennart/shared")
          * %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR)
+         * %u the username of the configured User or running user
+         * %h the homedir of the configured User or running user
          */
 
         const Specifier table[] = {
@@ -2215,6 +2338,9 @@ char *unit_full_printf(Unit *u, const char *format) {
                 { 'r', specifier_cgroup_root,         NULL },
                 { 'R', specifier_cgroup_root,         NULL },
                 { 't', specifier_runtime,             NULL },
+                { 'u', specifier_user_name,           NULL },
+                { 'h', specifier_user_home,           NULL },
+                { 's', specifier_user_shell,          NULL },
                 { 0, NULL, NULL }
         };
 
@@ -2275,7 +2401,7 @@ bool unit_can_serialize(Unit *u) {
         return UNIT_VTABLE(u)->serialize && UNIT_VTABLE(u)->deserialize_item;
 }
 
-int unit_serialize(Unit *u, FILE *f, FDSet *fds) {
+int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
         int r;
 
         assert(u);
@@ -2288,8 +2414,18 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds) {
         if ((r = UNIT_VTABLE(u)->serialize(u, f, fds)) < 0)
                 return r;
 
-        if (u->job)
-                unit_serialize_item(u, f, "job", job_type_to_string(u->job->type));
+
+        if (serialize_jobs) {
+                if (u->job) {
+                        fprintf(f, "job\n");
+                        job_serialize(u->job, f, fds);
+                }
+
+                if (u->nop_job) {
+                        fprintf(f, "job\n");
+                        job_serialize(u->nop_job, f, fds);
+                }
+        }
 
         dual_timestamp_serialize(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp);
         dual_timestamp_serialize(f, "active-enter-timestamp", &u->active_enter_timestamp);
@@ -2368,13 +2504,38 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                         v = l+k;
 
                 if (streq(l, "job")) {
-                        JobType type;
+                        if (v[0] == '\0') {
+                                /* new-style serialized job */
+                                Job *j = job_new_raw(u);
+                                if (!j)
+                                        return -ENOMEM;
+
+                                r = job_deserialize(j, f, fds);
+                                if (r < 0) {
+                                        job_free(j);
+                                        return r;
+                                }
 
-                        if ((type = job_type_from_string(v)) < 0)
-                                log_debug("Failed to parse job type value %s", v);
-                        else
-                                u->deserialized_job = type;
+                                r = hashmap_put(u->manager->jobs, UINT32_TO_PTR(j->id), j);
+                                if (r < 0) {
+                                        job_free(j);
+                                        return r;
+                                }
 
+                                r = job_install_deserialized(j);
+                                if (r < 0) {
+                                        hashmap_remove(u->manager->jobs, UINT32_TO_PTR(j->id));
+                                        job_free(j);
+                                        return r;
+                                }
+                        } else {
+                                /* legacy */
+                                JobType type = job_type_from_string(v);
+                                if (type < 0)
+                                        log_debug("Failed to parse job type value %s", v);
+                                else
+                                        u->deserialized_job = type;
+                        }
                         continue;
                 } else if (streq(l, "inactive-exit-timestamp")) {
                         dual_timestamp_deserialize(v, &u->inactive_exit_timestamp);
@@ -2422,7 +2583,8 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) {
         if (!is_device_path(what))
                 return 0;
 
-        if (!(e = unit_name_build_escape(what+1, NULL, ".device")))
+        e = unit_name_from_path(what, ".device");
+        if (!e)
                 return -ENOMEM;
 
         r = manager_load_unit(u->manager, e, NULL, NULL, &device);
@@ -2431,7 +2593,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) {
         if (r < 0)
                 return r;
 
-        if ((r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_BIND_TO, device, true)) < 0)
+        if ((r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_BINDS_TO, device, true)) < 0)
                 return r;
 
         if (wants)
@@ -2450,8 +2612,14 @@ int unit_coldplug(Unit *u) {
                 if ((r = UNIT_VTABLE(u)->coldplug(u)) < 0)
                         return r;
 
-        if (u->deserialized_job >= 0) {
-                if ((r = manager_add_job(u->manager, u->deserialized_job, u, JOB_IGNORE_REQUIREMENTS, false, NULL, NULL)) < 0)
+        if (u->job) {
+                r = job_coldplug(u->job);
+                if (r < 0)
+                        return r;
+        } else if (u->deserialized_job >= 0) {
+                /* legacy */
+                r = manager_add_job(u->manager, u->deserialized_job, u, JOB_IGNORE_REQUIREMENTS, false, NULL, NULL);
+                if (r < 0)
                         return r;
 
                 u->deserialized_job = _JOB_TYPE_INVALID;
@@ -2466,9 +2634,6 @@ void unit_status_printf(Unit *u, const char *status, const char *format, ...) {
         assert(u);
         assert(format);
 
-        if (!UNIT_VTABLE(u)->show_status)
-                return;
-
         if (!manager_get_show_status(u->manager))
                 return;
 
@@ -2481,11 +2646,11 @@ void unit_status_printf(Unit *u, const char *status, const char *format, ...) {
 }
 
 bool unit_need_daemon_reload(Unit *u) {
+        struct stat st;
+
         assert(u);
 
         if (u->fragment_path) {
-                struct stat st;
-
                 zero(st);
                 if (stat(u->fragment_path, &st) < 0)
                         /* What, cannot access this anymore? */
@@ -2496,8 +2661,15 @@ bool unit_need_daemon_reload(Unit *u) {
                         return true;
         }
 
-        if (UNIT_VTABLE(u)->need_daemon_reload)
-                return UNIT_VTABLE(u)->need_daemon_reload(u);
+        if (u->source_path) {
+                zero(st);
+                if (stat(u->source_path, &st) < 0)
+                        return true;
+
+                if (u->source_mtime > 0 &&
+                    timespec_load(&st.st_mtim) != u->source_mtime)
+                        return true;
+        }
 
         return false;
 }
@@ -2549,28 +2721,6 @@ bool unit_pending_active(Unit *u) {
         return false;
 }
 
-UnitType unit_name_to_type(const char *n) {
-        UnitType t;
-
-        assert(n);
-
-        for (t = 0; t < _UNIT_TYPE_MAX; t++)
-                if (endswith(n, unit_vtable[t]->suffix))
-                        return t;
-
-        return _UNIT_TYPE_INVALID;
-}
-
-bool unit_name_is_valid(const char *n, bool template_ok) {
-        UnitType t;
-
-        t = unit_name_to_type(n);
-        if (t < 0 || t >= _UNIT_TYPE_MAX)
-                return false;
-
-        return unit_name_is_valid_no_type(n, template_ok);
-}
-
 int unit_kill(Unit *u, KillWho w, KillMode m, int signo, DBusError *error) {
         assert(u);
         assert(w >= 0 && w < _KILL_WHO_MAX);
@@ -2604,7 +2754,7 @@ UnitFileState unit_get_unit_file_state(Unit *u) {
         if (u->unit_file_state < 0 && u->fragment_path)
                 u->unit_file_state = unit_file_get_state(
                                 u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
-                                NULL, file_name_from_path(u->fragment_path));
+                                NULL, path_get_file_name(u->fragment_path));
 
         return u->unit_file_state;
 }
@@ -2631,15 +2781,57 @@ void unit_ref_unset(UnitRef *ref) {
         ref->unit = NULL;
 }
 
-static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
-        [UNIT_STUB] = "stub",
-        [UNIT_LOADED] = "loaded",
-        [UNIT_ERROR] = "error",
-        [UNIT_MERGED] = "merged",
-        [UNIT_MASKED] = "masked"
-};
+int unit_add_one_mount_link(Unit *u, Mount *m) {
+        char **i;
 
-DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
+        assert(u);
+        assert(m);
+
+        if (u->load_state != UNIT_LOADED ||
+            UNIT(m)->load_state != UNIT_LOADED)
+                return 0;
+
+        STRV_FOREACH(i, u->requires_mounts_for) {
+
+                if (UNIT(m) == u)
+                        continue;
+
+                if (!path_startswith(*i, m->where))
+                        continue;
+
+                return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true);
+        }
+
+        return 0;
+}
+
+int unit_add_mount_links(Unit *u) {
+        Unit *other;
+        int r;
+
+        assert(u);
+
+        LIST_FOREACH(units_by_type, other, u->manager->units_by_type[UNIT_MOUNT]) {
+                r = unit_add_one_mount_link(u, MOUNT(other));
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int unit_patch_working_directory(Unit *u, ExecContext *c) {
+        assert(u);
+        assert(c);
+
+        if (u->manager->running_as != MANAGER_USER)
+                return 0;
+
+        if (c->working_directory)
+                return 0;
+
+        return get_home_dir(&c->working_directory);
+}
 
 static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
         [UNIT_ACTIVE] = "active",
@@ -2660,7 +2852,7 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
         [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
         [UNIT_REQUIRED_BY] = "RequiredBy",
         [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
-        [UNIT_BIND_TO] = "BindTo",
+        [UNIT_BINDS_TO] = "BindsTo",
         [UNIT_WANTED_BY] = "WantedBy",
         [UNIT_CONFLICTS] = "Conflicts",
         [UNIT_CONFLICTED_BY] = "ConflictedBy",
@@ -2672,8 +2864,8 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
         [UNIT_ON_FAILURE] = "OnFailure",
         [UNIT_TRIGGERS] = "Triggers",
         [UNIT_TRIGGERED_BY] = "TriggeredBy",
-        [UNIT_PROPAGATE_RELOAD_TO] = "PropagateReloadTo",
-        [UNIT_PROPAGATE_RELOAD_FROM] = "PropagateReloadFrom"
+        [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
+        [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom"
 };
 
 DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);