]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/dbus-common.c
build-sys: __secure_getenv lost dunder in libc 2.17
[thirdparty/systemd.git] / src / shared / dbus-common.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 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
22 #include <assert.h>
23 #include <sys/socket.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <dbus/dbus.h>
29 #include <string.h>
30 #include <sys/epoll.h>
31
32 #include "log.h"
33 #include "dbus-common.h"
34 #include "util.h"
35 #include "missing.h"
36 #include "def.h"
37 #include "strv.h"
38
39 int bus_check_peercred(DBusConnection *c) {
40 int fd;
41 struct ucred ucred;
42 socklen_t l;
43
44 assert(c);
45
46 assert_se(dbus_connection_get_unix_fd(c, &fd));
47
48 l = sizeof(struct ucred);
49 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0) {
50 log_error("SO_PEERCRED failed: %m");
51 return -errno;
52 }
53
54 if (l != sizeof(struct ucred)) {
55 log_error("SO_PEERCRED returned wrong size.");
56 return -E2BIG;
57 }
58
59 if (ucred.uid != 0 && ucred.uid != geteuid())
60 return -EPERM;
61
62 return 1;
63 }
64
65 static int sync_auth(DBusConnection *bus, DBusError *error) {
66 usec_t begin, tstamp;
67
68 assert(bus);
69
70 /* This complexity should probably move into D-Bus itself:
71 *
72 * https://bugs.freedesktop.org/show_bug.cgi?id=35189 */
73
74 begin = tstamp = now(CLOCK_MONOTONIC);
75 for (;;) {
76
77 if (tstamp > begin + DEFAULT_TIMEOUT_USEC)
78 break;
79
80 if (dbus_connection_get_is_authenticated(bus))
81 break;
82
83 if (!dbus_connection_read_write_dispatch(bus, ((begin + DEFAULT_TIMEOUT_USEC - tstamp) + USEC_PER_MSEC - 1) / USEC_PER_MSEC))
84 break;
85
86 tstamp = now(CLOCK_MONOTONIC);
87 }
88
89 if (!dbus_connection_get_is_connected(bus)) {
90 dbus_set_error_const(error, DBUS_ERROR_NO_SERVER, "Connection terminated during authentication.");
91 return -ECONNREFUSED;
92 }
93
94 if (!dbus_connection_get_is_authenticated(bus)) {
95 dbus_set_error_const(error, DBUS_ERROR_TIMEOUT, "Failed to authenticate in time.");
96 return -EACCES;
97 }
98
99 return 0;
100 }
101
102 int bus_connect(DBusBusType t, DBusConnection **_bus, bool *_private, DBusError *error) {
103 DBusConnection *bus = NULL;
104 int r;
105 bool private = true;
106
107 assert(_bus);
108
109 if (geteuid() == 0 && t == DBUS_BUS_SYSTEM) {
110 /* If we are root, then let's talk directly to the
111 * system instance, instead of going via the bus */
112
113 bus = dbus_connection_open_private("unix:path=/run/systemd/private", error);
114 if (!bus)
115 return -EIO;
116
117 } else {
118 if (t == DBUS_BUS_SESSION) {
119 const char *e;
120
121 /* If we are supposed to talk to the instance,
122 * try via XDG_RUNTIME_DIR first, then
123 * fallback to normal bus access */
124
125 e = secure_getenv("XDG_RUNTIME_DIR");
126 if (e) {
127 char *p;
128
129 if (asprintf(&p, "unix:path=%s/systemd/private", e) < 0)
130 return -ENOMEM;
131
132 bus = dbus_connection_open_private(p, NULL);
133 free(p);
134 }
135 }
136
137 if (!bus) {
138 bus = dbus_bus_get_private(t, error);
139 if (!bus)
140 return -EIO;
141
142 private = false;
143 }
144 }
145
146 dbus_connection_set_exit_on_disconnect(bus, FALSE);
147
148 if (private) {
149 if (bus_check_peercred(bus) < 0) {
150 dbus_connection_close(bus);
151 dbus_connection_unref(bus);
152
153 dbus_set_error_const(error, DBUS_ERROR_ACCESS_DENIED, "Failed to verify owner of bus.");
154 return -EACCES;
155 }
156 }
157
158 r = sync_auth(bus, error);
159 if (r < 0) {
160 dbus_connection_close(bus);
161 dbus_connection_unref(bus);
162 return r;
163 }
164
165 if (_private)
166 *_private = private;
167
168 *_bus = bus;
169 return 0;
170 }
171
172 int bus_connect_system_ssh(const char *user, const char *host, DBusConnection **_bus, DBusError *error) {
173 DBusConnection *bus;
174 char *p = NULL;
175 int r;
176
177 assert(_bus);
178 assert(user || host);
179
180 if (user && host)
181 asprintf(&p, "unixexec:path=ssh,argv1=-xT,argv2=%s@%s,argv3=systemd-stdio-bridge", user, host);
182 else if (user)
183 asprintf(&p, "unixexec:path=ssh,argv1=-xT,argv2=%s@localhost,argv3=systemd-stdio-bridge", user);
184 else if (host)
185 asprintf(&p, "unixexec:path=ssh,argv1=-xT,argv2=%s,argv3=systemd-stdio-bridge", host);
186
187 if (!p) {
188 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
189 return -ENOMEM;
190 }
191
192 bus = dbus_connection_open_private(p, error);
193 free(p);
194
195 if (!bus)
196 return -EIO;
197
198 dbus_connection_set_exit_on_disconnect(bus, FALSE);
199
200 if ((r = sync_auth(bus, error)) < 0) {
201 dbus_connection_close(bus);
202 dbus_connection_unref(bus);
203 return r;
204 }
205
206 if (!dbus_bus_register(bus, error)) {
207 dbus_connection_close(bus);
208 dbus_connection_unref(bus);
209 return r;
210 }
211
212 *_bus = bus;
213 return 0;
214 }
215
216 int bus_connect_system_polkit(DBusConnection **_bus, DBusError *error) {
217 DBusConnection *bus;
218 int r;
219
220 assert(_bus);
221
222 /* Don't bother with PolicyKit if we are root */
223 if (geteuid() == 0)
224 return bus_connect(DBUS_BUS_SYSTEM, _bus, NULL, error);
225
226 bus = dbus_connection_open_private("unixexec:path=pkexec,argv1=" SYSTEMD_STDIO_BRIDGE_BINARY_PATH, error);
227 if (!bus)
228 return -EIO;
229
230 dbus_connection_set_exit_on_disconnect(bus, FALSE);
231
232 if ((r = sync_auth(bus, error)) < 0) {
233 dbus_connection_close(bus);
234 dbus_connection_unref(bus);
235 return r;
236 }
237
238 if (!dbus_bus_register(bus, error)) {
239 dbus_connection_close(bus);
240 dbus_connection_unref(bus);
241 return r;
242 }
243
244 *_bus = bus;
245 return 0;
246 }
247
248 const char *bus_error_message(const DBusError *error) {
249 if (!error)
250 return NULL;
251
252 /* Sometimes the D-Bus server is a little bit too verbose with
253 * its error messages, so let's override them here */
254 if (dbus_error_has_name(error, DBUS_ERROR_ACCESS_DENIED))
255 return "Access denied";
256
257 return error->message;
258 }
259
260 const char *bus_error_message_or_strerror(const DBusError *error, int err) {
261
262 if (error && dbus_error_is_set(error))
263 return bus_error_message(error);
264
265 return strerror(err);
266 }
267
268 DBusHandlerResult bus_default_message_handler(
269 DBusConnection *c,
270 DBusMessage *message,
271 const char *introspection,
272 const char *interfaces,
273 const BusBoundProperties *bound_properties) {
274
275 DBusError error;
276 DBusMessage *reply = NULL;
277 int r;
278
279 assert(c);
280 assert(message);
281
282 dbus_error_init(&error);
283
284 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") && introspection) {
285
286 if (!(reply = dbus_message_new_method_return(message)))
287 goto oom;
288
289 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID))
290 goto oom;
291
292 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Get") && bound_properties) {
293 const char *interface, *property;
294 const BusBoundProperties *bp;
295 const BusProperty *p;
296 void *data;
297 DBusMessageIter iter, sub;
298
299 if (!dbus_message_get_args(
300 message,
301 &error,
302 DBUS_TYPE_STRING, &interface,
303 DBUS_TYPE_STRING, &property,
304 DBUS_TYPE_INVALID))
305 return bus_send_error_reply(c, message, &error, -EINVAL);
306
307 for (bp = bound_properties; bp->interface; bp++) {
308 if (!streq(bp->interface, interface))
309 continue;
310
311 for (p = bp->properties; p->property; p++)
312 if (streq(p->property, property))
313 goto get_prop;
314 }
315
316 /* no match */
317 if (!nulstr_contains(interfaces, interface))
318 dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface");
319 else
320 dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property");
321
322 return bus_send_error_reply(c, message, &error, -EINVAL);
323
324 get_prop:
325 reply = dbus_message_new_method_return(message);
326 if (!reply)
327 goto oom;
328
329 dbus_message_iter_init_append(reply, &iter);
330
331 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, p->signature, &sub))
332 goto oom;
333
334 data = (char*)bp->base + p->offset;
335 if (p->indirect)
336 data = *(void**)data;
337 r = p->append(&sub, property, data);
338 if (r < 0) {
339 if (r == -ENOMEM)
340 goto oom;
341
342 dbus_message_unref(reply);
343 return bus_send_error_reply(c, message, NULL, r);
344 }
345
346 if (!dbus_message_iter_close_container(&iter, &sub))
347 goto oom;
348
349 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "GetAll") && bound_properties) {
350 const char *interface;
351 const BusBoundProperties *bp;
352 const BusProperty *p;
353 DBusMessageIter iter, sub, sub2, sub3;
354
355 if (!dbus_message_get_args(
356 message,
357 &error,
358 DBUS_TYPE_STRING, &interface,
359 DBUS_TYPE_INVALID))
360 return bus_send_error_reply(c, message, &error, -EINVAL);
361
362 if (interface[0] && !nulstr_contains(interfaces, interface)) {
363 dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface");
364 return bus_send_error_reply(c, message, &error, -EINVAL);
365 }
366
367 if (!(reply = dbus_message_new_method_return(message)))
368 goto oom;
369
370 dbus_message_iter_init_append(reply, &iter);
371
372 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub))
373 goto oom;
374
375 for (bp = bound_properties; bp->interface; bp++) {
376 if (interface[0] && !streq(bp->interface, interface))
377 continue;
378
379 for (p = bp->properties; p->property; p++) {
380 void *data;
381
382 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, NULL, &sub2) ||
383 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &p->property) ||
384 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, p->signature, &sub3))
385 goto oom;
386
387 data = (char*)bp->base + p->offset;
388 if (p->indirect)
389 data = *(void**)data;
390 r = p->append(&sub3, p->property, data);
391 if (r < 0) {
392 if (r == -ENOMEM)
393 goto oom;
394
395 dbus_message_unref(reply);
396 return bus_send_error_reply(c, message, NULL, r);
397 }
398
399 if (!dbus_message_iter_close_container(&sub2, &sub3) ||
400 !dbus_message_iter_close_container(&sub, &sub2))
401 goto oom;
402 }
403 }
404
405 if (!dbus_message_iter_close_container(&iter, &sub))
406 goto oom;
407
408 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Set") && bound_properties) {
409 const char *interface, *property;
410 DBusMessageIter iter;
411 const BusBoundProperties *bp;
412 const BusProperty *p;
413 DBusMessageIter sub;
414 char *sig;
415 void *data;
416 DBusMessage *changed;
417
418 if (!dbus_message_iter_init(message, &iter) ||
419 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
420 return bus_send_error_reply(c, message, NULL, -EINVAL);
421
422 dbus_message_iter_get_basic(&iter, &interface);
423
424 if (!dbus_message_iter_next(&iter) ||
425 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
426 return bus_send_error_reply(c, message, NULL, -EINVAL);
427
428 dbus_message_iter_get_basic(&iter, &property);
429
430 if (!dbus_message_iter_next(&iter) ||
431 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT ||
432 dbus_message_iter_has_next(&iter))
433 return bus_send_error_reply(c, message, NULL, -EINVAL);
434
435 for (bp = bound_properties; bp->interface; bp++) {
436 if (!streq(bp->interface, interface))
437 continue;
438
439 for (p = bp->properties; p->property; p++)
440 if (streq(p->property, property))
441 goto set_prop;
442 }
443
444 /* no match */
445 if (!nulstr_contains(interfaces, interface))
446 dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface");
447 else
448 dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property");
449
450 return bus_send_error_reply(c, message, &error, -EINVAL);
451
452 set_prop:
453 if (!p->set) {
454 dbus_set_error_const(&error, DBUS_ERROR_PROPERTY_READ_ONLY, "Property read-only");
455 return bus_send_error_reply(c, message, &error, -EINVAL);
456 }
457
458 dbus_message_iter_recurse(&iter, &sub);
459
460 sig = dbus_message_iter_get_signature(&sub);
461 if (!sig)
462 goto oom;
463
464 if (!streq(sig, p->signature)) {
465 dbus_free(sig);
466 return bus_send_error_reply(c, message, NULL, -EINVAL);
467 }
468 dbus_free(sig);
469
470 data = (uint8_t*) bp->base + p->offset;
471 if (p->indirect)
472 data = *(void**)data;
473
474 r = p->set(&sub, property, data);
475 if (r == -ENOMEM)
476 goto oom;
477 else if (r < 0)
478 return bus_send_error_reply(c, message, NULL, r);
479
480 reply = dbus_message_new_method_return(message);
481 if (!reply)
482 goto oom;
483
484 /* Send out a signal about this, but it doesn't really
485 * matter if this fails, so eat all errors */
486 changed = bus_properties_changed_one_new(
487 dbus_message_get_path(message),
488 interface,
489 property);
490 if (changed) {
491 dbus_connection_send(c, changed, NULL);
492 dbus_message_unref(changed);
493 }
494
495
496 } else {
497 const char *interface = dbus_message_get_interface(message);
498
499 if (!interface || !nulstr_contains(interfaces, interface)) {
500 dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface");
501 return bus_send_error_reply(c, message, &error, -EINVAL);
502 }
503 }
504
505 if (reply) {
506 if (!dbus_connection_send(c, reply, NULL))
507 goto oom;
508
509 dbus_message_unref(reply);
510 return DBUS_HANDLER_RESULT_HANDLED;
511 }
512
513 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
514
515 oom:
516 if (reply)
517 dbus_message_unref(reply);
518
519 dbus_error_free(&error);
520
521 return DBUS_HANDLER_RESULT_NEED_MEMORY;
522 }
523
524 int bus_property_append_string(DBusMessageIter *i, const char *property, void *data) {
525 const char *t = data;
526
527 assert(i);
528 assert(property);
529
530 if (!t)
531 t = "";
532
533 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
534 return -ENOMEM;
535
536 return 0;
537 }
538
539 int bus_property_append_strv(DBusMessageIter *i, const char *property, void *data) {
540 char **t = data;
541
542 assert(i);
543 assert(property);
544
545 return bus_append_strv_iter(i, t);
546 }
547
548 int bus_property_append_bool(DBusMessageIter *i, const char *property, void *data) {
549 bool *b = data;
550 dbus_bool_t db;
551
552 assert(i);
553 assert(property);
554 assert(b);
555
556 db = *b;
557
558 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db))
559 return -ENOMEM;
560
561 return 0;
562 }
563
564 int bus_property_append_tristate_false(DBusMessageIter *i, const char *property, void *data) {
565 int *b = data;
566 dbus_bool_t db;
567
568 assert(i);
569 assert(property);
570 assert(b);
571
572 db = *b > 0;
573
574 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db))
575 return -ENOMEM;
576
577 return 0;
578 }
579
580 int bus_property_append_uint64(DBusMessageIter *i, const char *property, void *data) {
581 assert(i);
582 assert(property);
583 assert(data);
584
585 /* Let's ensure that usec_t is actually 64bit, and hence this
586 * function can be used for usec_t */
587 assert_cc(sizeof(uint64_t) == sizeof(usec_t));
588
589 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, data))
590 return -ENOMEM;
591
592 return 0;
593 }
594
595 int bus_property_append_uint32(DBusMessageIter *i, const char *property, void *data) {
596 assert(i);
597 assert(property);
598 assert(data);
599
600 /* Let's ensure that pid_t, mode_t, uid_t, gid_t are actually
601 * 32bit, and hence this function can be used for
602 * pid_t/mode_t/uid_t/gid_t */
603 assert_cc(sizeof(uint32_t) == sizeof(pid_t));
604 assert_cc(sizeof(uint32_t) == sizeof(mode_t));
605 assert_cc(sizeof(uint32_t) == sizeof(unsigned));
606 assert_cc(sizeof(uint32_t) == sizeof(uid_t));
607 assert_cc(sizeof(uint32_t) == sizeof(gid_t));
608
609 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, data))
610 return -ENOMEM;
611
612 return 0;
613 }
614
615 int bus_property_append_int32(DBusMessageIter *i, const char *property, void *data) {
616 assert(i);
617 assert(property);
618 assert(data);
619
620 assert_cc(sizeof(int32_t) == sizeof(int));
621
622 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, data))
623 return -ENOMEM;
624
625 return 0;
626 }
627
628 int bus_property_append_size(DBusMessageIter *i, const char *property, void *data) {
629 uint64_t u;
630
631 assert(i);
632 assert(property);
633 assert(data);
634
635 u = (uint64_t) *(size_t*) data;
636
637 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
638 return -ENOMEM;
639
640 return 0;
641 }
642
643 int bus_property_append_ul(DBusMessageIter *i, const char *property, void *data) {
644 uint64_t u;
645
646 assert(i);
647 assert(property);
648 assert(data);
649
650 u = (uint64_t) *(unsigned long*) data;
651
652 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
653 return -ENOMEM;
654
655 return 0;
656 }
657
658 int bus_property_append_long(DBusMessageIter *i, const char *property, void *data) {
659 int64_t l;
660
661 assert(i);
662 assert(property);
663 assert(data);
664
665 l = (int64_t) *(long*) data;
666
667 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT64, &l))
668 return -ENOMEM;
669
670 return 0;
671 }
672
673 int bus_property_set_uint64(DBusMessageIter *i, const char *property, void *data) {
674 uint64_t *t = data;
675
676 assert(i);
677 assert(property);
678
679 dbus_message_iter_get_basic(i, t);
680 return 0;
681 }
682
683 const char *bus_errno_to_dbus(int error) {
684
685 switch(error) {
686
687 case -EINVAL:
688 return DBUS_ERROR_INVALID_ARGS;
689
690 case -ENOMEM:
691 return DBUS_ERROR_NO_MEMORY;
692
693 case -EPERM:
694 case -EACCES:
695 return DBUS_ERROR_ACCESS_DENIED;
696
697 case -ESRCH:
698 return DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN;
699
700 case -ENOENT:
701 return DBUS_ERROR_FILE_NOT_FOUND;
702
703 case -EEXIST:
704 return DBUS_ERROR_FILE_EXISTS;
705
706 case -ETIMEDOUT:
707 case -ETIME:
708 return DBUS_ERROR_TIMEOUT;
709
710 case -EIO:
711 return DBUS_ERROR_IO_ERROR;
712
713 case -ENETRESET:
714 case -ECONNABORTED:
715 case -ECONNRESET:
716 return DBUS_ERROR_DISCONNECTED;
717 }
718
719 return DBUS_ERROR_FAILED;
720 }
721
722 DBusHandlerResult bus_send_error_reply(DBusConnection *c, DBusMessage *message, DBusError *berror, int error) {
723 DBusMessage *reply = NULL;
724 const char *name, *text;
725
726 if (berror && dbus_error_is_set(berror)) {
727 name = berror->name;
728 text = berror->message;
729 } else {
730 name = bus_errno_to_dbus(error);
731 text = strerror(-error);
732 }
733
734 if (!(reply = dbus_message_new_error(message, name, text)))
735 goto oom;
736
737 if (!dbus_connection_send(c, reply, NULL))
738 goto oom;
739
740 dbus_message_unref(reply);
741
742 if (berror)
743 dbus_error_free(berror);
744
745 return DBUS_HANDLER_RESULT_HANDLED;
746
747 oom:
748 if (reply)
749 dbus_message_unref(reply);
750
751 if (berror)
752 dbus_error_free(berror);
753
754 return DBUS_HANDLER_RESULT_NEED_MEMORY;
755 }
756
757 DBusMessage* bus_properties_changed_new(const char *path, const char *interface, const char *properties) {
758 DBusMessage *m;
759 DBusMessageIter iter, sub;
760 const char *i;
761
762 assert(interface);
763 assert(properties);
764
765 m = dbus_message_new_signal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
766 if (!m)
767 goto oom;
768
769 dbus_message_iter_init_append(m, &iter);
770
771 /* We won't send any property values, since they might be
772 * large and sometimes not cheap to generated */
773
774 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface) ||
775 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub) ||
776 !dbus_message_iter_close_container(&iter, &sub) ||
777 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub))
778 goto oom;
779
780 NULSTR_FOREACH(i, properties)
781 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &i))
782 goto oom;
783
784 if (!dbus_message_iter_close_container(&iter, &sub))
785 goto oom;
786
787 return m;
788
789 oom:
790 if (m)
791 dbus_message_unref(m);
792
793 return NULL;
794 }
795
796 DBusMessage* bus_properties_changed_one_new(const char *path, const char *interface, const char *property) {
797 DBusMessage *m;
798 DBusMessageIter iter, sub;
799
800 assert(interface);
801 assert(property);
802
803 m = dbus_message_new_signal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
804 if (!m)
805 goto oom;
806
807 dbus_message_iter_init_append(m, &iter);
808
809 /* We won't send any property values, since they might be
810 * large and sometimes not cheap to generated */
811
812 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface) ||
813 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub) ||
814 !dbus_message_iter_close_container(&iter, &sub) ||
815 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub))
816 goto oom;
817
818 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &property))
819 goto oom;
820
821 if (!dbus_message_iter_close_container(&iter, &sub))
822 goto oom;
823
824 return m;
825
826 oom:
827 if (m)
828 dbus_message_unref(m);
829
830 return NULL;
831 }
832
833 uint32_t bus_flags_to_events(DBusWatch *bus_watch) {
834 unsigned flags;
835 uint32_t events = 0;
836
837 assert(bus_watch);
838
839 /* no watch flags for disabled watches */
840 if (!dbus_watch_get_enabled(bus_watch))
841 return 0;
842
843 flags = dbus_watch_get_flags(bus_watch);
844
845 if (flags & DBUS_WATCH_READABLE)
846 events |= EPOLLIN;
847 if (flags & DBUS_WATCH_WRITABLE)
848 events |= EPOLLOUT;
849
850 return events | EPOLLHUP | EPOLLERR;
851 }
852
853 unsigned bus_events_to_flags(uint32_t events) {
854 unsigned flags = 0;
855
856 if (events & EPOLLIN)
857 flags |= DBUS_WATCH_READABLE;
858 if (events & EPOLLOUT)
859 flags |= DBUS_WATCH_WRITABLE;
860 if (events & EPOLLHUP)
861 flags |= DBUS_WATCH_HANGUP;
862 if (events & EPOLLERR)
863 flags |= DBUS_WATCH_ERROR;
864
865 return flags;
866 }
867
868 int bus_parse_strv(DBusMessage *m, char ***_l) {
869 DBusMessageIter iter;
870
871 assert(m);
872 assert(_l);
873
874 if (!dbus_message_iter_init(m, &iter))
875 return -EINVAL;
876
877 return bus_parse_strv_iter(&iter, _l);
878 }
879
880 int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l) {
881 DBusMessageIter sub;
882 unsigned n = 0, i = 0;
883 char **l;
884
885 assert(iter);
886 assert(_l);
887
888 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
889 dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRING)
890 return -EINVAL;
891
892 dbus_message_iter_recurse(iter, &sub);
893
894 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
895 n++;
896 dbus_message_iter_next(&sub);
897 }
898
899 if (!(l = new(char*, n+1)))
900 return -ENOMEM;
901
902 dbus_message_iter_recurse(iter, &sub);
903
904 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
905 const char *s;
906
907 assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
908 dbus_message_iter_get_basic(&sub, &s);
909
910 if (!(l[i++] = strdup(s))) {
911 strv_free(l);
912 return -ENOMEM;
913 }
914
915 dbus_message_iter_next(&sub);
916 }
917
918 assert(i == n);
919 l[i] = NULL;
920
921 if (_l)
922 *_l = l;
923
924 return 0;
925 }
926
927 int bus_append_strv_iter(DBusMessageIter *iter, char **l) {
928 DBusMessageIter sub;
929
930 assert(iter);
931
932 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &sub))
933 return -ENOMEM;
934
935 STRV_FOREACH(l, l)
936 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, l))
937 return -ENOMEM;
938
939 if (!dbus_message_iter_close_container(iter, &sub))
940 return -ENOMEM;
941
942 return 0;
943 }
944
945 int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) {
946
947 assert(iter);
948 assert(data);
949
950 if (dbus_message_iter_get_arg_type(iter) != type)
951 return -EIO;
952
953 dbus_message_iter_get_basic(iter, data);
954
955 if (!dbus_message_iter_next(iter) != !next)
956 return -EIO;
957
958 return 0;
959 }
960
961 int generic_print_property(const char *name, DBusMessageIter *iter, bool all) {
962 assert(name);
963 assert(iter);
964
965 switch (dbus_message_iter_get_arg_type(iter)) {
966
967 case DBUS_TYPE_STRING: {
968 const char *s;
969 dbus_message_iter_get_basic(iter, &s);
970
971 if (all || !isempty(s))
972 printf("%s=%s\n", name, s);
973
974 return 1;
975 }
976
977 case DBUS_TYPE_BOOLEAN: {
978 dbus_bool_t b;
979
980 dbus_message_iter_get_basic(iter, &b);
981 printf("%s=%s\n", name, yes_no(b));
982
983 return 1;
984 }
985
986 case DBUS_TYPE_UINT64: {
987 uint64_t u;
988 dbus_message_iter_get_basic(iter, &u);
989
990 /* Yes, heuristics! But we can change this check
991 * should it turn out to not be sufficient */
992
993 if (endswith(name, "Timestamp")) {
994 char timestamp[FORMAT_TIMESTAMP_MAX], *t;
995
996 t = format_timestamp(timestamp, sizeof(timestamp), u);
997 if (t || all)
998 printf("%s=%s\n", name, strempty(t));
999
1000 } else if (strstr(name, "USec")) {
1001 char timespan[FORMAT_TIMESPAN_MAX];
1002
1003 printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u));
1004 } else
1005 printf("%s=%llu\n", name, (unsigned long long) u);
1006
1007 return 1;
1008 }
1009
1010 case DBUS_TYPE_UINT32: {
1011 uint32_t u;
1012 dbus_message_iter_get_basic(iter, &u);
1013
1014 if (strstr(name, "UMask") || strstr(name, "Mode"))
1015 printf("%s=%04o\n", name, u);
1016 else
1017 printf("%s=%u\n", name, (unsigned) u);
1018
1019 return 1;
1020 }
1021
1022 case DBUS_TYPE_INT32: {
1023 int32_t i;
1024 dbus_message_iter_get_basic(iter, &i);
1025
1026 printf("%s=%i\n", name, (int) i);
1027 return 1;
1028 }
1029
1030 case DBUS_TYPE_DOUBLE: {
1031 double d;
1032 dbus_message_iter_get_basic(iter, &d);
1033
1034 printf("%s=%g\n", name, d);
1035 return 1;
1036 }
1037
1038 case DBUS_TYPE_ARRAY:
1039
1040 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
1041 DBusMessageIter sub;
1042 bool space = false;
1043
1044 dbus_message_iter_recurse(iter, &sub);
1045 if (all ||
1046 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1047 printf("%s=", name);
1048
1049 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1050 const char *s;
1051
1052 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
1053 dbus_message_iter_get_basic(&sub, &s);
1054 printf("%s%s", space ? " " : "", s);
1055
1056 space = true;
1057 dbus_message_iter_next(&sub);
1058 }
1059
1060 puts("");
1061 }
1062
1063 return 1;
1064
1065 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_BYTE) {
1066 DBusMessageIter sub;
1067
1068 dbus_message_iter_recurse(iter, &sub);
1069 if (all ||
1070 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1071 printf("%s=", name);
1072
1073 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1074 uint8_t u;
1075
1076 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE);
1077 dbus_message_iter_get_basic(&sub, &u);
1078 printf("%02x", u);
1079
1080 dbus_message_iter_next(&sub);
1081 }
1082
1083 puts("");
1084 }
1085
1086 return 1;
1087
1088 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_UINT32) {
1089 DBusMessageIter sub;
1090
1091 dbus_message_iter_recurse(iter, &sub);
1092 if (all ||
1093 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1094 printf("%s=", name);
1095
1096 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1097 uint32_t u;
1098
1099 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32);
1100 dbus_message_iter_get_basic(&sub, &u);
1101 printf("%08x", u);
1102
1103 dbus_message_iter_next(&sub);
1104 }
1105
1106 puts("");
1107 }
1108
1109 return 1;
1110 }
1111
1112 break;
1113 }
1114
1115 return 0;
1116 }
1117
1118 static void release_name_pending_cb(DBusPendingCall *pending, void *userdata) {
1119 DBusMessage *reply;
1120 DBusConnection *bus = userdata;
1121
1122 assert_se(reply = dbus_pending_call_steal_reply(pending));
1123 dbus_message_unref(reply);
1124
1125 dbus_connection_close(bus);
1126 }
1127
1128 void bus_async_unregister_and_exit(DBusConnection *bus, const char *name) {
1129 DBusMessage *m = NULL;
1130 DBusPendingCall *pending = NULL;
1131
1132 assert(bus);
1133
1134 /* We unregister the name here, but we continue to process
1135 * requests, until we get the response for it, so that all
1136 * requests are guaranteed to be processed. */
1137
1138 m = dbus_message_new_method_call(
1139 DBUS_SERVICE_DBUS,
1140 DBUS_PATH_DBUS,
1141 DBUS_INTERFACE_DBUS,
1142 "ReleaseName");
1143 if (!m)
1144 goto oom;
1145
1146 if (!dbus_message_append_args(
1147 m,
1148 DBUS_TYPE_STRING,
1149 &name,
1150 DBUS_TYPE_INVALID))
1151 goto oom;
1152
1153 if (!dbus_connection_send_with_reply(bus, m, &pending, -1))
1154 goto oom;
1155
1156 if (!dbus_pending_call_set_notify(pending, release_name_pending_cb, bus, NULL))
1157 goto oom;
1158
1159 dbus_message_unref(m);
1160 dbus_pending_call_unref(pending);
1161
1162 return;
1163
1164 oom:
1165 log_oom();
1166
1167 if (pending) {
1168 dbus_pending_call_cancel(pending);
1169 dbus_pending_call_unref(pending);
1170 }
1171
1172 if (m)
1173 dbus_message_unref(m);
1174 }
1175
1176 DBusHandlerResult bus_exit_idle_filter(DBusConnection *bus, DBusMessage *m, void *userdata) {
1177 usec_t *remain_until = userdata;
1178
1179 assert(bus);
1180 assert(m);
1181 assert(remain_until);
1182
1183 /* Every time we get a new message we reset out timeout */
1184 *remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
1185
1186 if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected"))
1187 dbus_connection_close(bus);
1188
1189 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1190 }
1191
1192 /* This mimics dbus_bus_get_unix_user() */
1193 pid_t bus_get_unix_process_id(
1194 DBusConnection *connection,
1195 const char *name,
1196 DBusError *error) {
1197
1198 DBusMessage *m = NULL, *reply = NULL;
1199 uint32_t pid = 0;
1200
1201 m = dbus_message_new_method_call(
1202 DBUS_SERVICE_DBUS,
1203 DBUS_PATH_DBUS,
1204 DBUS_INTERFACE_DBUS,
1205 "GetConnectionUnixProcessID");
1206 if (!m) {
1207 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
1208 goto finish;
1209 }
1210
1211 if (!dbus_message_append_args(
1212 m,
1213 DBUS_TYPE_STRING, &name,
1214 DBUS_TYPE_INVALID)) {
1215 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
1216 goto finish;
1217 }
1218
1219 reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
1220 if (!reply)
1221 goto finish;
1222
1223 if (dbus_set_error_from_message(error, reply))
1224 goto finish;
1225
1226 if (!dbus_message_get_args(
1227 reply, error,
1228 DBUS_TYPE_UINT32, &pid,
1229 DBUS_TYPE_INVALID))
1230 goto finish;
1231
1232 finish:
1233 if (m)
1234 dbus_message_unref(m);
1235
1236 if (reply)
1237 dbus_message_unref(reply);
1238
1239 return (pid_t) pid;
1240 }
1241
1242 bool bus_error_is_no_service(const DBusError *error) {
1243 assert(error);
1244
1245 if (!dbus_error_is_set(error))
1246 return false;
1247
1248 if (dbus_error_has_name(error, DBUS_ERROR_NAME_HAS_NO_OWNER))
1249 return true;
1250
1251 if (dbus_error_has_name(error, DBUS_ERROR_SERVICE_UNKNOWN))
1252 return true;
1253
1254 return startswith(error->name, "org.freedesktop.DBus.Error.Spawn.");
1255 }
1256
1257 int bus_method_call_with_reply(
1258 DBusConnection *bus,
1259 const char *destination,
1260 const char *path,
1261 const char *interface,
1262 const char *method,
1263 DBusMessage **return_reply,
1264 DBusError *return_error,
1265 int first_arg_type, ...) {
1266
1267 DBusError error;
1268 DBusMessage *m, *reply;
1269 va_list ap;
1270 int r = 0;
1271
1272 dbus_error_init(&error);
1273 assert(bus);
1274
1275 m = dbus_message_new_method_call(destination, path, interface, method);
1276 if (!m) {
1277 r = log_oom();
1278 goto finish;
1279 }
1280
1281 va_start(ap, first_arg_type);
1282 if (!dbus_message_append_args_valist(m, first_arg_type, ap)) {
1283 va_end(ap);
1284 r = log_oom();
1285 goto finish;
1286 }
1287 va_end(ap);
1288
1289 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1290 if (!reply) {
1291 if (!return_error)
1292 log_error("Failed to issue method call: %s", bus_error_message(&error));
1293
1294 if (bus_error_is_no_service(&error))
1295 r = -ENOENT;
1296 else if (dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED))
1297 r = -EACCES;
1298 else if (dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY))
1299 r = -ETIMEDOUT;
1300 else
1301 r = -EIO;
1302 goto finish;
1303 }
1304
1305 if (return_reply)
1306 *return_reply = reply;
1307 else
1308 dbus_message_unref(reply);
1309
1310 finish:
1311 if (m)
1312 dbus_message_unref(m);
1313
1314 if (return_error)
1315 *return_error = error;
1316 else
1317 dbus_error_free(&error);
1318
1319 return r;
1320 }