X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Funit.c;h=016b1e3d869d846b29806e1be9f758c8529f249d;hp=31d5f113f7ea2ae7c7ea071cd6fe345c8342695c;hb=db57f3c6d11864f4227bb6734a0d509695a32a7d;hpb=28b99ccdcb43937123b170509558af6296a0d271 diff --git a/src/core/unit.c b/src/core/unit.c index 31d5f113f..016b1e3d8 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -50,17 +50,19 @@ #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 @@ -118,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; } @@ -480,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); @@ -670,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 && @@ -762,7 +778,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { prefix, strna(unit_slice_name(u)), prefix, strna(u->cgroup_path), prefix, yes_no(u->cgroup_realized), - prefix, u->cgroup_mask, + prefix, u->cgroup_realized_mask, prefix, u->cgroup_members_mask); SET_FOREACH(t, u->names, i) @@ -931,7 +947,7 @@ int unit_add_default_target_dependency(Unit *u, Unit *target) { return unit_add_dependency(target, UNIT_AFTER, u, true); } -static int unit_add_default_dependencies(Unit *u) { +static int unit_add_target_dependencies(Unit *u) { static const UnitDependency deps[] = { UNIT_REQUIRED_BY, @@ -942,8 +958,8 @@ static int unit_add_default_dependencies(Unit *u) { Unit *target; Iterator i; - int r; unsigned k; + int r = 0; assert(u); @@ -954,20 +970,22 @@ static int unit_add_default_dependencies(Unit *u) { return r; } - if (u->default_dependencies && unit_get_cgroup_context(u)) { - if (UNIT_ISSET(u->slice)) - r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, UNIT_DEREF(u->slice), true); - else - r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, SPECIAL_ROOT_SLICE, NULL, true); + return r; +} - if (r < 0) - return r; - } +static int unit_add_slice_dependencies(Unit *u) { + assert(u); - return 0; + if (!unit_get_cgroup_context(u)) + return 0; + + if (UNIT_ISSET(u->slice)) + return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, UNIT_DEREF(u->slice), true); + + return unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, SPECIAL_ROOT_SLICE, NULL, true); } -static int unit_add_mount_links(Unit *u) { +static int unit_add_mount_dependencies(Unit *u) { char **i; int r; @@ -1034,27 +1052,25 @@ int unit_load(Unit *u) { if (u->load_state == UNIT_LOADED) { - if (u->default_dependencies) { - r = unit_add_default_dependencies(u); - if (r < 0) - goto fail; - } - - unit_update_member_masks(u); - - r = unit_add_mount_links(u); + r = unit_add_target_dependencies(u); if (r < 0) goto fail; - if (u->on_failure_job_mode == JOB_ISOLATE && - set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) { + r = unit_add_slice_dependencies(u); + if (r < 0) + goto fail; - log_error_unit(u->id, - "More than one OnFailure= dependencies specified for %s but OnFailureJobMode=isolate set. Refusing.", u->id); + r = unit_add_mount_dependencies(u); + if (r < 0) + goto fail; + 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 OnFailureJobMode=isolate set. Refusing.", u->id); r = -EINVAL; goto fail; } + + unit_update_cgroup_members_masks(u); } assert((u->load_state != UNIT_MERGED) == !u->merged_into); @@ -1124,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; @@ -1138,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" @@ -1300,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) { @@ -1512,7 +1534,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su * sucessfully, since there's no change of state in that case. Which is * why it is handled in service_set_state() */ if (UNIT_IS_INACTIVE_OR_FAILED(os) != UNIT_IS_INACTIVE_OR_FAILED(ns)) { - ExecContext *ec = unit_get_exec_context(u); + ExecContext *ec; + + 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 --; @@ -1608,12 +1632,11 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su } /* stop unneeded units regardless if going down was expected or not */ - if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns)) + if (UNIT_IS_INACTIVE_OR_DEACTIVATING(ns)) check_unneeded_dependencies(u); if (ns != os && ns == UNIT_FAILED) { - log_notice_unit(u->id, - "Unit %s entered failed state.", u->id); + log_notice_unit(u->id, "Unit %s entered failed state.", u->id); unit_start_on_failure(u); } } @@ -1677,20 +1700,142 @@ 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); - /* Watch a specific PID. We only support one unit watching - * each PID for now. */ + /* Watch a specific PID. We only support one or two units + * watching each PID for now, not more. */ - return hashmap_put(u->manager->watch_pids, LONG_TO_PTR(pid), u); + r = hashmap_ensure_allocated(&u->manager->watch_pids1, trivial_hash_func, trivial_compare_func); + if (r < 0) + return r; + + r = set_ensure_allocated(&u->pids, trivial_hash_func, trivial_compare_func); + if (r < 0) + return r; + + r = hashmap_put(u->manager->watch_pids1, LONG_TO_PTR(pid), u); + if (r == -EEXIST) { + r = hashmap_ensure_allocated(&u->manager->watch_pids2, trivial_hash_func, trivial_compare_func); + if (r < 0) + return r; + + r = hashmap_put(u->manager->watch_pids2, LONG_TO_PTR(pid), u); + } + + q = set_put(u->pids, LONG_TO_PTR(pid)); + if (q < 0) + return q; + + return r; } void unit_unwatch_pid(Unit *u, pid_t pid) { assert(u); assert(pid >= 1); - hashmap_remove_value(u->manager->watch_pids, LONG_TO_PTR(pid), u); + hashmap_remove_value(u->manager->watch_pids1, LONG_TO_PTR(pid), u); + hashmap_remove_value(u->manager->watch_pids2, 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_pids1, e, u); + hashmap_remove_value(u->manager->watch_pids2, 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 (!pid_is_unwaited(pid)) + set_remove(u->pids, e); + } } bool unit_job_is_applicable(Unit *u, JobType j) { @@ -1844,7 +1989,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; @@ -2611,7 +2756,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; } @@ -2715,7 +2860,6 @@ static int drop_in_file(Unit *u, UnitSetPropertiesMode mode, const char *name, c assert(name); assert(_p); assert(_q); - assert(mode & (UNIT_PERSISTENT|UNIT_RUNTIME)); b = xescape(name, "/."); if (!b) @@ -2734,7 +2878,7 @@ static int drop_in_file(Unit *u, UnitSetPropertiesMode mode, const char *name, c return -ENOENT; p = strjoin(c, "/", u->id, ".d", NULL); - } else if (mode & UNIT_PERSISTENT) + } else if (mode == UNIT_PERSISTENT && !u->transient) p = strjoin("/etc/systemd/system/", u->id, ".d", NULL); else p = strjoin("/run/systemd/system/", u->id, ".d", NULL); @@ -2760,7 +2904,7 @@ int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, co assert(name); assert(data); - if (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME))) + if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) return 0; r = drop_in_file(u, mode, name, &p, &q); @@ -2780,7 +2924,7 @@ int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *n assert(name); assert(format); - if (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME))) + if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) return 0; va_start(ap, format); @@ -2803,7 +2947,7 @@ int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char * if (!UNIT_VTABLE(u)->private_section) return -EINVAL; - if (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME))) + if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) return 0; ndata = strjoin("[", UNIT_VTABLE(u)->private_section, "]\n", data, NULL); @@ -2822,7 +2966,7 @@ int unit_write_drop_in_private_format(Unit *u, UnitSetPropertiesMode mode, const assert(name); assert(format); - if (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME))) + if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) return 0; va_start(ap, format); @@ -2841,7 +2985,7 @@ int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name) { assert(u); - if (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME))) + if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) return 0; r = drop_in_file(u, mode, name, &p, &q); @@ -2902,7 +3046,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); @@ -2919,12 +3063,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); } } @@ -2936,18 +3080,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 */ @@ -2960,15 +3102,25 @@ 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) { + + /* 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, true, true, false, pid_set); + cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, SIGHUP, false, true, false, pid_set); } } } @@ -2990,17 +3142,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;