X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Funit.c;h=6e1c112124146f8f39f1dcfc46cf3981bf2ebc16;hp=894485f6d72be6275c1b9871d42f10a273fc02ef;hb=8190da36f71a887945297cd65d6426c78b466a50;hpb=6414b7c981378a6eef480f6806d7cbfc98ca22a1 diff --git a/src/core/unit.c b/src/core/unit.c index 894485f6d..6e1c11212 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -49,17 +49,20 @@ #include "fileio-label.h" #include "bus-errors.h" #include "dbus.h" +#include "execute.h" +#include "virt.h" const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = &service_vtable, - [UNIT_TIMER] = &timer_vtable, [UNIT_SOCKET] = &socket_vtable, + [UNIT_BUSNAME] = &busname_vtable, [UNIT_TARGET] = &target_vtable, + [UNIT_SNAPSHOT] = &snapshot_vtable, [UNIT_DEVICE] = &device_vtable, [UNIT_MOUNT] = &mount_vtable, [UNIT_AUTOMOUNT] = &automount_vtable, - [UNIT_SNAPSHOT] = &snapshot_vtable, [UNIT_SWAP] = &swap_vtable, + [UNIT_TIMER] = &timer_vtable, [UNIT_PATH] = &path_vtable, [UNIT_SLICE] = &slice_vtable, [UNIT_SCOPE] = &scope_vtable @@ -86,6 +89,7 @@ Unit *unit_new(Manager *m, size_t size) { u->deserialized_job = _JOB_TYPE_INVALID; u->default_dependencies = true; u->unit_file_state = _UNIT_FILE_STATE_INVALID; + u->on_failure_job_mode = JOB_REPLACE; return u; } @@ -116,7 +120,7 @@ int unit_add_name(Unit *u, const char *text) { if (!s) return -ENOMEM; - if (!unit_name_is_valid(s, false)) { + if (!unit_name_is_valid(s, TEMPLATE_INVALID)) { r = -EINVAL; goto fail; } @@ -478,6 +482,8 @@ void unit_free(Unit *u) { set_free_free(u->names); + unit_unwatch_all_pids(u); + condition_free_list(u->conditions); unit_ref_unset(&u->slice); @@ -668,6 +674,18 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { assert(u); assert(c); + if (c->working_directory) { + r = unit_require_mounts_for(u, c->working_directory); + if (r < 0) + return r; + } + + if (c->root_directory) { + r = unit_require_mounts_for(u, c->root_directory); + if (r < 0) + return r; + } + if (c->std_output != EXEC_OUTPUT_KMSG && c->std_output != EXEC_OUTPUT_SYSLOG && c->std_output != EXEC_OUTPUT_JOURNAL && @@ -716,6 +734,8 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { timestamp4[FORMAT_TIMESTAMP_MAX], timespan[FORMAT_TIMESPAN_MAX]; Unit *following; + _cleanup_set_free_ Set *following_set = NULL; + int r; assert(u); assert(u->type >= 0); @@ -767,9 +787,18 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { STRV_FOREACH(j, u->documentation) fprintf(f, "%s\tDocumentation: %s\n", prefix, *j); - if ((following = unit_following(u))) + following = unit_following(u); + if (following) fprintf(f, "%s\tFollowing: %s\n", prefix, following->id); + r = unit_following_set(u, &following_set); + if (r >= 0) { + Unit *other; + + SET_FOREACH(other, following_set, i) + fprintf(f, "%s\tFollowing Set Member: %s\n", prefix, other->id); + } + if (u->fragment_path) fprintf(f, "%s\tFragment Path: %s\n", prefix, u->fragment_path); @@ -815,14 +844,14 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tRefuseManualStart: %s\n" "%s\tRefuseManualStop: %s\n" "%s\tDefaultDependencies: %s\n" - "%s\tOnFailureIsolate: %s\n" + "%s\tOnFailureJobMode: %s\n" "%s\tIgnoreOnIsolate: %s\n" "%s\tIgnoreOnSnapshot: %s\n", prefix, yes_no(u->stop_when_unneeded), prefix, yes_no(u->refuse_manual_start), prefix, yes_no(u->refuse_manual_stop), prefix, yes_no(u->default_dependencies), - prefix, yes_no(u->on_failure_isolate), + prefix, job_mode_to_string(u->on_failure_job_mode), prefix, yes_no(u->ignore_on_isolate), prefix, yes_no(u->ignore_on_snapshot)); @@ -1033,11 +1062,11 @@ int unit_load(Unit *u) { if (r < 0) goto fail; - if (u->on_failure_isolate && + if (u->on_failure_job_mode == JOB_ISOLATE && set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) { log_error_unit(u->id, - "More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.", u->id); + "More than one OnFailure= dependencies specified for %s but OnFailureJobMode=isolate set. Refusing.", u->id); r = -EINVAL; goto fail; @@ -1111,6 +1140,8 @@ _pure_ static const char *unit_get_status_message_format_try_harder(Unit *u, Job return NULL; } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" static void unit_status_print_starting_stopping(Unit *u, JobType t) { const char *format; @@ -1125,6 +1156,7 @@ static void unit_status_print_starting_stopping(Unit *u, JobType t) { unit_status_printf(u, "", format); } +#pragma GCC diagnostic pop #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" @@ -1287,8 +1319,11 @@ int unit_reload(Unit *u) { if (state == UNIT_RELOADING) return -EALREADY; - if (state != UNIT_ACTIVE) + if (state != UNIT_ACTIVE) { + log_warning_unit(u->id, "Unit %s cannot be reloaded because it is inactive.", + u->id); return -ENOEXEC; + } following = unit_following(u); if (following) { @@ -1443,7 +1478,7 @@ void unit_start_on_failure(Unit *u) { SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) { int r; - r = manager_add_job(u->manager, JOB_START, other, u->on_failure_isolate ? JOB_ISOLATE : JOB_REPLACE, true, NULL, NULL); + r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, true, NULL, NULL); if (r < 0) log_error_unit(u->id, "Failed to enqueue OnFailure= job: %s", strerror(-r)); } @@ -1664,13 +1699,25 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su } int unit_watch_pid(Unit *u, pid_t pid) { + int q, r; + assert(u); assert(pid >= 1); + r = set_ensure_allocated(&u->pids, trivial_hash_func, trivial_compare_func); + if (r < 0) + return r; + /* Watch a specific PID. We only support one unit watching * each PID for now. */ - return hashmap_put(u->manager->watch_pids, LONG_TO_PTR(pid), u); + r = set_put(u->pids, LONG_TO_PTR(pid)); + + q = hashmap_put(u->manager->watch_pids, LONG_TO_PTR(pid), u); + if (q < 0) + return q; + + return r; } void unit_unwatch_pid(Unit *u, pid_t pid) { @@ -1678,6 +1725,102 @@ void unit_unwatch_pid(Unit *u, pid_t pid) { assert(pid >= 1); hashmap_remove_value(u->manager->watch_pids, LONG_TO_PTR(pid), u); + set_remove(u->pids, LONG_TO_PTR(pid)); +} + +static int watch_pids_in_path(Unit *u, const char *path) { + _cleanup_closedir_ DIR *d = NULL; + _cleanup_fclose_ FILE *f = NULL; + int ret = 0, r; + + assert(u); + assert(path); + + /* Adds all PIDs from a specific cgroup path to the set of PIDs we watch. */ + + r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, path, &f); + if (r >= 0) { + pid_t pid; + + while ((r = cg_read_pid(f, &pid)) > 0) { + r = unit_watch_pid(u, pid); + if (r < 0 && ret >= 0) + ret = r; + } + if (r < 0 && ret >= 0) + ret = r; + + } else if (ret >= 0) + ret = r; + + r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, path, &d); + if (r >= 0) { + char *fn; + + while ((r = cg_read_subgroup(d, &fn)) > 0) { + _cleanup_free_ char *p = NULL; + + p = strjoin(path, "/", fn, NULL); + free(fn); + + if (!p) + return -ENOMEM; + + r = watch_pids_in_path(u, p); + if (r < 0 && ret >= 0) + ret = r; + } + if (r < 0 && ret >= 0) + ret = r; + + } else if (ret >= 0) + ret = r; + + return ret; +} + + +int unit_watch_all_pids(Unit *u) { + assert(u); + + if (!u->cgroup_path) + return -ENOENT; + + /* Adds all PIDs from our cgroup to the set of PIDs we watch */ + + return watch_pids_in_path(u, u->cgroup_path); +} + +void unit_unwatch_all_pids(Unit *u) { + Iterator i; + void *e; + + assert(u); + + SET_FOREACH(e, u->pids, i) + hashmap_remove_value(u->manager->watch_pids, e, u); + + set_free(u->pids); + u->pids = NULL; +} + +void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2) { + Iterator i; + void *e; + + assert(u); + + /* Cleans dead PIDs from our list */ + + SET_FOREACH(e, u->pids, i) { + pid_t pid = PTR_TO_LONG(e); + + if (pid == except1 || pid == except2) + continue; + + if (kill(pid, 0) < 0 && errno == ESRCH) + set_remove(u->pids, e); + } } bool unit_job_is_applicable(Unit *u, JobType j) { @@ -1733,6 +1876,7 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen [UNIT_TRIGGERED_BY] = UNIT_TRIGGERS, [UNIT_PROPAGATES_RELOAD_TO] = UNIT_RELOAD_PROPAGATED_FROM, [UNIT_RELOAD_PROPAGATED_FROM] = UNIT_PROPAGATES_RELOAD_TO, + [UNIT_JOINS_NAMESPACE_OF] = UNIT_JOINS_NAMESPACE_OF, }; int r, q = 0, v = 0, w = 0; @@ -1748,34 +1892,47 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen if (u == other) return 0; - if ((r = set_ensure_allocated(&u->dependencies[d], trivial_hash_func, trivial_compare_func)) < 0) + r = set_ensure_allocated(&u->dependencies[d], trivial_hash_func, trivial_compare_func); + if (r < 0) return r; - if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID) - if ((r = set_ensure_allocated(&other->dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func)) < 0) + if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID) { + r = set_ensure_allocated(&other->dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func); + if (r < 0) return r; + } - if (add_reference) - if ((r = set_ensure_allocated(&u->dependencies[UNIT_REFERENCES], trivial_hash_func, trivial_compare_func)) < 0 || - (r = set_ensure_allocated(&other->dependencies[UNIT_REFERENCED_BY], trivial_hash_func, trivial_compare_func)) < 0) + if (add_reference) { + r = set_ensure_allocated(&u->dependencies[UNIT_REFERENCES], trivial_hash_func, trivial_compare_func); + if (r < 0) return r; - if ((q = set_put(u->dependencies[d], other)) < 0) + r = set_ensure_allocated(&other->dependencies[UNIT_REFERENCED_BY], trivial_hash_func, trivial_compare_func); + if (r < 0) + return r; + } + + q = set_put(u->dependencies[d], other); + if (q < 0) return q; - if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID) - if ((v = set_put(other->dependencies[inverse_table[d]], u)) < 0) { + if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID && inverse_table[d] != d) { + v = set_put(other->dependencies[inverse_table[d]], u); + if (v < 0) { r = v; goto fail; } + } if (add_reference) { - if ((w = set_put(u->dependencies[UNIT_REFERENCES], other)) < 0) { + w = set_put(u->dependencies[UNIT_REFERENCES], other); + if (w < 0) { r = w; goto fail; } - if ((r = set_put(other->dependencies[UNIT_REFERENCED_BY], u)) < 0) + r = set_put(other->dependencies[UNIT_REFERENCED_BY], u); + if (r < 0) goto fail; } @@ -1817,7 +1974,7 @@ static const char *resolve_template(Unit *u, const char *name, const char*path, assert(p); if (!name) - name = path_get_file_name(path); + name = basename(path); if (!unit_name_is_template(name)) { *p = NULL; @@ -2070,6 +2227,7 @@ bool unit_can_serialize(Unit *u) { } int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { + ExecRuntime *rt; int r; assert(u); @@ -2083,17 +2241,11 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { if (r < 0) return r; - - 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); - } + rt = unit_get_exec_runtime(u); + if (rt) { + r = exec_runtime_serialize(rt, u, f, fds); + if (r < 0) + return r; } dual_timestamp_serialize(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp); @@ -2110,6 +2262,18 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { if (u->cgroup_path) unit_serialize_item(u, f, "cgroup", u->cgroup_path); + 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); + } + } + /* End marker */ fputc('\n', f); return 0; @@ -2143,6 +2307,8 @@ void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) { } int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { + size_t offset; + ExecRuntime **rt = NULL; int r; assert(u); @@ -2152,6 +2318,10 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { if (!unit_can_serialize(u)) return 0; + offset = UNIT_VTABLE(u)->exec_runtime_offset; + if (offset > 0) + rt = (ExecRuntime**) ((uint8_t*) u + offset); + for (;;) { char line[LINE_MAX], *l, *v; size_t k; @@ -2264,6 +2434,14 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { continue; } + if (rt) { + r = exec_runtime_deserialize_item(rt, u, l, v, fds); + if (r < 0) + return r; + if (r > 0) + continue; + } + r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds); if (r < 0) return r; @@ -2563,7 +2741,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 == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, - NULL, path_get_file_name(u->fragment_path)); + NULL, basename(u->fragment_path)); return u->unit_file_state; } @@ -2648,6 +2826,16 @@ CGroupContext *unit_get_cgroup_context(Unit *u) { return (CGroupContext*) ((uint8_t*) u + offset); } +ExecRuntime *unit_get_exec_runtime(Unit *u) { + size_t offset; + + offset = UNIT_VTABLE(u)->exec_runtime_offset; + if (offset <= 0) + return NULL; + + return *(ExecRuntime**) ((uint8_t*) u + offset); +} + static int drop_in_file(Unit *u, UnitSetPropertiesMode mode, const char *name, char **_p, char **_q) { _cleanup_free_ char *b = NULL; char *p, *q; @@ -2844,7 +3032,7 @@ int unit_kill_context( pid_t control_pid, bool main_pid_alien) { - int sig, wait_for_exit = 0, r; + int sig, wait_for_exit = false, r; assert(u); assert(c); @@ -2861,12 +3049,12 @@ int unit_kill_context( _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)); + log_warning_unit(u->id, "Failed to kill main process " PID_FMT " (%s): %s", main_pid, strna(comm), strerror(-r)); } else { - wait_for_exit = !main_pid_alien; + if (!main_pid_alien) + wait_for_exit = true; - if (c->send_sighup) + if (c->send_sighup && !sigkill) kill(main_pid, SIGHUP); } } @@ -2878,18 +3066,16 @@ int unit_kill_context( _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)); + 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) + if (c->send_sighup && !sigkill) kill(control_pid, SIGHUP); } } - if (c->kill_mode == KILL_CONTROL_GROUP && u->cgroup_path) { + 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 */ @@ -2902,15 +3088,17 @@ int unit_kill_context( 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; - if (c->send_sighup) { + + 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, true, true, false, pid_set); + cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, SIGHUP, false, true, false, pid_set); } } } @@ -2932,17 +3120,15 @@ int unit_require_mounts_for(Unit *u, const char *path) { * 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_absolute(p)) { - free(p); - return -EINVAL; - } - if (!path_is_safe(p)) { free(p); return -EPERM; @@ -2998,6 +3184,33 @@ int unit_require_mounts_for(Unit *u, const char *path) { 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); +} + static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = { [UNIT_ACTIVE] = "active", [UNIT_RELOADING] = "reloading", @@ -3033,6 +3246,7 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = { [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom", [UNIT_REFERENCES] = "References", [UNIT_REFERENCED_BY] = "ReferencedBy", + [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf", }; DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);