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