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>
33 static int quit_callback(sd_bus *bus, sd_bus_message *m, void *userdata) {
34 sd_event *e = userdata;
40 sd_event_request_quit(e);
44 int bus_async_unregister_and_quit(sd_event *e, sd_bus *bus, const char *name) {
45 _cleanup_free_ char *match = NULL;
52 r = asprintf(&match, "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameLost',arg0='%s'", name);
56 r = sd_bus_add_match(bus, match, quit_callback, e);
60 r = sd_bus_release_name(bus, name);
64 if (r != SD_BUS_NAME_RELEASED)
70 int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t timeout) {
79 r = sd_event_get_state(e);
83 if (r == SD_EVENT_FINISHED)
86 r = sd_event_run(e, exiting ? (uint64_t) -1 : timeout);
90 if (r == 0 && !exiting) {
91 r = bus_async_unregister_and_quit(e, bus, name);
102 int bus_property_get_tristate(
105 const char *interface,
106 const char *property,
107 sd_bus_message *reply,
111 int *tristate = userdata;
114 r = sd_bus_message_append(reply, "b", *tristate > 0);
121 int bus_verify_polkit(
137 sender = sd_bus_message_get_sender(m);
141 r = sd_bus_get_owner_uid(bus, sender, &uid);
150 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
151 unsigned authorized = false, challenge = false;
153 r = sd_bus_call_method(
155 "org.freedesktop.PolicyKit1",
156 "/org/freedesktop/PolicyKit1/Authority",
157 "org.freedesktop.PolicyKit1.Authority",
158 "CheckAuthorization",
162 "system-bus-name", 1, "name", "s", sender,
169 /* Treat no PK available as access denied */
170 if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
171 sd_bus_error_free(e);
178 r = sd_bus_message_read(reply, "(bb)", &authorized, &challenge);
186 *_challenge = challenge;
197 typedef struct AsyncPolkitQuery {
198 sd_bus_message *request, *reply;
199 sd_bus_message_handler_t callback;
204 static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userdata) {
205 AsyncPolkitQuery *q = userdata;
206 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
213 q->reply = sd_bus_message_ref(reply);
216 m = sd_bus_message_ref(q->request);
218 r = sd_bus_message_rewind(m, true);
222 r = q->callback(bus, m, q->userdata);
229 static void async_polkit_query_free(sd_bus *b, AsyncPolkitQuery *q) {
234 if (q->serial > 0 && b)
235 sd_bus_send_with_reply_cancel(b, q->serial);
237 sd_bus_message_unref(q->request);
238 sd_bus_message_unref(q->reply);
244 int bus_verify_polkit_async(
251 sd_bus_message_handler_t callback,
255 _cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
268 q = hashmap_remove(*registry, m);
270 unsigned authorized, challenge;
272 /* This is the second invocation of this function, and
273 * there's already a response from polkit, let's
277 if (sd_bus_message_is_method_error(q->reply, NULL)) {
278 const sd_bus_error *e;
280 /* Treat no PK available as access denied */
281 if (sd_bus_message_is_method_error(q->reply, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
282 async_polkit_query_free(bus, q);
286 e = sd_bus_message_get_error(q->reply);
287 sd_bus_error_copy(error, e);
288 r = sd_bus_error_get_errno(e);
290 async_polkit_query_free(bus, q);
294 r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
296 r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
298 async_polkit_query_free(bus, q);
310 sender = sd_bus_message_get_sender(m);
314 r = sd_bus_get_owner_uid(bus, sender, &uid);
322 r = hashmap_ensure_allocated(registry, trivial_hash_func, trivial_compare_func);
326 r = sd_bus_message_new_method_call(
328 "org.freedesktop.PolicyKit1",
329 "/org/freedesktop/PolicyKit1/Authority",
330 "org.freedesktop.PolicyKit1.Authority",
331 "CheckAuthorization",
336 r = sd_bus_message_append(
339 "system-bus-name", 1, "name", "s", sender,
347 q = new0(AsyncPolkitQuery, 1);
351 q->request = sd_bus_message_ref(m);
352 q->callback = callback;
353 q->userdata = userdata;
355 r = hashmap_put(*registry, m, q);
357 async_polkit_query_free(bus, q);
361 r = sd_bus_send_with_reply(bus, pk, async_polkit_callback, q, 0, &q->serial);
371 void bus_verify_polkit_async_registry_free(sd_bus *bus, Hashmap *registry) {
375 while ((q = hashmap_steal_first(registry)))
376 async_polkit_query_free(bus, q);
378 hashmap_free(registry);
382 static int bus_check_peercred(sd_bus *c) {
389 fd = sd_bus_get_fd(c);
393 l = sizeof(struct ucred);
394 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0) {
395 log_error("SO_PEERCRED failed: %m");
399 if (l != sizeof(struct ucred)) {
400 log_error("SO_PEERCRED returned wrong size.");
404 if (ucred.uid != 0 && ucred.uid != geteuid())
410 int bus_connect_system(sd_bus **_bus) {
417 if (geteuid() == 0) {
418 /* If we are root, then let's talk directly to the
419 * system 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);
434 r = sd_bus_open_system(&bus);
442 r = bus_check_peercred(bus);
454 int bus_connect_system_ssh(const char *host, sd_bus **_bus) {
462 asprintf(&p, "unixexec:path=ssh,argv1=-xT,argv2=%s,argv3=systemd-stdio-bridge", host);
466 r = sd_bus_new(&bus);
470 r = sd_bus_set_address(bus, p);
474 r = sd_bus_set_bus_client(bus, true);
478 r = sd_bus_start(bus);
486 int bus_generic_print_property(const char *name, sd_bus_message *property, bool all) {
488 const char *contents;
493 sd_bus_message_peek_type(property, &type, &contents);
497 case SD_BUS_TYPE_STRING: {
499 sd_bus_message_read_basic(property, type, &s);
501 if (all || !isempty(s))
502 printf("%s=%s\n", name, s);
507 case SD_BUS_TYPE_BOOLEAN: {
510 sd_bus_message_read_basic(property, type, &b);
511 printf("%s=%s\n", name, yes_no(b));
516 case SD_BUS_TYPE_UINT64: {
519 sd_bus_message_read_basic(property, type, &u);
521 /* Yes, heuristics! But we can change this check
522 * should it turn out to not be sufficient */
524 if (endswith(name, "Timestamp")) {
525 char timestamp[FORMAT_TIMESTAMP_MAX], *t;
527 t = format_timestamp(timestamp, sizeof(timestamp), u);
529 printf("%s=%s\n", name, strempty(t));
531 } else if (strstr(name, "USec")) {
532 char timespan[FORMAT_TIMESPAN_MAX];
534 printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
536 printf("%s=%llu\n", name, (unsigned long long) u);
541 case SD_BUS_TYPE_UINT32: {
544 sd_bus_message_read_basic(property, type, &u);
546 if (strstr(name, "UMask") || strstr(name, "Mode"))
547 printf("%s=%04o\n", name, u);
549 printf("%s=%u\n", name, (unsigned) u);
554 case SD_BUS_TYPE_INT32: {
557 sd_bus_message_read_basic(property, type, &i);
559 printf("%s=%i\n", name, (int) i);
563 case SD_BUS_TYPE_DOUBLE: {
566 sd_bus_message_read_basic(property, type, &d);
568 printf("%s=%g\n", name, d);
572 case SD_BUS_TYPE_ARRAY:
574 if (streq(contents, "s")) {
579 sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
581 sd_bus_message_peek_type(property, &tp, &cnt);
588 while(sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) {
589 printf("%s%s", space ? " " : "", str);
597 sd_bus_message_exit_container(property);
601 } else if (streq(contents, "y")) {
605 sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
611 for (i = 0; i < n; i++)
612 printf("%02x", u[i]);
619 } else if (streq(contents, "u")) {
623 sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
629 for (i = 0; i < n; i++)
630 printf("%08x", u[i]);