X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=unit.c;h=30ffd9631182d60fb7f2fd331426f8f0329147c6;hp=6fa878345fcf8f9c89ed5bd90beb4742bd8775d1;hb=5e7ee61c1a2b45c1006336f22ded8e099d155270;hpb=a096ed36f043770ee35cd4d2d292ba0e67ba2c1b diff --git a/unit.c b/unit.c index 6fa878345..30ffd9631 100644 --- a/unit.c +++ b/unit.c @@ -155,6 +155,11 @@ int unit_add_name(Unit *u, const char *text) { if (u->meta.type != _UNIT_TYPE_INVALID && t != u->meta.type) return -EINVAL; + if (u->meta.type != _UNIT_TYPE_INVALID && + UNIT_VTABLE(u)->no_alias && + !set_isempty(u->meta.names)) + return -EEXIST; + if (!(s = strdup(text))) return -ENOMEM; @@ -173,10 +178,14 @@ int unit_add_name(Unit *u, const char *text) { return r; } - if (u->meta.type == _UNIT_TYPE_INVALID) + if (u->meta.type == _UNIT_TYPE_INVALID) { LIST_PREPEND(Meta, units_per_type, u->meta.manager->units_per_type[t], &u->meta); - u->meta.type = t; + u->meta.type = t; + + if (UNIT_VTABLE(u)->init) + UNIT_VTABLE(u)->init(u); + } if (!u->meta.id) u->meta.id = s; @@ -219,6 +228,7 @@ int unit_set_description(Unit *u, const char *description) { void unit_add_to_load_queue(Unit *u) { assert(u); + assert(u->meta.type != _UNIT_TYPE_INVALID); if (u->meta.load_state != UNIT_STUB || u->meta.in_load_queue) return; @@ -239,6 +249,7 @@ void unit_add_to_cleanup_queue(Unit *u) { void unit_add_to_dbus_queue(Unit *u) { assert(u); + assert(u->meta.type != _UNIT_TYPE_INVALID); if (u->meta.load_state == UNIT_STUB || u->meta.in_dbus_queue || set_isempty(u->meta.manager->subscribed)) return; @@ -276,9 +287,6 @@ void unit_free(Unit *u) { bus_unit_send_removed_signal(u); /* Detach from next 'bigger' objects */ - - cgroup_bonding_free_list(u->meta.cgroup_bondings); - SET_FOREACH(t, u->meta.names, i) hashmap_remove_value(u->meta.manager->units, t, u); @@ -294,13 +302,15 @@ void unit_free(Unit *u) { if (u->meta.in_cleanup_queue) LIST_REMOVE(Meta, cleanup_queue, u->meta.manager->cleanup_queue, &u->meta); + /* Free data and next 'smaller' objects */ + if (u->meta.job) + job_free(u->meta.job); + if (u->meta.load_state != UNIT_STUB) if (UNIT_VTABLE(u)->done) UNIT_VTABLE(u)->done(u); - /* Free data and next 'smaller' objects */ - if (u->meta.job) - job_free(u->meta.job); + cgroup_bonding_free_list(u->meta.cgroup_bondings); for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) bidi_set_free(u, u->meta.dependencies[d]); @@ -394,16 +404,22 @@ int unit_merge(Unit *u, Unit *other) { assert(other); assert(u->meta.manager == other->meta.manager); + other = unit_follow_merge(other); + if (other == u) return 0; - /* This merges 'other' into 'unit'. FIXME: This does not - * rollback on failure. */ - if (u->meta.type != u->meta.type) return -EINVAL; - if (other->meta.load_state != UNIT_STUB) + if (other->meta.load_state != UNIT_STUB && + other->meta.load_state != UNIT_FAILED) + return -EEXIST; + + if (other->meta.job) + return -EEXIST; + + if (unit_active_state(other) != UNIT_INACTIVE) return -EEXIST; /* Merge names */ @@ -413,11 +429,16 @@ int unit_merge(Unit *u, Unit *other) { for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) merge_dependencies(u, other, d); - unit_add_to_dbus_queue(u); - other->meta.load_state = UNIT_MERGED; other->meta.merged_into = u; + /* If there is still some data attached to the other node, we + * don't need it anymore, and can free it. */ + if (other->meta.load_state != UNIT_STUB) + if (UNIT_VTABLE(other)->done) + UNIT_VTABLE(other)->done(other); + + unit_add_to_dbus_queue(u); unit_add_to_cleanup_queue(other); return 0; @@ -450,7 +471,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { assert(u); assert(c); - if (c->output != EXEC_OUTPUT_KERNEL && c->output != EXEC_OUTPUT_SYSLOG) + if (c->std_output != EXEC_OUTPUT_KERNEL && c->std_output != EXEC_OUTPUT_SYSLOG) return 0; /* If syslog or kernel logging is requested, make sure our own @@ -491,6 +512,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { char *p2; const char *prefix2; CGroupBonding *b; + char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX]; assert(u); @@ -503,11 +525,15 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s→ Unit %s:\n" "%s\tDescription: %s\n" "%s\tUnit Load State: %s\n" - "%s\tUnit Active State: %s\n", + "%s\tUnit Active State: %s\n" + "%s\tActive Enter Timestamp: %s\n" + "%s\tActive Exit Timestamp: %s\n", prefix, unit_id(u), prefix, unit_description(u), prefix, unit_load_state_to_string(u->meta.load_state), - prefix, unit_active_state_to_string(unit_active_state(u))); + prefix, unit_active_state_to_string(unit_active_state(u)), + prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->meta.active_enter_timestamp)), + prefix, strna(format_timestamp(timestamp2, sizeof(timestamp2), u->meta.active_exit_timestamp))); SET_FOREACH(t, u->meta.names, i) fprintf(f, "%s\tName: %s\n", prefix, t); @@ -518,27 +544,28 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { Unit *other; - if (set_isempty(u->meta.dependencies[d])) - continue; - SET_FOREACH(other, u->meta.dependencies[d], i) fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), unit_id(other)); } - fprintf(f, - "%s\tRecursive Stop: %s\n" - "%s\tStop When Unneeded: %s\n", - prefix, yes_no(u->meta.recursive_stop), - prefix, yes_no(u->meta.stop_when_unneeded)); - if (u->meta.load_state == UNIT_LOADED) { + fprintf(f, + "%s\tRecursive Stop: %s\n" + "%s\tStop When Unneeded: %s\n", + prefix, yes_no(u->meta.recursive_stop), + prefix, yes_no(u->meta.stop_when_unneeded)); + LIST_FOREACH(by_unit, b, u->meta.cgroup_bondings) fprintf(f, "%s\tControlGroup: %s:%s\n", prefix, b->controller, b->path); if (UNIT_VTABLE(u)->dump) UNIT_VTABLE(u)->dump(u, f, prefix2); - } + + } else if (u->meta.load_state == UNIT_MERGED) + fprintf(f, + "%s\tMerged into: %s\n", + prefix, unit_id(u->meta.merged_into)); if (u->meta.job) job_dump(u->meta.job, f, prefix2); @@ -547,18 +574,16 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { } /* Common implementation for multiple backends */ -int unit_load_fragment_and_dropin(Unit *u, UnitLoadState *new_state) { +int unit_load_fragment_and_dropin(Unit *u) { int r; assert(u); - assert(new_state); - assert(*new_state == UNIT_STUB || *new_state == UNIT_LOADED); /* Load a .service file */ - if ((r = unit_load_fragment(u, new_state)) < 0) + if ((r = unit_load_fragment(u)) < 0) return r; - if (*new_state == UNIT_STUB) + if (u->meta.load_state == UNIT_STUB) return -ENOENT; /* Load drop-in directory data */ @@ -569,22 +594,20 @@ int unit_load_fragment_and_dropin(Unit *u, UnitLoadState *new_state) { } /* Common implementation for multiple backends */ -int unit_load_fragment_and_dropin_optional(Unit *u, UnitLoadState *new_state) { +int unit_load_fragment_and_dropin_optional(Unit *u) { int r; assert(u); - assert(new_state); - assert(*new_state == UNIT_STUB || *new_state == UNIT_LOADED); /* Same as unit_load_fragment_and_dropin(), but whether * something can be loaded or not doesn't matter. */ /* Load a .service file */ - if ((r = unit_load_fragment(u, new_state)) < 0) + if ((r = unit_load_fragment(u)) < 0) return r; - if (*new_state == UNIT_STUB) - *new_state = UNIT_LOADED; + if (u->meta.load_state == UNIT_STUB) + u->meta.load_state = UNIT_LOADED; /* Load drop-in directory data */ if ((r = unit_load_dropin(unit_follow_merge(u))) < 0) @@ -595,7 +618,6 @@ int unit_load_fragment_and_dropin_optional(Unit *u, UnitLoadState *new_state) { int unit_load(Unit *u) { int r; - UnitLoadState res; assert(u); @@ -604,21 +626,21 @@ int unit_load(Unit *u) { u->meta.in_load_queue = false; } + if (u->meta.type == _UNIT_TYPE_INVALID) + return -EINVAL; + if (u->meta.load_state != UNIT_STUB) return 0; - if (UNIT_VTABLE(u)->init) { - res = UNIT_STUB; - if ((r = UNIT_VTABLE(u)->init(u, &res)) < 0) + if (UNIT_VTABLE(u)->load) + if ((r = UNIT_VTABLE(u)->load(u)) < 0) goto fail; - } - if (res == UNIT_STUB) { + if (u->meta.load_state == UNIT_STUB) { r = -ENOENT; goto fail; } - u->meta.load_state = res; assert((u->meta.load_state != UNIT_MERGED) == !u->meta.merged_into); unit_add_to_dbus_queue(unit_follow_merge(u)); @@ -689,9 +711,6 @@ int unit_stop(Unit *u) { if (!UNIT_VTABLE(u)->stop) return -EBADR; - if (state == UNIT_DEACTIVATING) - return 0; - unit_add_to_dbus_queue(u); return UNIT_VTABLE(u)->stop(u); } @@ -831,11 +850,13 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { assert(u); assert(os < _UNIT_ACTIVE_STATE_MAX); assert(ns < _UNIT_ACTIVE_STATE_MAX); - assert(!(os == UNIT_ACTIVE && ns == UNIT_ACTIVATING)); - assert(!(os == UNIT_INACTIVE && ns == UNIT_DEACTIVATING)); - if (os == ns) - return; + /* Note that this is called for all low-level state changes, + * even if they might map to the same high-level + * UnitActiveState! That means that ns == os is OK an expected + * behaviour here. For example: if a mount point is remounted + * this function will be called too and the utmp code below + * relies on that! */ if (!UNIT_IS_ACTIVE_OR_RELOADING(os) && UNIT_IS_ACTIVE_OR_RELOADING(ns)) u->meta.active_enter_timestamp = now(CLOCK_REALTIME); @@ -914,25 +935,38 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { retroactively_stop_dependencies(u); } - if (!UNIT_IS_ACTIVE_OR_RELOADING(os) && UNIT_IS_ACTIVE_OR_RELOADING(ns)) { - + /* Some names are special */ + if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) { if (unit_has_name(u, SPECIAL_DBUS_SERVICE)) { - log_info("D-Bus became available, trying to reconnect."); - /* The bus just got started, hence try to connect to it. */ + /* The bus just might have become available, + * hence try to connect to it, if we aren't + * yet connected. */ bus_init_system(u->meta.manager); bus_init_api(u->meta.manager); } - if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE)) { - /* The syslog daemon just got started, hence try to connect to it. */ - log_info("Syslog became available, trying to reconnect."); + if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE)) + /* The syslog daemon just might have become + * available, hence try to connect to it, if + * we aren't yet connected. */ log_open_syslog(); - } - } else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns)) { + if (u->meta.type == UNIT_MOUNT) + /* Another directory became available, let's + * check if that is enough to write our utmp + * entry. */ + manager_write_utmp_reboot(u->meta.manager); + + if (u->meta.type == UNIT_TARGET) + /* A target got activated, maybe this is a runlevel? */ + manager_write_utmp_runlevel(u->meta.manager, u); + + } else if (!UNIT_IS_ACTIVE_OR_RELOADING(ns)) { if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE)) - /* The syslog daemon just got terminated, hence try to disconnect from it. */ + /* The syslog daemon might just have + * terminated, hence try to disconnect from + * it. */ log_close_syslog(); /* We don't care about D-Bus here, since we'll get an @@ -1290,55 +1324,72 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) { return 0; } +static char *default_cgroup_path(Unit *u) { + char *p; + + assert(u); + + if (asprintf(&p, "%s/%s", u->meta.manager->cgroup_hierarchy, unit_id(u)) < 0) + return NULL; + + return p; +} + int unit_add_cgroup_from_text(Unit *u, const char *name) { size_t n; - const char *p; - char *controller; - CGroupBonding *b; + char *controller = NULL, *path = NULL; + CGroupBonding *b = NULL; int r; assert(u); assert(name); /* Detect controller name */ - n = strcspn(name, ":/"); + n = strcspn(name, ":"); - /* Only controller name, no path? No path? */ - if (name[n] == 0) - return -EINVAL; + if (name[n] == 0 || + (name[n] == ':' && name[n+1] == 0)) { - if (n > 0) { - if (name[n] != ':') - return -EINVAL; + /* Only controller name, no path? */ - p = name+n+1; - } else - p = name; + if (!(path = default_cgroup_path(u))) + return -ENOMEM; - /* Insist in absolute paths */ - if (p[0] != '/') - return -EINVAL; + } else { + const char *p; - if (!(controller = strndup(name, n))) - return -ENOMEM; + /* Controller name, and path. */ + p = name+n+1; - if (cgroup_bonding_find_list(u->meta.cgroup_bondings, controller)) { - free(controller); - return -EEXIST; + if (!path_is_absolute(p)) + return -EINVAL; + + if (!(path = strdup(p))) + return -ENOMEM; } - if (!(b = new0(CGroupBonding, 1))) { - free(controller); - return -ENOMEM; + if (n > 0) + controller = strndup(name, n); + else + controller = strdup(u->meta.manager->cgroup_controller); + + if (!controller) { + r = -ENOMEM; + goto fail; } - b->controller = controller; + if (cgroup_bonding_find_list(u->meta.cgroup_bondings, controller)) { + r = -EEXIST; + goto fail; + } - if (!(b->path = strdup(p))) { + if (!(b = new0(CGroupBonding, 1))) { r = -ENOMEM; goto fail; } + b->controller = controller; + b->path = path; b->only_us = false; b->clean_up = false; @@ -1348,8 +1399,8 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) { return 0; fail: - free(b->path); - free(b->controller); + free(path); + free(controller); free(b); return r; @@ -1372,7 +1423,7 @@ int unit_add_default_cgroup(Unit *u) { if (!(b->controller = strdup(u->meta.manager->cgroup_controller))) goto fail; - if (asprintf(&b->path, "%s/%s", u->meta.manager->cgroup_hierarchy, unit_id(u)) < 0) + if (!(b->path = default_cgroup_path(u))) goto fail; b->clean_up = true; @@ -1397,6 +1448,28 @@ CGroupBonding* unit_get_default_cgroup(Unit *u) { return cgroup_bonding_find_list(u->meta.cgroup_bondings, u->meta.manager->cgroup_controller); } +int unit_load_related_unit(Unit *u, const char *type, Unit **_found) { + char *t; + int r; + + assert(u); + assert(type); + assert(_found); + + if (!(t = unit_name_change_suffix(unit_id(u), type))) + return -ENOMEM; + + assert(!unit_has_name(u, t)); + + r = manager_load_unit(u->meta.manager, t, _found); + free(t); + + if (r >= 0) + assert(*_found != u); + + return r; +} + static const char* const unit_type_table[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = "service", [UNIT_TIMER] = "timer", @@ -1445,9 +1518,10 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = { DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency); static const char* const kill_mode_table[_KILL_MODE_MAX] = { - [KILL_PROCESS] = "process", + [KILL_CONTROL_GROUP] = "control-group", [KILL_PROCESS_GROUP] = "process-group", - [KILL_CONTROL_GROUP] = "control-group" + [KILL_PROCESS] = "process", + [KILL_NONE] = "none" }; DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode);