+ r = get_home_dir(&ec->working_directory);
+ if (r < 0)
+ return r;
+ }
+
+ if (u->manager->running_as == SYSTEMD_USER &&
+ (ec->syscall_whitelist ||
+ !set_isempty(ec->syscall_filter) ||
+ !set_isempty(ec->syscall_archs) ||
+ ec->address_families_whitelist ||
+ !set_isempty(ec->address_families)))
+ ec->no_new_privileges = true;
+
+ if (ec->private_devices)
+ ec->capability_bounding_set_drop |= (uint64_t) 1ULL << (uint64_t) CAP_MKNOD;
+ }
+
+ cc = unit_get_cgroup_context(u);
+ if (cc) {
+
+ if (ec &&
+ ec->private_devices &&
+ cc->device_policy == CGROUP_AUTO)
+ cc->device_policy = CGROUP_CLOSED;
+ }
+
+ return 0;
+}
+
+ExecContext *unit_get_exec_context(Unit *u) {
+ size_t offset;
+ assert(u);
+
+ if (u->type < 0)
+ return NULL;
+
+ offset = UNIT_VTABLE(u)->exec_context_offset;
+ if (offset <= 0)
+ return NULL;
+
+ return (ExecContext*) ((uint8_t*) u + offset);
+}
+
+KillContext *unit_get_kill_context(Unit *u) {
+ size_t offset;
+ assert(u);
+
+ if (u->type < 0)
+ return NULL;
+
+ offset = UNIT_VTABLE(u)->kill_context_offset;
+ if (offset <= 0)
+ return NULL;
+
+ return (KillContext*) ((uint8_t*) u + offset);
+}
+
+CGroupContext *unit_get_cgroup_context(Unit *u) {
+ size_t offset;
+
+ if (u->type < 0)
+ return NULL;
+
+ offset = UNIT_VTABLE(u)->cgroup_context_offset;
+ if (offset <= 0)
+ return NULL;
+
+ return (CGroupContext*) ((uint8_t*) u + offset);
+}
+
+ExecRuntime *unit_get_exec_runtime(Unit *u) {
+ size_t offset;
+
+ if (u->type < 0)
+ return NULL;
+
+ offset = UNIT_VTABLE(u)->exec_runtime_offset;
+ if (offset <= 0)
+ return NULL;
+
+ return *(ExecRuntime**) ((uint8_t*) u + offset);
+}
+
+static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient, char **dir) {
+ if (u->manager->running_as == SYSTEMD_USER) {
+ int r;
+
+ r = user_config_home(dir);
+ if (r == 0)
+ return -ENOENT;
+ return r;
+ }
+
+ if (mode == UNIT_PERSISTENT && !transient)
+ *dir = strdup("/etc/systemd/system");
+ else
+ *dir = strdup("/run/systemd/system");
+ if (!*dir)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int unit_drop_in_file(Unit *u,
+ UnitSetPropertiesMode mode, const char *name, char **p, char **q) {
+ _cleanup_free_ char *dir = NULL;
+ int r;
+
+ assert(u);
+
+ r = unit_drop_in_dir(u, mode, u->transient, &dir);
+ if (r < 0)
+ return r;
+
+ return drop_in_file(dir, u->id, 50, name, p, q);
+}
+
+int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) {
+
+ _cleanup_free_ char *dir = NULL;
+ int r;
+
+ assert(u);
+
+ if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME))
+ return 0;
+
+ r = unit_drop_in_dir(u, mode, u->transient, &dir);
+ if (r < 0)
+ return r;
+
+ return write_drop_in(dir, u->id, 50, name, data);
+}
+
+int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) {
+ _cleanup_free_ char *p = NULL;
+ va_list ap;
+ int r;
+
+ assert(u);
+ assert(name);
+ assert(format);
+
+ if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME))
+ return 0;
+
+ va_start(ap, format);
+ r = vasprintf(&p, format, ap);
+ va_end(ap);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ return unit_write_drop_in(u, mode, name, p);
+}
+
+int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) {
+ _cleanup_free_ char *ndata = NULL;
+
+ assert(u);
+ assert(name);
+ assert(data);
+
+ if (!UNIT_VTABLE(u)->private_section)
+ return -EINVAL;
+
+ if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME))
+ return 0;
+
+ ndata = strjoin("[", UNIT_VTABLE(u)->private_section, "]\n", data, NULL);
+ if (!ndata)
+ return -ENOMEM;
+
+ return unit_write_drop_in(u, mode, name, ndata);
+}
+
+int unit_write_drop_in_private_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) {
+ _cleanup_free_ char *p = NULL;
+ va_list ap;
+ int r;
+
+ assert(u);
+ assert(name);
+ assert(format);
+
+ if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME))
+ return 0;
+
+ va_start(ap, format);
+ r = vasprintf(&p, format, ap);
+ va_end(ap);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ return unit_write_drop_in_private(u, mode, name, p);
+}
+
+int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name) {
+ _cleanup_free_ char *p = NULL, *q = NULL;
+ int r;
+
+ assert(u);
+
+ if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME))
+ return 0;
+
+ r = unit_drop_in_file(u, mode, name, &p, &q);
+ if (r < 0)
+ return r;
+
+ if (unlink(q) < 0)
+ r = errno == ENOENT ? 0 : -errno;
+ else
+ r = 1;
+
+ rmdir(p);
+ return r;
+}
+
+int unit_make_transient(Unit *u) {
+ int r;
+
+ assert(u);
+
+ u->load_state = UNIT_STUB;
+ u->load_error = 0;
+ u->transient = true;
+
+ free(u->fragment_path);
+ u->fragment_path = 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;
+
+ u->fragment_path = strjoin(c, "/", u->id, NULL);
+ if (!u->fragment_path)
+ return -ENOMEM;
+
+ mkdir_p(c, 0755);
+ } else {
+ u->fragment_path = strappend("/run/systemd/system/", u->id);
+ if (!u->fragment_path)
+ return -ENOMEM;
+
+ mkdir_p("/run/systemd/system", 0755);
+ }
+
+ return write_string_file_atomic_label(u->fragment_path, "# Transient stub");
+}
+
+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 = false, 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 " PID_FMT " (%s): %s", main_pid, strna(comm), strerror(-r));
+ } else {
+ if (!main_pid_alien)
+ wait_for_exit = true;
+
+ if (c->send_sighup && !sigkill)
+ kill(main_pid, SIGHUP);
+ }
+ }
+
+ 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 " PID_FMT " (%s): %s", control_pid, strna(comm), strerror(-r));
+ } else {
+ wait_for_exit = true;
+
+ if (c->send_sighup && !sigkill)
+ kill(control_pid, SIGHUP);
+ }
+ }
+
+ if ((c->kill_mode == KILL_CONTROL_GROUP || (c->kill_mode == KILL_MIXED && sigkill)) && u->cgroup_path) {
+ _cleanup_set_free_ Set *pid_set = NULL;
+
+ /* Exclude the main/control pids from being killed via the cgroup */
+ pid_set = unit_pid_set(main_pid, control_pid);
+ if (!pid_set)
+ return -ENOMEM;
+
+ r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, sig, true, true, false, pid_set);
+ 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) {
+
+ /* FIXME: For now, we will not wait for the
+ * cgroup members to die, simply because
+ * cgroup notification is unreliable. It
+ * doesn't work at all in containers, and
+ * outside of containers it can be confused
+ * easily by leaving directories in the
+ * cgroup. */
+
+ /* wait_for_exit = true; */
+
+ if (c->send_sighup && !sigkill) {
+ set_free(pid_set);
+
+ pid_set = unit_pid_set(main_pid, control_pid);
+ if (!pid_set)
+ return -ENOMEM;
+
+ cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, SIGHUP, false, true, false, pid_set);
+ }
+ }
+ }
+
+ return wait_for_exit;
+}
+
+int unit_require_mounts_for(Unit *u, const char *path) {
+ char prefix[strlen(path) + 1], *p;
+ int r;
+
+ assert(u);
+ assert(path);
+
+ /* Registers a unit for requiring a certain path and all its
+ * prefixes. We keep a simple array of these paths in the
+ * unit, since its usually short. However, we build a prefix
+ * table for all possible prefixes so that new appearing mount
+ * units can easily determine which units to make themselves a
+ * dependency of. */
+
+ if (!path_is_absolute(path))
+ return -EINVAL;
+
+ p = strdup(path);
+ if (!p)
+ return -ENOMEM;
+
+ path_kill_slashes(p);
+
+ if (!path_is_safe(p)) {
+ free(p);
+ return -EPERM;
+ }
+
+ if (strv_contains(u->requires_mounts_for, p)) {
+ free(p);
+ return 0;
+ }
+
+ r = strv_consume(&u->requires_mounts_for, p);
+ if (r < 0)
+ return r;
+
+ PATH_FOREACH_PREFIX_MORE(prefix, p) {
+ Set *x;
+
+ x = hashmap_get(u->manager->units_requiring_mounts_for, prefix);
+ if (!x) {
+ char *q;
+
+ if (!u->manager->units_requiring_mounts_for) {
+ u->manager->units_requiring_mounts_for = hashmap_new(string_hash_func, string_compare_func);
+ if (!u->manager->units_requiring_mounts_for)
+ return -ENOMEM;
+ }
+
+ q = strdup(prefix);
+ if (!q)
+ return -ENOMEM;
+
+ x = set_new(NULL, NULL);
+ if (!x) {
+ free(q);
+ return -ENOMEM;
+ }
+
+ r = hashmap_put(u->manager->units_requiring_mounts_for, q, x);
+ if (r < 0) {
+ free(q);
+ set_free(x);
+ return r;
+ }
+ }
+
+ r = set_put(x, u);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int unit_setup_exec_runtime(Unit *u) {
+ ExecRuntime **rt;
+ size_t offset;
+ Iterator i;
+ Unit *other;
+
+ offset = UNIT_VTABLE(u)->exec_runtime_offset;
+ assert(offset > 0);
+
+ /* Check if ther already is an ExecRuntime for this unit? */
+ rt = (ExecRuntime**) ((uint8_t*) u + offset);
+ if (*rt)
+ return 0;
+
+ /* Try to get it from somebody else */
+ SET_FOREACH(other, u->dependencies[UNIT_JOINS_NAMESPACE_OF], i) {
+
+ *rt = unit_get_exec_runtime(other);
+ if (*rt) {
+ exec_runtime_ref(*rt);
+ return 0;
+ }
+ }
+
+ return exec_runtime_make(rt, unit_get_exec_context(u), u->id);
+}