X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Funit.c;h=a6cc3b6102140df840d708e03184579547948e2b;hp=d26f6e456c1dcfb5a451b2a495eee0357e561059;hb=6b19ad24d3b6ade48d9e3d29e5348fc6b8d7c811;hpb=8755586eba8b824851f11aa6c3f8f184ed69a30a diff --git a/src/core/unit.c b/src/core/unit.c index d26f6e456..a6cc3b610 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -45,6 +45,9 @@ #include "cgroup-util.h" #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, @@ -613,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; } @@ -744,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) @@ -991,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 @@ -1022,9 +1025,9 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { t == JOB_STOP ? SD_MESSAGE_UNIT_STOPPING : SD_MESSAGE_UNIT_RELOADING; - log_struct(LOG_INFO, + log_struct_unit(LOG_INFO, + u->id, MESSAGE_ID(mid), - "UNIT=%s", u->id, "MESSAGE=%s", buf, NULL); } @@ -1317,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); @@ -1329,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); @@ -1351,6 +1357,16 @@ 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)) { + if (UNIT_IS_INACTIVE_OR_FAILED(ns)) + m->n_on_console--; + else + m->n_on_console++; + } + } + if (u->job) { unexpected = false; @@ -1417,7 +1433,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 @@ -1438,9 +1454,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su check_unneeded_dependencies(u); if (ns != os && ns == UNIT_FAILED) { - log_struct(LOG_NOTICE, + log_struct_unit(LOG_NOTICE, + u->id, "MESSAGE=Unit %s entered failed state", u->id, - "UNIT=%s", u->id, NULL); unit_trigger_on_failure(u); } @@ -1453,18 +1469,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 { @@ -1474,29 +1490,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); @@ -1895,30 +1912,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; } @@ -2104,7 +2103,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); @@ -2148,58 +2147,78 @@ 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; CGroupAttribute *a; + int r; assert(u); - assert(name); assert(value); - if (!controller) { - const char *dot; + if (semantics) { + /* Semantics always take precedence */ + if (semantics->name) + name = semantics->name; - dot = strchr(name, '.'); - if (!dot) - return -EINVAL; + if (semantics->controller) + controller = semantics->controller; + } - c = strndup(name, dot - name); - if (!c) - return -ENOMEM; + if (!name) + return -EINVAL; + + if (!controller) { + r = cg_controller_from_attr(name, &c); + if (r < 0) + return -EINVAL; controller = c; } - if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) + 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 */ - if (ret) - *ret = a; + v = strdup(value); + if (!v) + return -ENOMEM; - return 1; + free(a->value); + a->value = v; + + if (ret) + *ret = a; + return 1; + } } a = new0(CGroupAttribute, 1); @@ -2220,11 +2239,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); @@ -2429,6 +2447,9 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { job_free(j); return r; } + + if (j->state == JOB_RUNNING) + u->manager->n_running_jobs++; } else { /* legacy */ JobType type = job_type_from_string(v); @@ -2531,21 +2552,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) { @@ -2757,6 +2765,155 @@ ExecContext *unit_get_exec_context(Unit *u) { return (ExecContext*) ((uint8_t*) u + offset); } +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_USER && runtime) + return -ENOTSUP; + + if (!filename_is_safe(name)) + return -EINVAL; + + 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) { + free(p); + return -ENOMEM; + } + + *_p = p; + *_q = q; + return 0; +} + +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); + + r = drop_in_file(u, runtime, name, &p, &q); + if (r < 0) + return r; + + mkdir_p(p, 0755); + return write_one_line_file_atomic_label(q, data); +} + +int unit_remove_drop_in(Unit *u, bool runtime, const char *name) { + _cleanup_free_ char *p = NULL, *q = NULL; + int r; + + assert(u); + + r = drop_in_file(u, runtime, name, &p, &q); + if (unlink(q) < 0) + r = -errno; + else + r = 0; + + rmdir(p); + return r; +} + +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 = 0, 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 %li (%s): %s", + (long) main_pid, strna(comm), strerror(-r)); + } else + wait_for_exit = !main_pid_alien; + } + + 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 %li (%s): %s", + (long) control_pid, strna(comm), strerror(-r)); + } else + wait_for_exit = true; + } + + if (c->kill_mode == KILL_CONTROL_GROUP) { + _cleanup_set_free_ Set *pid_set = NULL; + + pid_set = set_new(trivial_hash_func, trivial_compare_func); + if (!pid_set) + return -ENOMEM; + + /* Exclude the main/control pids from being killed via the cgroup */ + if (main_pid > 0) { + r = set_put(pid_set, LONG_TO_PTR(main_pid)); + if (r < 0) + return r; + } + + if (control_pid > 0) { + r = set_put(pid_set, LONG_TO_PTR(control_pid)); + if (r < 0) + return r; + } + + r = cgroup_bonding_kill_list(u->cgroup_bondings, sig, true, false, pid_set, NULL); + 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) + wait_for_exit = true; + } + + return wait_for_exit; +} + static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = { [UNIT_ACTIVE] = "active", [UNIT_RELOADING] = "reloading",