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/>.
22 #include <sys/socket.h>
31 #include "bus-error.h"
32 #include "bus-message.h"
36 static int quit_callback(sd_bus *bus, sd_bus_message *m, void *userdata) {
37 sd_event *e = userdata;
43 sd_event_request_quit(e);
47 int bus_async_unregister_and_quit(sd_event *e, sd_bus *bus, const char *name) {
48 _cleanup_free_ char *match = NULL;
55 r = asprintf(&match, "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameLost',arg0='%s'", name);
59 r = sd_bus_add_match(bus, match, quit_callback, e);
63 r = sd_bus_release_name(bus, name);
67 if (r != SD_BUS_NAME_RELEASED)
73 int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t timeout) {
82 r = sd_event_get_state(e);
86 if (r == SD_EVENT_FINISHED)
89 r = sd_event_run(e, exiting ? (uint64_t) -1 : timeout);
93 if (r == 0 && !exiting) {
94 r = bus_async_unregister_and_quit(e, bus, name);
105 int bus_property_get_tristate(
108 const char *interface,
109 const char *property,
110 sd_bus_message *reply,
114 int *tristate = userdata;
117 r = sd_bus_message_append(reply, "b", *tristate > 0);
124 int bus_verify_polkit(
140 sender = sd_bus_message_get_sender(m);
144 r = sd_bus_get_owner_uid(bus, sender, &uid);
153 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
154 unsigned authorized = false, challenge = false;
156 r = sd_bus_call_method(
158 "org.freedesktop.PolicyKit1",
159 "/org/freedesktop/PolicyKit1/Authority",
160 "org.freedesktop.PolicyKit1.Authority",
161 "CheckAuthorization",
165 "system-bus-name", 1, "name", "s", sender,
172 /* Treat no PK available as access denied */
173 if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
174 sd_bus_error_free(e);
181 r = sd_bus_message_read(reply, "(bb)", &authorized, &challenge);
189 *_challenge = challenge;
200 typedef struct AsyncPolkitQuery {
201 sd_bus_message *request, *reply;
202 sd_bus_message_handler_t callback;
207 static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userdata) {
208 AsyncPolkitQuery *q = userdata;
209 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
216 q->reply = sd_bus_message_ref(reply);
219 m = sd_bus_message_ref(q->request);
221 r = sd_bus_message_rewind(m, true);
225 r = q->callback(bus, m, q->userdata);
232 static void async_polkit_query_free(sd_bus *b, AsyncPolkitQuery *q) {
237 if (q->serial > 0 && b)
238 sd_bus_send_with_reply_cancel(b, q->serial);
240 sd_bus_message_unref(q->request);
241 sd_bus_message_unref(q->reply);
247 int bus_verify_polkit_async(
254 sd_bus_message_handler_t callback,
258 _cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
271 q = hashmap_remove(*registry, m);
273 unsigned authorized, challenge;
275 /* This is the second invocation of this function, and
276 * there's already a response from polkit, let's
280 if (sd_bus_message_is_method_error(q->reply, NULL)) {
281 const sd_bus_error *e;
283 /* Treat no PK available as access denied */
284 if (sd_bus_message_is_method_error(q->reply, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
285 async_polkit_query_free(bus, q);
289 e = sd_bus_message_get_error(q->reply);
290 sd_bus_error_copy(error, e);
291 r = sd_bus_error_get_errno(e);
293 async_polkit_query_free(bus, q);
297 r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
299 r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
301 async_polkit_query_free(bus, q);
313 sender = sd_bus_message_get_sender(m);
317 r = sd_bus_get_owner_uid(bus, sender, &uid);
325 r = hashmap_ensure_allocated(registry, trivial_hash_func, trivial_compare_func);
329 r = sd_bus_message_new_method_call(
331 "org.freedesktop.PolicyKit1",
332 "/org/freedesktop/PolicyKit1/Authority",
333 "org.freedesktop.PolicyKit1.Authority",
334 "CheckAuthorization",
339 r = sd_bus_message_append(
342 "system-bus-name", 1, "name", "s", sender,
350 q = new0(AsyncPolkitQuery, 1);
354 q->request = sd_bus_message_ref(m);
355 q->callback = callback;
356 q->userdata = userdata;
358 r = hashmap_put(*registry, m, q);
360 async_polkit_query_free(bus, q);
364 r = sd_bus_send_with_reply(bus, pk, async_polkit_callback, q, 0, &q->serial);
374 void bus_verify_polkit_async_registry_free(sd_bus *bus, Hashmap *registry) {
378 while ((q = hashmap_steal_first(registry)))
379 async_polkit_query_free(bus, q);
381 hashmap_free(registry);
385 static int bus_check_peercred(sd_bus *c) {
392 fd = sd_bus_get_fd(c);
396 l = sizeof(struct ucred);
397 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0)
400 if (l != sizeof(struct ucred))
403 if (ucred.uid != 0 && ucred.uid != geteuid())
409 int bus_open_system_systemd(sd_bus **_bus) {
410 _cleanup_bus_unref_ sd_bus *bus = NULL;
416 return sd_bus_open_system(_bus);
418 /* If we are root, then let's talk directly to the system
419 * instance, instead of going via the bus */
421 r = sd_bus_new(&bus);
425 r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
429 r = sd_bus_start(bus);
433 r = bus_check_peercred(bus);
443 int bus_generic_print_property(const char *name, sd_bus_message *property, bool all) {
445 const char *contents;
450 sd_bus_message_peek_type(property, &type, &contents);
454 case SD_BUS_TYPE_STRING: {
456 sd_bus_message_read_basic(property, type, &s);
458 if (all || !isempty(s))
459 printf("%s=%s\n", name, s);
464 case SD_BUS_TYPE_BOOLEAN: {
467 sd_bus_message_read_basic(property, type, &b);
468 printf("%s=%s\n", name, yes_no(b));
473 case SD_BUS_TYPE_UINT64: {
476 sd_bus_message_read_basic(property, type, &u);
478 /* Yes, heuristics! But we can change this check
479 * should it turn out to not be sufficient */
481 if (endswith(name, "Timestamp")) {
482 char timestamp[FORMAT_TIMESTAMP_MAX], *t;
484 t = format_timestamp(timestamp, sizeof(timestamp), u);
486 printf("%s=%s\n", name, strempty(t));
488 } else if (strstr(name, "USec")) {
489 char timespan[FORMAT_TIMESPAN_MAX];
491 printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
493 printf("%s=%llu\n", name, (unsigned long long) u);
498 case SD_BUS_TYPE_UINT32: {
501 sd_bus_message_read_basic(property, type, &u);
503 if (strstr(name, "UMask") || strstr(name, "Mode"))
504 printf("%s=%04o\n", name, u);
506 printf("%s=%u\n", name, (unsigned) u);
511 case SD_BUS_TYPE_INT32: {
514 sd_bus_message_read_basic(property, type, &i);
516 printf("%s=%i\n", name, (int) i);
520 case SD_BUS_TYPE_DOUBLE: {
523 sd_bus_message_read_basic(property, type, &d);
525 printf("%s=%g\n", name, d);
529 case SD_BUS_TYPE_ARRAY:
531 if (streq(contents, "s")) {
536 sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
538 sd_bus_message_peek_type(property, &tp, &cnt);
545 while(sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str) > 0) {
546 printf("%s%s", space ? " " : "", str);
554 sd_bus_message_exit_container(property);
558 } else if (streq(contents, "y")) {
562 sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
568 for (i = 0; i < n; i++)
569 printf("%02x", u[i]);
576 } else if (streq(contents, "u")) {
580 sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
586 for (i = 0; i < n; i++)
587 printf("%08x", u[i]);
601 int bus_map_all_properties(sd_bus *bus,
602 const char *destination,
604 const struct bus_properties_map *map) {
605 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
606 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
609 r = sd_bus_call_method( bus,
612 "org.freedesktop.DBus.Properties",
618 log_error("Could not get properties: %s", bus_error_message(&error, -r));
622 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
626 while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
629 const char *contents;
632 r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &name);
636 r = sd_bus_message_peek_type(m, NULL, &contents);
640 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
644 r = sd_bus_message_peek_type(m, &type, &contents);
646 log_error("Could not determine type of message: %s", strerror(-r));
651 case SD_BUS_TYPE_STRING: {
654 sd_bus_message_read_basic(m, type, &s);
658 for (i = 0; map[i].type; i++) {
661 if (!streq(map[i].type, "s"))
663 if (!streq(map[i].name, name))
677 case SD_BUS_TYPE_ARRAY: {
678 _cleanup_strv_free_ char **l = NULL;
680 if (!streq(contents, "s"))
683 for (i = 0; map[i].type; i++) {
686 if (!streq(map[i].type, "as"))
688 if (!streq(map[i].name, name))
691 r = bus_message_read_strv_extend(m, &l);
703 case SD_BUS_TYPE_BOOLEAN: {
706 sd_bus_message_read_basic(m, type, &b);
708 for (i = 0; map[i].type; i++) {
711 if (!streq(map[i].type, "b"))
713 if (!streq(map[i].name, name))
722 case SD_BUS_TYPE_UINT64: {
725 sd_bus_message_read_basic(m, type, &t);
727 for (i = 0; map[i].type; i++) {
730 if (!streq(map[i].type, "t"))
732 if (!streq(map[i].name, name))
745 r = sd_bus_message_exit_container(m);
749 r = sd_bus_message_exit_container(m);
758 int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
761 assert(transport >= 0);
762 assert(transport < _BUS_TRANSPORT_MAX);
765 assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
766 assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP);
770 case BUS_TRANSPORT_LOCAL:
772 r = sd_bus_open_user(bus);
774 r = sd_bus_open_system(bus);
778 case BUS_TRANSPORT_REMOTE:
779 r = sd_bus_open_system_remote(host, bus);
782 case BUS_TRANSPORT_CONTAINER:
783 r = sd_bus_open_system_container(host, bus);
787 assert_not_reached("Hmm, unknown transport type.");