X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fdbus-unit.c;h=47aa8d338c34ac81aa23b32877f4aad396eb8140;hp=ba428a8d5c707515769ab0318e7ff07735cbf30e;hb=dce8e2e12304946a13c261b2324ce1e14ed9f5cd;hpb=7535cc788e37afaf9904d0ee8e2b98f1cb3a4cbd diff --git a/src/dbus-unit.c b/src/dbus-unit.c index ba428a8d5..47aa8d338 100644 --- a/src/dbus-unit.c +++ b/src/dbus-unit.c @@ -1,4 +1,4 @@ -/*-*- Mode: C; c-basic-offset: 8 -*-*/ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ /*** This file is part of systemd. @@ -24,6 +24,21 @@ #include "dbus.h" #include "log.h" #include "dbus-unit.h" +#include "bus-errors.h" + +const char bus_unit_interface[] = BUS_UNIT_INTERFACE; + +#define INVALIDATING_PROPERTIES \ + "LoadState\0" \ + "ActiveState\0" \ + "SubState\0" \ + "InactiveExitTimestamp\0" \ + "ActiveEnterTimestamp\0" \ + "ActiveExitTimestamp\0" \ + "InactiveEnterTimestamp\0" \ + "Job\0" \ + "NeedDaemonReload\0" \ + "\0" int bus_unit_append_names(Manager *m, DBusMessageIter *i, const char *property, void *data) { char *t; @@ -44,6 +59,24 @@ int bus_unit_append_names(Manager *m, DBusMessageIter *i, const char *property, return 0; } +int bus_unit_append_following(Manager *m, DBusMessageIter *i, const char *property, void *data) { + Unit *u = data, *f; + const char *d; + + assert(m); + assert(i); + assert(property); + assert(u); + + f = unit_following(u); + d = f ? f->meta.id : ""; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d)) + return -ENOMEM; + + return 0; +} + int bus_unit_append_dependencies(Manager *m, DBusMessageIter *i, const char *property, void *data) { Unit *u; Iterator j; @@ -125,7 +158,29 @@ int bus_unit_append_can_start(Manager *m, DBusMessageIter *i, const char *proper assert(property); assert(u); - b = unit_can_start(u); + b = unit_can_start(u) && + !u->meta.refuse_manual_start; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b)) + return -ENOMEM; + + return 0; +} + +int bus_unit_append_can_stop(Manager *m, DBusMessageIter *i, const char *property, void *data) { + Unit *u = data; + dbus_bool_t b; + + assert(m); + assert(i); + assert(property); + assert(u); + + /* On the lower levels we assume that every unit we can start + * we can also stop */ + + b = unit_can_start(u) && + !u->meta.refuse_manual_stop; if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b)) return -ENOMEM; @@ -150,6 +205,24 @@ int bus_unit_append_can_reload(Manager *m, DBusMessageIter *i, const char *prope return 0; } +int bus_unit_append_can_isolate(Manager *m, DBusMessageIter *i, const char *property, void *data) { + Unit *u = data; + dbus_bool_t b; + + assert(m); + assert(i); + assert(property); + assert(u); + + b = unit_can_isolate(u) && + !u->meta.refuse_manual_start; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b)) + return -ENOMEM; + + return 0; +} + int bus_unit_append_job(Manager *m, DBusMessageIter *i, const char *property, void *data) { Unit *u = data; DBusMessageIter sub; @@ -251,13 +324,30 @@ int bus_unit_append_cgroups(Manager *m, DBusMessageIter *i, const char *property return 0; } -DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_kill_mode, kill_mode, KillMode); +int bus_unit_append_need_daemon_reload(Manager *m, DBusMessageIter *i, const char *property, void *data) { + Unit *u = data; + dbus_bool_t b; -static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusMessage *message) { + assert(m); + assert(i); + assert(property); + assert(u); + + b = unit_need_daemon_reload(u); + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b)) + return -ENOMEM; + + return 0; +} + +static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) { DBusMessage *reply = NULL; Manager *m = u->meta.manager; DBusError error; JobType job_type = _JOB_TYPE_INVALID; + char *path = NULL; + bool reload_if_possible = false; dbus_error_init(&error); @@ -269,8 +359,51 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusMessage *message job_type = JOB_RELOAD; else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart")) job_type = JOB_RESTART; - else if (UNIT_VTABLE(u)->bus_message_handler) - return UNIT_VTABLE(u)->bus_message_handler(u, message); + else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart")) + job_type = JOB_TRY_RESTART; + else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) { + reload_if_possible = true; + job_type = JOB_RESTART; + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) { + reload_if_possible = true; + job_type = JOB_TRY_RESTART; + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) { + const char *swho, *smode; + int32_t signo; + KillMode mode; + KillWho who; + int r; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_STRING, &swho, + DBUS_TYPE_STRING, &smode, + DBUS_TYPE_INT32, &signo, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(m, connection, message, &error, -EINVAL); + + if ((mode = kill_mode_from_string(smode)) < 0 || + (who = kill_who_from_string(swho)) < 0 || + signo <= 0 || + signo >= _NSIG) + return bus_send_error_reply(m, connection, message, &error, -EINVAL); + + if ((r = unit_kill(u, who, mode, signo, &error)) < 0) + return bus_send_error_reply(m, connection, message, &error, r); + + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) { + + unit_reset_failed(u); + + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + } else if (UNIT_VTABLE(u)->bus_message_handler) + return UNIT_VTABLE(u)->bus_message_handler(u, connection, message); else return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -279,23 +412,36 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusMessage *message JobMode mode; Job *j; int r; - char *path; - if (job_type == JOB_START && u->meta.only_by_dependency) - return bus_send_error_reply(m, message, NULL, -EPERM); + if ((job_type == JOB_START && u->meta.refuse_manual_start) || + (job_type == JOB_STOP && u->meta.refuse_manual_stop) || + ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) && + (u->meta.refuse_manual_start || u->meta.refuse_manual_stop))) { + dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only."); + return bus_send_error_reply(m, connection, message, &error, -EPERM); + } if (!dbus_message_get_args( message, &error, DBUS_TYPE_STRING, &smode, DBUS_TYPE_INVALID)) - return bus_send_error_reply(m, message, &error, -EINVAL); + return bus_send_error_reply(m, connection, message, &error, -EINVAL); + + if (reload_if_possible && unit_can_reload(u)) { + if (job_type == JOB_RESTART) + job_type = JOB_RELOAD_OR_START; + else if (job_type == JOB_TRY_RESTART) + job_type = JOB_RELOAD; + } - if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) - return bus_send_error_reply(m, message, NULL, -EINVAL); + if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) { + dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode); + return bus_send_error_reply(m, connection, message, &error, -EINVAL); + } - if ((r = manager_add_job(m, job_type, u, mode, true, &j)) < 0) - return bus_send_error_reply(m, message, NULL, r); + if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0) + return bus_send_error_reply(m, connection, message, &error, r); if (!(reply = dbus_message_new_method_return(message))) goto oom; @@ -310,8 +456,10 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusMessage *message goto oom; } + free(path); + if (reply) { - if (!dbus_connection_send(m->api_bus, reply, NULL)) + if (!dbus_connection_send(connection, reply, NULL)) goto oom; dbus_message_unref(reply); @@ -320,6 +468,8 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusMessage *message return DBUS_HANDLER_RESULT_HANDLED; oom: + free(path); + if (reply) dbus_message_unref(reply); @@ -328,19 +478,89 @@ oom: return DBUS_HANDLER_RESULT_NEED_MEMORY; } -static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) { +static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) { Manager *m = data; Unit *u; int r; + DBusMessage *reply; assert(connection); assert(message); assert(m); - log_debug("Got D-Bus request: %s.%s() on %s", - dbus_message_get_interface(message), - dbus_message_get_member(message), - dbus_message_get_path(message)); + if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) { + /* Be nice to gdbus and return introspection data for our mid-level paths */ + + if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) { + char *introspection = NULL; + FILE *f; + Iterator i; + const char *k; + size_t size; + + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + /* We roll our own introspection code here, instead of + * relying on bus_default_message_handler() because we + * need to generate our introspection string + * dynamically. */ + + if (!(f = open_memstream(&introspection, &size))) + goto oom; + + fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE + "\n", f); + + fputs(BUS_INTROSPECTABLE_INTERFACE, f); + fputs(BUS_PEER_INTERFACE, f); + + HASHMAP_FOREACH_KEY(u, k, m->units, i) { + char *p; + + if (k != u->meta.id) + continue; + + if (!(p = bus_path_escape(k))) { + fclose(f); + free(introspection); + goto oom; + } + + fprintf(f, "", p); + free(p); + } + + fputs("\n", f); + + if (ferror(f)) { + fclose(f); + free(introspection); + goto oom; + } + + fclose(f); + + if (!introspection) + goto oom; + + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) { + free(introspection); + goto oom; + } + + free(introspection); + + if (!dbus_connection_send(connection, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } if ((r = manager_get_unit_from_dbus_path(m, dbus_message_get_path(message), &u)) < 0) { @@ -350,10 +570,16 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, D if (r == -ENOENT) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - return bus_send_error_reply(m, message, NULL, r); + return bus_send_error_reply(m, connection, message, NULL, r); } - return bus_unit_message_dispatch(u, message); + return bus_unit_message_dispatch(u, connection, message); + +oom: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; } const DBusObjectPathVTable bus_unit_vtable = { @@ -365,12 +591,16 @@ void bus_unit_send_change_signal(Unit *u) { DBusMessage *m = NULL; assert(u); - assert(u->meta.in_dbus_queue); - LIST_REMOVE(Meta, dbus_queue, u->meta.manager->dbus_unit_queue, &u->meta); - u->meta.in_dbus_queue = false; + if (u->meta.in_dbus_queue) { + LIST_REMOVE(Meta, dbus_queue, u->meta.manager->dbus_unit_queue, &u->meta); + u->meta.in_dbus_queue = false; + } + + if (!u->meta.id) + return; - if (set_isempty(u->meta.manager->subscribed)) { + if (!bus_has_subscriber(u->meta.manager)) { u->meta.sent_dbus_new_signal = true; return; } @@ -379,10 +609,27 @@ void bus_unit_send_change_signal(Unit *u) { goto oom; if (u->meta.sent_dbus_new_signal) { - /* Send a change signal */ + /* Send a properties changed signal. First for the + * specific type, then for the generic unit. The + * clients may rely on this order to get atomic + * behaviour if needed. */ + + if (UNIT_VTABLE(u)->bus_invalidating_properties) { - if (!(m = dbus_message_new_signal(p, "org.freedesktop.systemd1.Unit", "Changed"))) + if (!(m = bus_properties_changed_new(p, + UNIT_VTABLE(u)->bus_interface, + UNIT_VTABLE(u)->bus_invalidating_properties))) + goto oom; + + if (bus_broadcast(u->meta.manager, m) < 0) + goto oom; + + dbus_message_unref(m); + } + + if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", INVALIDATING_PROPERTIES))) goto oom; + } else { /* Send a new signal */ @@ -396,7 +643,7 @@ void bus_unit_send_change_signal(Unit *u) { goto oom; } - if (!dbus_connection_send(u->meta.manager->api_bus, m, NULL)) + if (bus_broadcast(u->meta.manager, m) < 0) goto oom; free(p); @@ -421,12 +668,15 @@ void bus_unit_send_removed_signal(Unit *u) { assert(u); - if (set_isempty(u->meta.manager->subscribed)) + if (!bus_has_subscriber(u->meta.manager)) return; if (!u->meta.sent_dbus_new_signal) bus_unit_send_change_signal(u); + if (!u->meta.id) + return; + if (!(p = unit_dbus_path(u))) goto oom; @@ -439,7 +689,7 @@ void bus_unit_send_removed_signal(Unit *u) { DBUS_TYPE_INVALID)) goto oom; - if (!dbus_connection_send(u->meta.manager->api_bus, m, NULL)) + if (bus_broadcast(u->meta.manager, m) < 0) goto oom; free(p);