]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-bus/bus-util.c
libudev: default log_priority to INFO
[thirdparty/systemd.git] / src / libsystemd-bus / bus-util.c
CommitLineData
40ca29a1
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
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.
12
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.
17
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/>.
20***/
21
0c842e0a
TG
22#include <sys/socket.h>
23
40ca29a1
LP
24#include "sd-event.h"
25#include "sd-bus.h"
26
27#include "util.h"
28#include "macro.h"
29#include "def.h"
30
31#include "bus-util.h"
32
33static int quit_callback(sd_bus *bus, sd_bus_message *m, void *userdata) {
34 sd_event *e = userdata;
35
36 assert(bus);
37 assert(m);
38 assert(e);
39
40 sd_event_request_quit(e);
41 return 1;
42}
43
44int bus_async_unregister_and_quit(sd_event *e, sd_bus *bus, const char *name) {
45 _cleanup_free_ char *match = NULL;
46 int r;
47
48 assert(e);
49 assert(bus);
50 assert(name);
51
52 r = asprintf(&match, "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameLost',arg0='%s'", name);
53 if (r < 0)
54 return r;
55
56 r = sd_bus_add_match(bus, match, quit_callback, e);
57 if (r < 0)
58 return r;
59
60 r = sd_bus_release_name(bus, name);
61 if (r < 0)
62 return r;
63
64 if (r != SD_BUS_NAME_RELEASED)
65 return -EIO;
66
67 return 0;
68}
69
70int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t timeout) {
71 bool exiting = false;
72 int r;
73
74 assert(e);
75 assert(bus);
76 assert(name);
77
78 for (;;) {
79 r = sd_event_get_state(e);
80 if (r < 0)
81 return r;
82
83 if (r == SD_EVENT_FINISHED)
84 break;
85
abc5fe72 86 r = sd_event_run(e, exiting ? (uint64_t) -1 : timeout);
40ca29a1
LP
87 if (r < 0)
88 return r;
89
90 if (r == 0 && !exiting) {
91 r = bus_async_unregister_and_quit(e, bus, name);
92 if (r < 0)
93 return r;
94
95 exiting = true;
96 }
97 }
98
99 return 0;
100}
101
102int bus_property_get_tristate(
103 sd_bus *bus,
104 const char *path,
105 const char *interface,
106 const char *property,
107 sd_bus_message *reply,
108 sd_bus_error *error,
109 void *userdata) {
110
111 int *tristate = userdata;
112 int r;
113
114 r = sd_bus_message_append(reply, "b", *tristate > 0);
115 if (r < 0)
116 return r;
117
118 return 1;
119}
120
121int bus_verify_polkit(
122 sd_bus *bus,
123 sd_bus_message *m,
124 const char *action,
125 bool interactive,
126 bool *_challenge,
127 sd_bus_error *e) {
128
129 const char *sender;
130 uid_t uid;
131 int r;
132
133 assert(bus);
134 assert(m);
135 assert(action);
136
137 sender = sd_bus_message_get_sender(m);
138 if (!sender)
139 return -EBADMSG;
140
141 r = sd_bus_get_owner_uid(bus, sender, &uid);
142 if (r < 0)
143 return r;
144
145 if (uid == 0)
146 return 1;
147
148#ifdef ENABLE_POLKIT
149 else {
150 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
9bcbce42 151 unsigned authorized = false, challenge = false;
40ca29a1
LP
152
153 r = sd_bus_call_method(
154 bus,
155 "org.freedesktop.PolicyKit1",
156 "/org/freedesktop/PolicyKit1/Authority",
157 "org.freedesktop.PolicyKit1.Authority",
158 "CheckAuthorization",
159 e,
160 &reply,
161 "(sa{sv})sa{ss}us",
162 "system-bus-name", 1, "name", "s", sender,
163 action,
164 0,
165 interactive ? 1 : 0,
166 "");
167
168 if (r < 0) {
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);
172 return -EACCES;
173 }
174
175 return r;
176 }
177
178 r = sd_bus_message_read(reply, "(bb)", &authorized, &challenge);
179 if (r < 0)
180 return r;
181
182 if (authorized)
183 return 1;
184
185 if (_challenge) {
186 *_challenge = challenge;
187 return 0;
188 }
189 }
190#endif
191
192 return -EACCES;
193}
194
195#ifdef ENABLE_POLKIT
196
197typedef struct AsyncPolkitQuery {
198 sd_bus_message *request, *reply;
199 sd_bus_message_handler_t callback;
200 void *userdata;
201 uint64_t serial;
202} AsyncPolkitQuery;
203
204static 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;
207 int r;
208
209 assert(bus);
210 assert(reply);
211 assert(q);
212
213 q->reply = sd_bus_message_ref(reply);
214 q->serial = 0;
215
216 m = sd_bus_message_ref(q->request);
217
218 r = sd_bus_message_rewind(m, true);
219 if (r < 0)
220 return r;
221
222 r = q->callback(bus, m, q->userdata);
223 if (r < 0)
224 return r;
225
226 return 1;
227}
228
229static void async_polkit_query_free(sd_bus *b, AsyncPolkitQuery *q) {
230
231 if (!q)
232 return;
233
234 if (q->serial > 0 && b)
235 sd_bus_send_with_reply_cancel(b, q->serial);
236
237 sd_bus_message_unref(q->request);
238 sd_bus_message_unref(q->reply);
239 free(q);
240}
241
242#endif
243
244int bus_verify_polkit_async(
245 sd_bus *bus,
246 Hashmap **registry,
247 sd_bus_message *m,
248 const char *action,
249 bool interactive,
250 sd_bus_error *error,
251 sd_bus_message_handler_t callback,
252 void *userdata) {
253
254#ifdef ENABLE_POLKIT
255 _cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
256 AsyncPolkitQuery *q;
257#endif
258 const char *sender;
259 uid_t uid;
260 int r;
261
262 assert(bus);
263 assert(registry);
264 assert(m);
265 assert(action);
266
267#ifdef ENABLE_POLKIT
268 q = hashmap_remove(*registry, m);
269 if (q) {
9bcbce42 270 unsigned authorized, challenge;
40ca29a1
LP
271
272 /* This is the second invocation of this function, and
273 * there's already a response from polkit, let's
274 * process it */
275 assert(q->reply);
276
277 if (sd_bus_message_is_method_error(q->reply, NULL)) {
278 const sd_bus_error *e;
279
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);
283 return -EACCES;
284 }
285
286 e = sd_bus_message_get_error(q->reply);
287 sd_bus_error_copy(error, e);
288 r = sd_bus_error_get_errno(e);
289
290 async_polkit_query_free(bus, q);
291 return r;
292 }
293
294 r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
295 if (r >= 0)
296 r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
297
298 async_polkit_query_free(bus, q);
299
300 if (r < 0)
301 return r;
302
303 if (authorized)
304 return 1;
305
306 return -EACCES;
307 }
308#endif
309
310 sender = sd_bus_message_get_sender(m);
311 if (!sender)
312 return -EBADMSG;
313
314 r = sd_bus_get_owner_uid(bus, sender, &uid);
315 if (r < 0)
316 return r;
317
318 if (uid == 0)
319 return 1;
320#ifdef ENABLE_POLKIT
321
322 r = hashmap_ensure_allocated(registry, trivial_hash_func, trivial_compare_func);
323 if (r < 0)
324 return r;
325
326 r = sd_bus_message_new_method_call(
327 bus,
328 "org.freedesktop.PolicyKit1",
329 "/org/freedesktop/PolicyKit1/Authority",
330 "org.freedesktop.PolicyKit1.Authority",
331 "CheckAuthorization",
332 &pk);
333 if (r < 0)
334 return r;
335
336 r = sd_bus_message_append(
337 pk,
338 "(sa{sv})sa{ss}us",
339 "system-bus-name", 1, "name", "s", sender,
340 action,
341 0,
342 interactive ? 1 : 0,
343 "");
344 if (r < 0)
345 return r;
346
347 q = new0(AsyncPolkitQuery, 1);
348 if (!q)
349 return -ENOMEM;
350
351 q->request = sd_bus_message_ref(m);
352 q->callback = callback;
353 q->userdata = userdata;
354
355 r = hashmap_put(*registry, m, q);
356 if (r < 0) {
357 async_polkit_query_free(bus, q);
358 return r;
359 }
360
361 r = sd_bus_send_with_reply(bus, pk, async_polkit_callback, q, 0, &q->serial);
362 if (r < 0)
363 return r;
364
365 return 0;
366#endif
367
368 return -EACCES;
369}
370
371void bus_verify_polkit_async_registry_free(sd_bus *bus, Hashmap *registry) {
372#ifdef ENABLE_POLKIT
373 AsyncPolkitQuery *q;
374
375 while ((q = hashmap_steal_first(registry)))
376 async_polkit_query_free(bus, q);
377
378 hashmap_free(registry);
379#endif
380}
0c842e0a
TG
381
382static int bus_check_peercred(sd_bus *c) {
383 int fd;
384 struct ucred ucred;
385 socklen_t l;
386
387 assert(c);
388
389 fd = sd_bus_get_fd(c);
390
391 assert(fd >= 0);
392
393 l = sizeof(struct ucred);
394 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0) {
395 log_error("SO_PEERCRED failed: %m");
396 return -errno;
397 }
398
399 if (l != sizeof(struct ucred)) {
400 log_error("SO_PEERCRED returned wrong size.");
401 return -E2BIG;
402 }
403
404 if (ucred.uid != 0 && ucred.uid != geteuid())
405 return -EPERM;
406
407 return 1;
408}
409
410int bus_connect_system(sd_bus **_bus) {
411 sd_bus *bus = NULL;
412 int r;
413 bool private = true;
414
415 assert(_bus);
416
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 */
420
421 r = sd_bus_new(&bus);
422 if (r < 0)
423 return r;
424
425 r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
426 if (r < 0)
427 return r;
428
429 r = sd_bus_start(bus);
430 if (r < 0)
431 return r;
432
433 } else {
434 r = sd_bus_open_system(&bus);
435 if (r < 0)
436 return r;
437
438 private = false;
439 }
440
441 if (private) {
442 r = bus_check_peercred(bus);
443 if (r < 0) {
444 sd_bus_unref(bus);
445
446 return -EACCES;
447 }
448 }
449
450 *_bus = bus;
451 return 0;
452}
a1da8583
TG
453
454int bus_connect_system_ssh(const char *host, sd_bus **_bus) {
455 sd_bus *bus;
456 char *p = NULL;
457 int r;
458
459 assert(_bus);
460 assert(host);
461
462 asprintf(&p, "unixexec:path=ssh,argv1=-xT,argv2=%s,argv3=systemd-stdio-bridge", host);
463 if (!p)
464 return -ENOMEM;
465
466 r = sd_bus_new(&bus);
467 if (r < 0)
468 return r;
469
470 r = sd_bus_set_address(bus, p);
471 if (r < 0)
472 return r;
473
474 r = sd_bus_set_bus_client(bus, true);
475 if (r < 0)
476 return r;
477
478 r = sd_bus_start(bus);
479 if (r < 0)
480 return r;
481
482 *_bus = bus;
483 return 0;
484}
485
486int bus_generic_print_property(const char *name, sd_bus_message *property, bool all) {
487 char type;
488 const char *contents;
489
490 assert(name);
491 assert(property);
492
493 sd_bus_message_peek_type(property, &type, &contents);
494
495 switch (type) {
496
497 case SD_BUS_TYPE_STRING: {
498 const char *s;
499 sd_bus_message_read_basic(property, type, &s);
500
501 if (all || !isempty(s))
502 printf("%s=%s\n", name, s);
503
504 return 1;
505 }
506
507 case SD_BUS_TYPE_BOOLEAN: {
508 bool b;
509
510 sd_bus_message_read_basic(property, type, &b);
511 printf("%s=%s\n", name, yes_no(b));
512
513 return 1;
514 }
515
516 case SD_BUS_TYPE_UINT64: {
517 uint64_t u;
518
519 sd_bus_message_read_basic(property, type, &u);
520
521 /* Yes, heuristics! But we can change this check
522 * should it turn out to not be sufficient */
523
524 if (endswith(name, "Timestamp")) {
525 char timestamp[FORMAT_TIMESTAMP_MAX], *t;
526
527 t = format_timestamp(timestamp, sizeof(timestamp), u);
528 if (t || all)
529 printf("%s=%s\n", name, strempty(t));
530
531 } else if (strstr(name, "USec")) {
532 char timespan[FORMAT_TIMESPAN_MAX];
533
534 printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
535 } else
536 printf("%s=%llu\n", name, (unsigned long long) u);
537
538 return 1;
539 }
540
541 case SD_BUS_TYPE_UINT32: {
542 uint32_t u;
543
544 sd_bus_message_read_basic(property, type, &u);
545
546 if (strstr(name, "UMask") || strstr(name, "Mode"))
547 printf("%s=%04o\n", name, u);
548 else
549 printf("%s=%u\n", name, (unsigned) u);
550
551 return 1;
552 }
553
554 case SD_BUS_TYPE_INT32: {
555 int32_t i;
556
557 sd_bus_message_read_basic(property, type, &i);
558
559 printf("%s=%i\n", name, (int) i);
560 return 1;
561 }
562
563 case SD_BUS_TYPE_DOUBLE: {
564 double d;
565
566 sd_bus_message_read_basic(property, type, &d);
567
568 printf("%s=%g\n", name, d);
569 return 1;
570 }
571
572 case SD_BUS_TYPE_ARRAY:
573
574 if (streq(contents, "s")) {
575 bool space = false;
576 char tp;
577 const char *cnt;
578
579 sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
580
581 sd_bus_message_peek_type(property, &tp, &cnt);
582 if (all || cnt) {
583 const char *str;
584
585 printf("%s=", name);
586
587
588 while(sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) {
589 printf("%s%s", space ? " " : "", str);
590
591 space = true;
592 }
593
594 puts("");
595 }
596
597 sd_bus_message_exit_container(property);
598
599 return 1;
600
601 } else if (streq(contents, "y")) {
602 const uint8_t *u;
603 size_t n;
604
605 sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
606 if (all || n > 0) {
607 unsigned int i;
608
609 printf("%s=", name);
610
611 for (i = 0; i < n; i++)
612 printf("%02x", u[i]);
613
614 puts("");
615 }
616
617 return 1;
618
619 } else if (streq(contents, "u")) {
620 uint32_t *u;
621 size_t n;
622
623 sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
624 if (all || n > 0) {
625 unsigned int i;
626
627 printf("%s=", name);
628
629 for (i = 0; i < n; i++)
630 printf("%08x", u[i]);
631
632 puts("");
633 }
634
635 return 1;
636 }
637
638 break;
639 }
640
641 return 0;
642}