]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/dbus-common.c
0e38933d8abba49a65463ad6b46ffa4c6d186375
[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 r = sync_auth(bus, error);
233 if (r < 0) {
234 dbus_connection_close(bus);
235 dbus_connection_unref(bus);
236 return r;
237 }
238
239 if (!dbus_bus_register(bus, error)) {
240 dbus_connection_close(bus);
241 dbus_connection_unref(bus);
242 return r;
243 }
244
245 *_bus = bus;
246 return 0;
247 }
248
249 const char *bus_error_message(const DBusError *error) {
250 if (!error)
251 return NULL;
252
253 /* Sometimes the D-Bus server is a little bit too verbose with
254 * its error messages, so let's override them here */
255 if (dbus_error_has_name(error, DBUS_ERROR_ACCESS_DENIED))
256 return "Access denied";
257
258 return error->message;
259 }
260
261 const char *bus_error_message_or_strerror(const DBusError *error, int err) {
262
263 if (error && dbus_error_is_set(error))
264 return bus_error_message(error);
265
266 return strerror(err);
267 }
268
269 DBusHandlerResult bus_default_message_handler(
270 DBusConnection *c,
271 DBusMessage *message,
272 const char *introspection,
273 const char *interfaces,
274 const BusBoundProperties *bound_properties) {
275
276 DBusError error;
277 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
278 int r;
279
280 assert(c);
281 assert(message);
282
283 dbus_error_init(&error);
284
285 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") && introspection) {
286
287 reply = dbus_message_new_method_return(message);
288 if (!reply)
289 goto oom;
290
291 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID))
292 goto oom;
293
294 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Get") && bound_properties) {
295 const char *interface, *property;
296 const BusBoundProperties *bp;
297 const BusProperty *p;
298 void *data;
299 DBusMessageIter iter, sub;
300
301 if (!dbus_message_get_args(
302 message,
303 &error,
304 DBUS_TYPE_STRING, &interface,
305 DBUS_TYPE_STRING, &property,
306 DBUS_TYPE_INVALID))
307 return bus_send_error_reply(c, message, &error, -EINVAL);
308
309 for (bp = bound_properties; bp->interface; bp++) {
310 if (!streq(bp->interface, interface))
311 continue;
312
313 for (p = bp->properties; p->property; p++)
314 if (streq(p->property, property))
315 goto get_prop;
316 }
317
318 /* no match */
319 if (!nulstr_contains(interfaces, interface))
320 dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface");
321 else
322 dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property");
323
324 return bus_send_error_reply(c, message, &error, -EINVAL);
325
326 get_prop:
327 reply = dbus_message_new_method_return(message);
328 if (!reply)
329 goto oom;
330
331 dbus_message_iter_init_append(reply, &iter);
332
333 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, p->signature, &sub))
334 goto oom;
335
336 data = (char*)bp->base + p->offset;
337 if (p->indirect)
338 data = *(void**)data;
339
340 r = p->append(&sub, property, data);
341 if (r == -ENOMEM)
342 goto oom;
343 if (r < 0)
344 return bus_send_error_reply(c, message, NULL, r);
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 reply = dbus_message_new_method_return(message);
368 if (!reply)
369 goto oom;
370
371 dbus_message_iter_init_append(reply, &iter);
372
373 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub))
374 goto oom;
375
376 for (bp = bound_properties; bp->interface; bp++) {
377 if (interface[0] && !streq(bp->interface, interface))
378 continue;
379
380 for (p = bp->properties; p->property; p++) {
381 void *data;
382
383 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, NULL, &sub2) ||
384 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &p->property) ||
385 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, p->signature, &sub3))
386 goto oom;
387
388 data = (char*)bp->base + p->offset;
389 if (p->indirect)
390 data = *(void**)data;
391 r = p->append(&sub3, p->property, data);
392 if (r == -ENOMEM)
393 goto oom;
394 if (r < 0)
395 return bus_send_error_reply(c, message, NULL, r);
396
397 if (!dbus_message_iter_close_container(&sub2, &sub3) ||
398 !dbus_message_iter_close_container(&sub, &sub2))
399 goto oom;
400 }
401 }
402
403 if (!dbus_message_iter_close_container(&iter, &sub))
404 goto oom;
405
406 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Set") && bound_properties) {
407 const char *interface, *property;
408 DBusMessageIter iter;
409 const BusBoundProperties *bp;
410 const BusProperty *p;
411 DBusMessageIter sub;
412 char *sig;
413 void *data;
414 DBusMessage *changed;
415
416 if (!dbus_message_iter_init(message, &iter) ||
417 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
418 return bus_send_error_reply(c, message, NULL, -EINVAL);
419
420 dbus_message_iter_get_basic(&iter, &interface);
421
422 if (!dbus_message_iter_next(&iter) ||
423 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
424 return bus_send_error_reply(c, message, NULL, -EINVAL);
425
426 dbus_message_iter_get_basic(&iter, &property);
427
428 if (!dbus_message_iter_next(&iter) ||
429 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT ||
430 dbus_message_iter_has_next(&iter))
431 return bus_send_error_reply(c, message, NULL, -EINVAL);
432
433 for (bp = bound_properties; bp->interface; bp++) {
434 if (!streq(bp->interface, interface))
435 continue;
436
437 for (p = bp->properties; p->property; p++)
438 if (streq(p->property, property))
439 goto set_prop;
440 }
441
442 /* no match */
443 if (!nulstr_contains(interfaces, interface))
444 dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface");
445 else
446 dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property");
447
448 return bus_send_error_reply(c, message, &error, -EINVAL);
449
450 set_prop:
451 if (!p->set) {
452 dbus_set_error_const(&error, DBUS_ERROR_PROPERTY_READ_ONLY, "Property read-only");
453 return bus_send_error_reply(c, message, &error, -EINVAL);
454 }
455
456 dbus_message_iter_recurse(&iter, &sub);
457
458 sig = dbus_message_iter_get_signature(&sub);
459 if (!sig)
460 goto oom;
461
462 if (!streq(sig, p->signature)) {
463 dbus_free(sig);
464 return bus_send_error_reply(c, message, NULL, -EINVAL);
465 }
466 dbus_free(sig);
467
468 data = (uint8_t*) bp->base + p->offset;
469 if (p->indirect)
470 data = *(void**)data;
471
472 r = p->set(&sub, property, data);
473 if (r == -ENOMEM)
474 goto oom;
475 else if (r < 0)
476 return bus_send_error_reply(c, message, NULL, r);
477
478 reply = dbus_message_new_method_return(message);
479 if (!reply)
480 goto oom;
481
482 /* Send out a signal about this, but it doesn't really
483 * matter if this fails, so eat all errors */
484 changed = bus_properties_changed_one_new(
485 dbus_message_get_path(message),
486 interface,
487 property);
488 if (changed) {
489 dbus_connection_send(c, changed, NULL);
490 dbus_message_unref(changed);
491 }
492
493
494 } else {
495 const char *interface = dbus_message_get_interface(message);
496
497 if (!interface || !nulstr_contains(interfaces, interface)) {
498 dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface");
499 return bus_send_error_reply(c, message, &error, -EINVAL);
500 }
501 }
502
503 if (reply) {
504 if (!bus_maybe_send_reply(c, message, reply))
505 goto oom;
506
507 return DBUS_HANDLER_RESULT_HANDLED;
508 }
509
510 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
511
512 oom:
513 dbus_error_free(&error);
514
515 return DBUS_HANDLER_RESULT_NEED_MEMORY;
516 }
517
518 int bus_property_append_string(DBusMessageIter *i, const char *property, void *data) {
519 const char *t = data;
520
521 assert(i);
522 assert(property);
523
524 if (!t)
525 t = "";
526
527 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
528 return -ENOMEM;
529
530 return 0;
531 }
532
533 int bus_property_append_strv(DBusMessageIter *i, const char *property, void *data) {
534 char **t = data;
535
536 assert(i);
537 assert(property);
538
539 return bus_append_strv_iter(i, t);
540 }
541
542 int bus_property_append_bool(DBusMessageIter *i, const char *property, void *data) {
543 bool *b = data;
544 dbus_bool_t db;
545
546 assert(i);
547 assert(property);
548 assert(b);
549
550 db = *b;
551
552 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db))
553 return -ENOMEM;
554
555 return 0;
556 }
557
558 int bus_property_append_tristate_false(DBusMessageIter *i, const char *property, void *data) {
559 int *b = data;
560 dbus_bool_t db;
561
562 assert(i);
563 assert(property);
564 assert(b);
565
566 db = *b > 0;
567
568 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db))
569 return -ENOMEM;
570
571 return 0;
572 }
573
574 int bus_property_append_uint64(DBusMessageIter *i, const char *property, void *data) {
575 assert(i);
576 assert(property);
577 assert(data);
578
579 /* Let's ensure that usec_t is actually 64bit, and hence this
580 * function can be used for usec_t */
581 assert_cc(sizeof(uint64_t) == sizeof(usec_t));
582
583 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, data))
584 return -ENOMEM;
585
586 return 0;
587 }
588
589 int bus_property_append_uint32(DBusMessageIter *i, const char *property, void *data) {
590 assert(i);
591 assert(property);
592 assert(data);
593
594 /* Let's ensure that pid_t, mode_t, uid_t, gid_t are actually
595 * 32bit, and hence this function can be used for
596 * pid_t/mode_t/uid_t/gid_t */
597 assert_cc(sizeof(uint32_t) == sizeof(pid_t));
598 assert_cc(sizeof(uint32_t) == sizeof(mode_t));
599 assert_cc(sizeof(uint32_t) == sizeof(unsigned));
600 assert_cc(sizeof(uint32_t) == sizeof(uid_t));
601 assert_cc(sizeof(uint32_t) == sizeof(gid_t));
602
603 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, data))
604 return -ENOMEM;
605
606 return 0;
607 }
608
609 int bus_property_append_int32(DBusMessageIter *i, const char *property, void *data) {
610 assert(i);
611 assert(property);
612 assert(data);
613
614 assert_cc(sizeof(int32_t) == sizeof(int));
615
616 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, data))
617 return -ENOMEM;
618
619 return 0;
620 }
621
622 int bus_property_append_size(DBusMessageIter *i, const char *property, void *data) {
623 uint64_t u;
624
625 assert(i);
626 assert(property);
627 assert(data);
628
629 u = (uint64_t) *(size_t*) data;
630
631 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
632 return -ENOMEM;
633
634 return 0;
635 }
636
637 int bus_property_append_ul(DBusMessageIter *i, const char *property, void *data) {
638 uint64_t u;
639
640 assert(i);
641 assert(property);
642 assert(data);
643
644 u = (uint64_t) *(unsigned long*) data;
645
646 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
647 return -ENOMEM;
648
649 return 0;
650 }
651
652 int bus_property_append_long(DBusMessageIter *i, const char *property, void *data) {
653 int64_t l;
654
655 assert(i);
656 assert(property);
657 assert(data);
658
659 l = (int64_t) *(long*) data;
660
661 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT64, &l))
662 return -ENOMEM;
663
664 return 0;
665 }
666
667 int bus_property_set_uint64(DBusMessageIter *i, const char *property, void *data) {
668 uint64_t *t = data;
669
670 assert(i);
671 assert(property);
672
673 dbus_message_iter_get_basic(i, t);
674 return 0;
675 }
676
677 const char *bus_errno_to_dbus(int error) {
678
679 switch(error) {
680
681 case -EINVAL:
682 return DBUS_ERROR_INVALID_ARGS;
683
684 case -ENOMEM:
685 return DBUS_ERROR_NO_MEMORY;
686
687 case -EPERM:
688 case -EACCES:
689 return DBUS_ERROR_ACCESS_DENIED;
690
691 case -ESRCH:
692 return DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN;
693
694 case -ENOENT:
695 return DBUS_ERROR_FILE_NOT_FOUND;
696
697 case -EEXIST:
698 return DBUS_ERROR_FILE_EXISTS;
699
700 case -ETIMEDOUT:
701 case -ETIME:
702 return DBUS_ERROR_TIMEOUT;
703
704 case -EIO:
705 return DBUS_ERROR_IO_ERROR;
706
707 case -ENETRESET:
708 case -ECONNABORTED:
709 case -ECONNRESET:
710 return DBUS_ERROR_DISCONNECTED;
711 }
712
713 return DBUS_ERROR_FAILED;
714 }
715
716 dbus_bool_t bus_maybe_send_reply (DBusConnection *c,
717 DBusMessage *message,
718 DBusMessage *reply)
719 {
720 /* Some parts of systemd "reply" to signals, which of course
721 * have the no-reply flag set. We will be defensive here and
722 * still send out a reply if we're passed a signal.
723 */
724 if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL &&
725 dbus_message_get_no_reply(message))
726 return TRUE;
727 return dbus_connection_send(c, reply, NULL);
728 }
729
730 DBusHandlerResult bus_send_error_reply(DBusConnection *c, DBusMessage *message, DBusError *berror, int error) {
731 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
732 const char *name, *text;
733
734 if (berror && dbus_error_is_set(berror)) {
735 name = berror->name;
736 text = berror->message;
737 } else {
738 name = bus_errno_to_dbus(error);
739 text = strerror(-error);
740 }
741
742 reply = dbus_message_new_error(message, name, text);
743 if (!reply)
744 goto oom;
745
746 if (!bus_maybe_send_reply(c, message, reply))
747 goto oom;
748
749 if (berror)
750 dbus_error_free(berror);
751
752 return DBUS_HANDLER_RESULT_HANDLED;
753
754 oom:
755 if (reply)
756 dbus_message_unref(reply);
757
758 if (berror)
759 dbus_error_free(berror);
760
761 return DBUS_HANDLER_RESULT_NEED_MEMORY;
762 }
763
764 DBusMessage* bus_properties_changed_new(const char *path, const char *interface, const char *properties) {
765 DBusMessage *m;
766 DBusMessageIter iter, sub;
767 const char *i;
768
769 assert(interface);
770 assert(properties);
771
772 m = dbus_message_new_signal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
773 if (!m)
774 goto oom;
775
776 dbus_message_iter_init_append(m, &iter);
777
778 /* We won't send any property values, since they might be
779 * large and sometimes not cheap to generated */
780
781 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface) ||
782 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub) ||
783 !dbus_message_iter_close_container(&iter, &sub) ||
784 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub))
785 goto oom;
786
787 NULSTR_FOREACH(i, properties)
788 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &i))
789 goto oom;
790
791 if (!dbus_message_iter_close_container(&iter, &sub))
792 goto oom;
793
794 return m;
795
796 oom:
797 if (m)
798 dbus_message_unref(m);
799
800 return NULL;
801 }
802
803 DBusMessage* bus_properties_changed_one_new(const char *path, const char *interface, const char *property) {
804 DBusMessage *m;
805 DBusMessageIter iter, sub;
806
807 assert(interface);
808 assert(property);
809
810 m = dbus_message_new_signal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
811 if (!m)
812 goto oom;
813
814 dbus_message_iter_init_append(m, &iter);
815
816 /* We won't send any property values, since they might be
817 * large and sometimes not cheap to generated */
818
819 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface) ||
820 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub) ||
821 !dbus_message_iter_close_container(&iter, &sub) ||
822 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub))
823 goto oom;
824
825 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &property))
826 goto oom;
827
828 if (!dbus_message_iter_close_container(&iter, &sub))
829 goto oom;
830
831 return m;
832
833 oom:
834 if (m)
835 dbus_message_unref(m);
836
837 return NULL;
838 }
839
840 uint32_t bus_flags_to_events(DBusWatch *bus_watch) {
841 unsigned flags;
842 uint32_t events = 0;
843
844 assert(bus_watch);
845
846 /* no watch flags for disabled watches */
847 if (!dbus_watch_get_enabled(bus_watch))
848 return 0;
849
850 flags = dbus_watch_get_flags(bus_watch);
851
852 if (flags & DBUS_WATCH_READABLE)
853 events |= EPOLLIN;
854 if (flags & DBUS_WATCH_WRITABLE)
855 events |= EPOLLOUT;
856
857 return events | EPOLLHUP | EPOLLERR;
858 }
859
860 unsigned bus_events_to_flags(uint32_t events) {
861 unsigned flags = 0;
862
863 if (events & EPOLLIN)
864 flags |= DBUS_WATCH_READABLE;
865 if (events & EPOLLOUT)
866 flags |= DBUS_WATCH_WRITABLE;
867 if (events & EPOLLHUP)
868 flags |= DBUS_WATCH_HANGUP;
869 if (events & EPOLLERR)
870 flags |= DBUS_WATCH_ERROR;
871
872 return flags;
873 }
874
875 int bus_parse_strv(DBusMessage *m, char ***_l) {
876 DBusMessageIter iter;
877
878 assert(m);
879 assert(_l);
880
881 if (!dbus_message_iter_init(m, &iter))
882 return -EINVAL;
883
884 return bus_parse_strv_iter(&iter, _l);
885 }
886
887 int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l) {
888 DBusMessageIter sub;
889 unsigned n = 0, i = 0;
890 char **l;
891
892 assert(iter);
893 assert(_l);
894
895 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
896 dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRING)
897 return -EINVAL;
898
899 dbus_message_iter_recurse(iter, &sub);
900
901 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
902 n++;
903 dbus_message_iter_next(&sub);
904 }
905
906 l = new(char*, n+1);
907 if (!l)
908 return -ENOMEM;
909
910 dbus_message_iter_recurse(iter, &sub);
911
912 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
913 const char *s;
914
915 assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
916 dbus_message_iter_get_basic(&sub, &s);
917
918 if (!(l[i++] = strdup(s))) {
919 strv_free(l);
920 return -ENOMEM;
921 }
922
923 dbus_message_iter_next(&sub);
924 }
925
926 assert(i == n);
927 l[i] = NULL;
928
929 if (_l)
930 *_l = l;
931
932 return 0;
933 }
934
935 int bus_parse_strv_pairs_iter(DBusMessageIter *iter, char ***_l) {
936 DBusMessageIter sub, sub2;
937 unsigned n = 0, i = 0;
938 char **l;
939
940 assert(iter);
941 assert(_l);
942
943 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
944 dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRUCT)
945 return -EINVAL;
946
947 dbus_message_iter_recurse(iter, &sub);
948
949 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
950 n++;
951 dbus_message_iter_next(&sub);
952 }
953
954 l = new(char*, n*2+1);
955 if (!l)
956 return -ENOMEM;
957
958 dbus_message_iter_recurse(iter, &sub);
959
960 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
961 const char *a, *b;
962
963 assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT);
964
965 dbus_message_iter_recurse(&sub, &sub2);
966
967 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &a, true) < 0 ||
968 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &b, false) < 0)
969 return -EINVAL;
970
971 l[i] = strdup(a);
972 if (!l[i]) {
973 strv_free(l);
974 return -ENOMEM;
975 }
976
977 l[++i] = strdup(b);
978 if (!l[i]) {
979 strv_free(l);
980 return -ENOMEM;
981 }
982
983 i++;
984 dbus_message_iter_next(&sub);
985 }
986
987 assert(i == n*2);
988 l[i] = NULL;
989
990 if (_l)
991 *_l = l;
992
993 return 0;
994 }
995
996 int bus_parse_unit_info(DBusMessageIter *iter, struct unit_info *u) {
997 DBusMessageIter sub;
998
999 assert(iter);
1000 assert(u);
1001
1002 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRUCT)
1003 return -EINVAL;
1004
1005 dbus_message_iter_recurse(iter, &sub);
1006
1007 if (bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->id, true) < 0 ||
1008 bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->description, true) < 0 ||
1009 bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->load_state, true) < 0 ||
1010 bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->active_state, true) < 0 ||
1011 bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->sub_state, true) < 0 ||
1012 bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->following, true) < 0 ||
1013 bus_iter_get_basic_and_next(&sub, DBUS_TYPE_OBJECT_PATH, &u->unit_path, true) < 0 ||
1014 bus_iter_get_basic_and_next(&sub, DBUS_TYPE_UINT32, &u->job_id, true) < 0 ||
1015 bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->job_type, true) < 0 ||
1016 bus_iter_get_basic_and_next(&sub, DBUS_TYPE_OBJECT_PATH, &u->job_path, false) < 0) {
1017 log_error("Failed to parse reply.");
1018 return -EIO;
1019 }
1020
1021 return 0;
1022 }
1023
1024 int bus_append_strv_iter(DBusMessageIter *iter, char **l) {
1025 DBusMessageIter sub;
1026
1027 assert(iter);
1028
1029 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &sub))
1030 return -ENOMEM;
1031
1032 STRV_FOREACH(l, l)
1033 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, l))
1034 return -ENOMEM;
1035
1036 if (!dbus_message_iter_close_container(iter, &sub))
1037 return -ENOMEM;
1038
1039 return 0;
1040 }
1041
1042 int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) {
1043
1044 assert(iter);
1045 assert(data);
1046
1047 if (dbus_message_iter_get_arg_type(iter) != type)
1048 return -EIO;
1049
1050 dbus_message_iter_get_basic(iter, data);
1051
1052 if (!dbus_message_iter_next(iter) != !next)
1053 return -EIO;
1054
1055 return 0;
1056 }
1057
1058 int generic_print_property(const char *name, DBusMessageIter *iter, bool all) {
1059 assert(name);
1060 assert(iter);
1061
1062 switch (dbus_message_iter_get_arg_type(iter)) {
1063
1064 case DBUS_TYPE_STRING: {
1065 const char *s;
1066 dbus_message_iter_get_basic(iter, &s);
1067
1068 if (all || !isempty(s))
1069 printf("%s=%s\n", name, s);
1070
1071 return 1;
1072 }
1073
1074 case DBUS_TYPE_BOOLEAN: {
1075 dbus_bool_t b;
1076
1077 dbus_message_iter_get_basic(iter, &b);
1078 printf("%s=%s\n", name, yes_no(b));
1079
1080 return 1;
1081 }
1082
1083 case DBUS_TYPE_UINT64: {
1084 uint64_t u;
1085 dbus_message_iter_get_basic(iter, &u);
1086
1087 /* Yes, heuristics! But we can change this check
1088 * should it turn out to not be sufficient */
1089
1090 if (endswith(name, "Timestamp")) {
1091 char timestamp[FORMAT_TIMESTAMP_MAX], *t;
1092
1093 t = format_timestamp(timestamp, sizeof(timestamp), u);
1094 if (t || all)
1095 printf("%s=%s\n", name, strempty(t));
1096
1097 } else if (strstr(name, "USec")) {
1098 char timespan[FORMAT_TIMESPAN_MAX];
1099
1100 printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u));
1101 } else
1102 printf("%s=%llu\n", name, (unsigned long long) u);
1103
1104 return 1;
1105 }
1106
1107 case DBUS_TYPE_UINT32: {
1108 uint32_t u;
1109 dbus_message_iter_get_basic(iter, &u);
1110
1111 if (strstr(name, "UMask") || strstr(name, "Mode"))
1112 printf("%s=%04o\n", name, u);
1113 else
1114 printf("%s=%u\n", name, (unsigned) u);
1115
1116 return 1;
1117 }
1118
1119 case DBUS_TYPE_INT32: {
1120 int32_t i;
1121 dbus_message_iter_get_basic(iter, &i);
1122
1123 printf("%s=%i\n", name, (int) i);
1124 return 1;
1125 }
1126
1127 case DBUS_TYPE_DOUBLE: {
1128 double d;
1129 dbus_message_iter_get_basic(iter, &d);
1130
1131 printf("%s=%g\n", name, d);
1132 return 1;
1133 }
1134
1135 case DBUS_TYPE_ARRAY:
1136
1137 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
1138 DBusMessageIter sub;
1139 bool space = false;
1140
1141 dbus_message_iter_recurse(iter, &sub);
1142 if (all ||
1143 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1144 printf("%s=", name);
1145
1146 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1147 const char *s;
1148
1149 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
1150 dbus_message_iter_get_basic(&sub, &s);
1151 printf("%s%s", space ? " " : "", s);
1152
1153 space = true;
1154 dbus_message_iter_next(&sub);
1155 }
1156
1157 puts("");
1158 }
1159
1160 return 1;
1161
1162 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_BYTE) {
1163 DBusMessageIter sub;
1164
1165 dbus_message_iter_recurse(iter, &sub);
1166 if (all ||
1167 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1168 printf("%s=", name);
1169
1170 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1171 uint8_t u;
1172
1173 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE);
1174 dbus_message_iter_get_basic(&sub, &u);
1175 printf("%02x", u);
1176
1177 dbus_message_iter_next(&sub);
1178 }
1179
1180 puts("");
1181 }
1182
1183 return 1;
1184
1185 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_UINT32) {
1186 DBusMessageIter sub;
1187
1188 dbus_message_iter_recurse(iter, &sub);
1189 if (all ||
1190 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1191 printf("%s=", name);
1192
1193 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1194 uint32_t u;
1195
1196 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32);
1197 dbus_message_iter_get_basic(&sub, &u);
1198 printf("%08x", u);
1199
1200 dbus_message_iter_next(&sub);
1201 }
1202
1203 puts("");
1204 }
1205
1206 return 1;
1207 }
1208
1209 break;
1210 }
1211
1212 return 0;
1213 }
1214
1215 static void release_name_pending_cb(DBusPendingCall *pending, void *userdata) {
1216 DBusMessage *reply;
1217 DBusConnection *bus = userdata;
1218
1219 assert_se(reply = dbus_pending_call_steal_reply(pending));
1220 dbus_message_unref(reply);
1221
1222 dbus_connection_close(bus);
1223 }
1224
1225 void bus_async_unregister_and_exit(DBusConnection *bus, const char *name) {
1226 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
1227 DBusPendingCall *pending = NULL;
1228
1229 assert(bus);
1230
1231 /* We unregister the name here, but we continue to process
1232 * requests, until we get the response for it, so that all
1233 * requests are guaranteed to be processed. */
1234
1235 m = dbus_message_new_method_call(
1236 DBUS_SERVICE_DBUS,
1237 DBUS_PATH_DBUS,
1238 DBUS_INTERFACE_DBUS,
1239 "ReleaseName");
1240 if (!m)
1241 goto oom;
1242
1243 if (!dbus_message_append_args(
1244 m,
1245 DBUS_TYPE_STRING,
1246 &name,
1247 DBUS_TYPE_INVALID))
1248 goto oom;
1249
1250 if (!dbus_connection_send_with_reply(bus, m, &pending, -1))
1251 goto oom;
1252
1253 if (!dbus_pending_call_set_notify(pending, release_name_pending_cb, bus, NULL))
1254 goto oom;
1255
1256 dbus_pending_call_unref(pending);
1257
1258 return;
1259
1260 oom:
1261 log_oom();
1262
1263 if (pending) {
1264 dbus_pending_call_cancel(pending);
1265 dbus_pending_call_unref(pending);
1266 }
1267 }
1268
1269 DBusHandlerResult bus_exit_idle_filter(DBusConnection *bus, DBusMessage *m, void *userdata) {
1270 usec_t *remain_until = userdata;
1271
1272 assert(bus);
1273 assert(m);
1274 assert(remain_until);
1275
1276 /* Every time we get a new message we reset out timeout */
1277 *remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
1278
1279 if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected"))
1280 dbus_connection_close(bus);
1281
1282 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1283 }
1284
1285 /* This mimics dbus_bus_get_unix_user() */
1286 pid_t bus_get_unix_process_id(
1287 DBusConnection *connection,
1288 const char *name,
1289 DBusError *error) {
1290
1291 _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
1292 uint32_t pid = 0;
1293
1294 m = dbus_message_new_method_call(
1295 DBUS_SERVICE_DBUS,
1296 DBUS_PATH_DBUS,
1297 DBUS_INTERFACE_DBUS,
1298 "GetConnectionUnixProcessID");
1299 if (!m) {
1300 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
1301 return 0;
1302 }
1303
1304 if (!dbus_message_append_args(
1305 m,
1306 DBUS_TYPE_STRING, &name,
1307 DBUS_TYPE_INVALID)) {
1308 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
1309 return 0;
1310 }
1311
1312 reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
1313 if (!reply)
1314 return 0;
1315
1316 if (dbus_set_error_from_message(error, reply))
1317 return 0;
1318
1319 if (!dbus_message_get_args(
1320 reply, error,
1321 DBUS_TYPE_UINT32, &pid,
1322 DBUS_TYPE_INVALID))
1323 return 0;
1324
1325 return (pid_t) pid;
1326 }
1327
1328 bool bus_error_is_no_service(const DBusError *error) {
1329 assert(error);
1330
1331 if (!dbus_error_is_set(error))
1332 return false;
1333
1334 if (dbus_error_has_name(error, DBUS_ERROR_NAME_HAS_NO_OWNER))
1335 return true;
1336
1337 if (dbus_error_has_name(error, DBUS_ERROR_SERVICE_UNKNOWN))
1338 return true;
1339
1340 return startswith(error->name, "org.freedesktop.DBus.Error.Spawn.");
1341 }
1342
1343 int bus_method_call_with_reply(
1344 DBusConnection *bus,
1345 const char *destination,
1346 const char *path,
1347 const char *interface,
1348 const char *method,
1349 DBusMessage **return_reply,
1350 DBusError *return_error,
1351 int first_arg_type, ...) {
1352
1353 DBusError error;
1354 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
1355 DBusMessage *reply;
1356 va_list ap;
1357 int r = 0;
1358
1359 dbus_error_init(&error);
1360 assert(bus);
1361
1362 m = dbus_message_new_method_call(destination, path, interface, method);
1363 if (!m) {
1364 r = log_oom();
1365 goto finish;
1366 }
1367
1368 va_start(ap, first_arg_type);
1369 if (!dbus_message_append_args_valist(m, first_arg_type, ap)) {
1370 va_end(ap);
1371 r = log_oom();
1372 goto finish;
1373 }
1374 va_end(ap);
1375
1376 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1377 if (!reply) {
1378 if (!return_error)
1379 log_error("Failed to issue method call: %s", bus_error_message(&error));
1380
1381 if (bus_error_is_no_service(&error))
1382 r = -ENOENT;
1383 else if (dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED))
1384 r = -EACCES;
1385 else if (dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY))
1386 r = -ETIMEDOUT;
1387 else
1388 r = -EIO;
1389 goto finish;
1390 }
1391
1392 if (return_reply)
1393 *return_reply = reply;
1394 else
1395 dbus_message_unref(reply);
1396
1397 finish:
1398 if (return_error)
1399 *return_error = error;
1400 else
1401 dbus_error_free(&error);
1402
1403 return r;
1404 }
1405
1406 void bus_message_unrefp(DBusMessage **reply) {
1407 if (!reply)
1408 return;
1409
1410 if (!*reply)
1411 return;
1412
1413 dbus_message_unref(*reply);
1414 }
1415
1416 const char *bus_message_get_sender_with_fallback(DBusMessage *m) {
1417 const char *s;
1418
1419 assert(m);
1420
1421 s = dbus_message_get_sender(m);
1422 if (s)
1423 return s;
1424
1425 /* When the message came in from a direct connection the
1426 * message will have no sender. We fix that here. */
1427
1428 return ":no-sender";
1429 }