1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include "bus-kernel.h"
24 #include "bus-internal.h"
27 #include "dbus-busname.h"
30 static const UnitActiveState state_translation_table[_BUSNAME_STATE_MAX] = {
31 [BUSNAME_DEAD] = UNIT_INACTIVE,
32 [BUSNAME_LISTENING] = UNIT_ACTIVE,
33 [BUSNAME_RUNNING] = UNIT_ACTIVE,
34 [BUSNAME_FAILED] = UNIT_FAILED
37 static int busname_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
39 static void busname_init(Unit *u) {
40 BusName *n = BUSNAME(u);
43 assert(u->load_state == UNIT_STUB);
49 static void busname_done(Unit *u) {
50 BusName *n = BUSNAME(u);
57 unit_ref_unset(&n->service);
59 n->event_source = sd_event_source_unref(n->event_source);
60 n->starter_fd = safe_close(n->starter_fd);
63 static int busname_add_default_default_dependencies(BusName *n) {
68 r = unit_add_dependency_by_name(UNIT(n), UNIT_BEFORE, SPECIAL_BUSNAMES_TARGET, NULL, true);
72 if (UNIT(n)->manager->running_as == SYSTEMD_SYSTEM) {
73 r = unit_add_two_dependencies_by_name(UNIT(n), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
78 return unit_add_two_dependencies_by_name(UNIT(n), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
81 static int busname_add_extras(BusName *n) {
88 n->name = unit_name_to_prefix(u->id);
93 if (!u->description) {
94 r = unit_set_description(u, n->name);
99 if (!UNIT_DEREF(n->service)) {
102 r = unit_load_related_unit(u, ".service", &x);
106 unit_ref_set(&n->service, x);
109 r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(n->service), true);
113 if (u->default_dependencies) {
114 r = busname_add_default_default_dependencies(n);
122 static int busname_verify(BusName *n) {
127 if (UNIT(n)->load_state != UNIT_LOADED)
130 if (!service_name_is_valid(n->name)) {
131 log_error_unit(UNIT(n)->id, "%s's Name= setting is not a valid service name Refusing.", UNIT(n)->id);
135 e = strappenda(n->name, ".busname");
136 if (!unit_has_name(UNIT(n), e)) {
137 log_error_unit(UNIT(n)->id, "%s's Name= setting doesn't match unit name. Refusing.", UNIT(n)->id);
144 static int busname_load(Unit *u) {
145 BusName *n = BUSNAME(u);
149 assert(u->load_state == UNIT_STUB);
151 r = unit_load_fragment_and_dropin(u);
155 if (u->load_state == UNIT_LOADED) {
156 /* This is a new unit? Then let's add in some extras */
157 r = busname_add_extras(n);
162 return busname_verify(n);
165 static void busname_dump(Unit *u, FILE *f, const char *prefix) {
166 BusName *n = BUSNAME(u);
172 "%sBus Name State: %s\n"
176 prefix, busname_state_to_string(n->state),
177 prefix, busname_result_to_string(n->result),
179 prefix, yes_no(n->accept_fd));
182 static void busname_unwatch_fd(BusName *n) {
187 if (n->event_source) {
188 r = sd_event_source_set_enabled(n->event_source, SD_EVENT_OFF);
190 log_debug_unit(UNIT(n)->id, "Failed to disable event source.");
194 static void busname_close_fd(BusName *n) {
197 busname_unwatch_fd(n);
199 if (n->starter_fd <= 0)
202 n->starter_fd = safe_close(n->starter_fd);
205 static int busname_watch_fd(BusName *n) {
210 if (n->starter_fd < 0)
214 r = sd_event_source_set_enabled(n->event_source, SD_EVENT_ON);
216 r = sd_event_add_io(UNIT(n)->manager->event, &n->event_source, n->starter_fd, EPOLLIN, busname_dispatch_io, n);
218 log_warning_unit(UNIT(n)->id, "Failed to watch starter fd: %s", strerror(-r));
219 busname_unwatch_fd(n);
226 static int busname_open_fd(BusName *n) {
229 if (n->starter_fd >= 0)
232 n->starter_fd = bus_kernel_create_starter(
233 UNIT(n)->manager->running_as == SYSTEMD_SYSTEM ? "system" : "user",
234 n->name, n->accept_fd, n->policy);
236 if (n->starter_fd < 0) {
237 log_warning_unit(UNIT(n)->id, "Failed to create starter fd: %s", strerror(-n->starter_fd));
238 return n->starter_fd;
244 static void busname_set_state(BusName *n, BusNameState state) {
245 BusNameState old_state;
248 old_state = n->state;
251 if (state != BUSNAME_LISTENING)
252 busname_unwatch_fd(n);
254 if (!IN_SET(state, BUSNAME_LISTENING, BUSNAME_RUNNING))
257 if (state != old_state)
258 log_debug_unit(UNIT(n)->id, "%s changed %s -> %s",
259 UNIT(n)->id, busname_state_to_string(old_state), busname_state_to_string(state));
261 unit_notify(UNIT(n), state_translation_table[old_state], state_translation_table[state], true);
264 static int busname_coldplug(Unit *u) {
265 BusName *n = BUSNAME(u);
269 assert(n->state == BUSNAME_DEAD);
271 if (n->deserialized_state == n->state)
274 if (IN_SET(n->deserialized_state, BUSNAME_LISTENING, BUSNAME_RUNNING)) {
275 r = busname_open_fd(n);
280 if (n->deserialized_state == BUSNAME_LISTENING) {
281 r = busname_watch_fd(n);
286 busname_set_state(n, n->deserialized_state);
290 static void busname_enter_dead(BusName *n, BusNameResult f) {
293 if (f != BUSNAME_SUCCESS)
296 busname_set_state(n, n->result != BUSNAME_SUCCESS ? BUSNAME_FAILED : BUSNAME_DEAD);
299 static void busname_enter_listening(BusName *n) {
304 r = busname_open_fd(n);
306 log_warning_unit(UNIT(n)->id, "%s failed to listen on bus names: %s", UNIT(n)->id, strerror(-r));
310 r = busname_watch_fd(n);
312 log_warning_unit(UNIT(n)->id, "%s failed to watch names: %s", UNIT(n)->id, strerror(-r));
316 busname_set_state(n, BUSNAME_LISTENING);
320 busname_enter_dead(n, BUSNAME_FAILURE_RESOURCES);
323 static void busname_enter_running(BusName *n) {
324 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
325 bool pending = false;
332 /* We don't take conenctions anymore if we are supposed to
333 * shut down anyway */
335 if (unit_stop_pending(UNIT(n))) {
336 log_debug_unit(UNIT(n)->id, "Suppressing activation request on %s since unit stop is scheduled.", UNIT(n)->id);
338 /* Flush all queued activation reqeuest by closing and reopening the connection */
339 bus_kernel_drop_one(n->starter_fd);
341 busname_enter_listening(n);
345 /* If there's already a start pending don't bother to do
347 SET_FOREACH(other, UNIT(n)->dependencies[UNIT_TRIGGERS], i)
348 if (unit_active_or_pending(other)) {
354 r = manager_add_job(UNIT(n)->manager, JOB_START, UNIT_DEREF(n->service), JOB_REPLACE, true, &error, NULL);
359 busname_set_state(n, BUSNAME_RUNNING);
363 log_warning_unit(UNIT(n)->id, "%s failed to queue service startup job: %s", UNIT(n)->id, bus_error_message(&error, r));
364 busname_enter_dead(n, BUSNAME_FAILURE_RESOURCES);
367 static int busname_start(Unit *u) {
368 BusName *n = BUSNAME(u);
372 if (UNIT_ISSET(n->service)) {
375 service = SERVICE(UNIT_DEREF(n->service));
377 if (UNIT(service)->load_state != UNIT_LOADED) {
378 log_error_unit(u->id, "Bus service %s not loaded, refusing.", UNIT(service)->id);
383 assert(IN_SET(n->state, BUSNAME_DEAD, BUSNAME_FAILED));
385 n->result = BUSNAME_SUCCESS;
386 busname_enter_listening(n);
391 static int busname_stop(Unit *u) {
392 BusName *n = BUSNAME(u);
395 assert(n->state == BUSNAME_LISTENING || n->state == BUSNAME_RUNNING);
397 busname_enter_dead(n, BUSNAME_SUCCESS);
401 static int busname_serialize(Unit *u, FILE *f, FDSet *fds) {
402 BusName *n = BUSNAME(u);
408 unit_serialize_item(u, f, "state", busname_state_to_string(n->state));
409 unit_serialize_item(u, f, "result", busname_result_to_string(n->result));
411 if (n->starter_fd >= 0) {
414 copy = fdset_put_dup(fds, n->starter_fd);
418 unit_serialize_item_format(u, f, "starter-fd", "%i", copy);
424 static int busname_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
425 BusName *n = BUSNAME(u);
431 if (streq(key, "state")) {
434 state = busname_state_from_string(value);
436 log_debug_unit(u->id, "Failed to parse state value %s", value);
438 n->deserialized_state = state;
440 } else if (streq(key, "result")) {
443 f = busname_result_from_string(value);
445 log_debug_unit(u->id, "Failed to parse result value %s", value);
446 else if (f != BUSNAME_SUCCESS)
449 } else if (streq(key, "starter-fd")) {
452 if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
453 log_debug_unit(u->id, "Failed to parse starter fd value %s", value);
455 safe_close(n->starter_fd);
456 n->starter_fd = fdset_remove(fds, fd);
459 log_debug_unit(u->id, "Unknown serialization key '%s'", key);
464 _pure_ static UnitActiveState busname_active_state(Unit *u) {
467 return state_translation_table[BUSNAME(u)->state];
470 _pure_ static const char *busname_sub_state_to_string(Unit *u) {
473 return busname_state_to_string(BUSNAME(u)->state);
476 static int busname_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
477 BusName *n = userdata;
482 if (n->state != BUSNAME_LISTENING)
485 log_debug_unit(UNIT(n)->id, "Activation request on %s", UNIT(n)->id);
487 if (revents != EPOLLIN) {
488 log_error_unit(UNIT(n)->id, "%s: Got unexpected poll event (0x%x) on starter fd.",
489 UNIT(n)->id, revents);
493 busname_enter_running(n);
497 busname_enter_dead(n, BUSNAME_FAILURE_RESOURCES);
501 static void busname_reset_failed(Unit *u) {
502 BusName *n = BUSNAME(u);
506 if (n->state == BUSNAME_FAILED)
507 busname_set_state(n, BUSNAME_DEAD);
509 n->result = BUSNAME_SUCCESS;
512 static void busname_trigger_notify(Unit *u, Unit *other) {
513 BusName *n = BUSNAME(u);
519 if (!IN_SET(n->state, BUSNAME_RUNNING, BUSNAME_LISTENING))
522 if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE)
527 if (s->state == SERVICE_FAILED && s->result == SERVICE_FAILURE_START_LIMIT)
528 busname_enter_dead(n, BUSNAME_FAILURE_SERVICE_FAILED_PERMANENT);
529 else if (IN_SET(s->state,
530 SERVICE_DEAD, SERVICE_FAILED,
531 SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL,
532 SERVICE_STOP_POST, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
533 SERVICE_AUTO_RESTART))
534 busname_enter_listening(n);
537 static const char* const busname_state_table[_BUSNAME_STATE_MAX] = {
538 [BUSNAME_DEAD] = "dead",
539 [BUSNAME_LISTENING] = "listening",
540 [BUSNAME_RUNNING] = "running",
541 [BUSNAME_FAILED] = "failed"
544 DEFINE_STRING_TABLE_LOOKUP(busname_state, BusNameState);
546 static const char* const busname_result_table[_BUSNAME_RESULT_MAX] = {
547 [BUSNAME_SUCCESS] = "success",
548 [BUSNAME_FAILURE_RESOURCES] = "resources",
549 [BUSNAME_FAILURE_SERVICE_FAILED_PERMANENT] = "service-failed-permanent",
552 DEFINE_STRING_TABLE_LOOKUP(busname_result, BusNameResult);
554 static const char* const busname_policy_access_table[_BUSNAME_POLICY_ACCESS_MAX] = {
555 [BUSNAME_POLICY_ACCESS_SEE] = "see",
556 [BUSNAME_POLICY_ACCESS_TALK] = "talk",
557 [BUSNAME_POLICY_ACCESS_OWN] = "own",
560 DEFINE_STRING_TABLE_LOOKUP(busname_policy_access, BusNamePolicyAccess);
562 const UnitVTable busname_vtable = {
563 .object_size = sizeof(BusName),
569 .private_section = "BusName",
571 .init = busname_init,
572 .done = busname_done,
573 .load = busname_load,
575 .coldplug = busname_coldplug,
577 .dump = busname_dump,
579 .start = busname_start,
580 .stop = busname_stop,
582 .serialize = busname_serialize,
583 .deserialize_item = busname_deserialize_item,
585 .active_state = busname_active_state,
586 .sub_state_to_string = busname_sub_state_to_string,
588 .trigger_notify = busname_trigger_notify,
590 .reset_failed = busname_reset_failed,
592 .bus_interface = "org.freedesktop.systemd1.BusName",
593 .bus_vtable = bus_busname_vtable,
595 .status_message_formats = {
596 .finished_start_job = {
597 [JOB_DONE] = "Listening on %s.",
598 [JOB_FAILED] = "Failed to listen on %s.",
599 [JOB_DEPENDENCY] = "Dependency failed for %s.",
600 [JOB_TIMEOUT] = "Timed out starting %s.",
602 .finished_stop_job = {
603 [JOB_DONE] = "Closed %s.",
604 [JOB_FAILED] = "Failed stopping %s.",
605 [JOB_TIMEOUT] = "Timed out stopping %s.",