]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-bus/bus-util.c
general: various cleanups
[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) {
0c842e0a
TG
383 struct ucred ucred;
384 socklen_t l;
0f8bd8de 385 int fd;
0c842e0a
TG
386
387 assert(c);
388
389 fd = sd_bus_get_fd(c);
0f8bd8de
LP
390 if (fd < 0)
391 return fd;
0c842e0a
TG
392
393 l = sizeof(struct ucred);
0f8bd8de 394 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0)
0c842e0a 395 return -errno;
0c842e0a 396
0f8bd8de 397 if (l != sizeof(struct ucred))
0c842e0a 398 return -E2BIG;
0c842e0a
TG
399
400 if (ucred.uid != 0 && ucred.uid != geteuid())
401 return -EPERM;
402
403 return 1;
404}
405
0f8bd8de
LP
406int bus_open_system_systemd(sd_bus **_bus) {
407 _cleanup_bus_unref_ sd_bus *bus = NULL;
0c842e0a 408 int r;
0c842e0a
TG
409
410 assert(_bus);
411
0f8bd8de
LP
412 if (geteuid() != 0)
413 return sd_bus_open_system(_bus);
a1da8583 414
0f8bd8de
LP
415 /* If we are root, then let's talk directly to the system
416 * instance, instead of going via the bus */
a1da8583 417
0f8bd8de
LP
418 r = sd_bus_new(&bus);
419 if (r < 0)
420 return r;
a1da8583 421
0f8bd8de
LP
422 r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
423 if (r < 0)
424 return r;
a1da8583 425
0f8bd8de
LP
426 r = sd_bus_start(bus);
427 if (r < 0)
428 return r;
a1da8583 429
0f8bd8de 430 r = bus_check_peercred(bus);
a1da8583
TG
431 if (r < 0)
432 return r;
433
434 *_bus = bus;
0f8bd8de
LP
435 bus = NULL;
436
a1da8583
TG
437 return 0;
438}
439
440int bus_generic_print_property(const char *name, sd_bus_message *property, bool all) {
441 char type;
442 const char *contents;
443
444 assert(name);
445 assert(property);
446
447 sd_bus_message_peek_type(property, &type, &contents);
448
449 switch (type) {
450
451 case SD_BUS_TYPE_STRING: {
452 const char *s;
453 sd_bus_message_read_basic(property, type, &s);
454
455 if (all || !isempty(s))
456 printf("%s=%s\n", name, s);
457
458 return 1;
459 }
460
461 case SD_BUS_TYPE_BOOLEAN: {
462 bool b;
463
464 sd_bus_message_read_basic(property, type, &b);
465 printf("%s=%s\n", name, yes_no(b));
466
467 return 1;
468 }
469
470 case SD_BUS_TYPE_UINT64: {
471 uint64_t u;
472
473 sd_bus_message_read_basic(property, type, &u);
474
475 /* Yes, heuristics! But we can change this check
476 * should it turn out to not be sufficient */
477
478 if (endswith(name, "Timestamp")) {
479 char timestamp[FORMAT_TIMESTAMP_MAX], *t;
480
481 t = format_timestamp(timestamp, sizeof(timestamp), u);
482 if (t || all)
483 printf("%s=%s\n", name, strempty(t));
484
485 } else if (strstr(name, "USec")) {
486 char timespan[FORMAT_TIMESPAN_MAX];
487
488 printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
489 } else
490 printf("%s=%llu\n", name, (unsigned long long) u);
491
492 return 1;
493 }
494
495 case SD_BUS_TYPE_UINT32: {
496 uint32_t u;
497
498 sd_bus_message_read_basic(property, type, &u);
499
500 if (strstr(name, "UMask") || strstr(name, "Mode"))
501 printf("%s=%04o\n", name, u);
502 else
503 printf("%s=%u\n", name, (unsigned) u);
504
505 return 1;
506 }
507
508 case SD_BUS_TYPE_INT32: {
509 int32_t i;
510
511 sd_bus_message_read_basic(property, type, &i);
512
513 printf("%s=%i\n", name, (int) i);
514 return 1;
515 }
516
517 case SD_BUS_TYPE_DOUBLE: {
518 double d;
519
520 sd_bus_message_read_basic(property, type, &d);
521
522 printf("%s=%g\n", name, d);
523 return 1;
524 }
525
526 case SD_BUS_TYPE_ARRAY:
527
528 if (streq(contents, "s")) {
529 bool space = false;
530 char tp;
531 const char *cnt;
532
533 sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
534
535 sd_bus_message_peek_type(property, &tp, &cnt);
536 if (all || cnt) {
537 const char *str;
538
539 printf("%s=", name);
540
541
542 while(sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) {
543 printf("%s%s", space ? " " : "", str);
544
545 space = true;
546 }
547
548 puts("");
549 }
550
551 sd_bus_message_exit_container(property);
552
553 return 1;
554
555 } else if (streq(contents, "y")) {
556 const uint8_t *u;
557 size_t n;
558
559 sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
560 if (all || n > 0) {
561 unsigned int i;
562
563 printf("%s=", name);
564
565 for (i = 0; i < n; i++)
566 printf("%02x", u[i]);
567
568 puts("");
569 }
570
571 return 1;
572
573 } else if (streq(contents, "u")) {
574 uint32_t *u;
575 size_t n;
576
577 sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
578 if (all || n > 0) {
579 unsigned int i;
580
581 printf("%s=", name);
582
583 for (i = 0; i < n; i++)
584 printf("%08x", u[i]);
585
586 puts("");
587 }
588
589 return 1;
590 }
591
592 break;
593 }
594
595 return 0;
596}