chiark / gitweb /
6d000e11622f204fbea272fa17088eafa7561e1a
[elogind.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         assert(error);
249
250         /* Sometimes the D-Bus server is a little bit too verbose with
251          * its error messages, so let's override them here */
252         if (dbus_error_has_name(error, DBUS_ERROR_ACCESS_DENIED))
253                 return "Access denied";
254
255         return error->message;
256 }
257
258 DBusHandlerResult bus_default_message_handler(
259                 DBusConnection *c,
260                 DBusMessage *message,
261                 const char *introspection,
262                 const char *interfaces,
263                 const BusBoundProperties *bound_properties) {
264
265         DBusError error;
266         DBusMessage *reply = NULL;
267         int r;
268
269         assert(c);
270         assert(message);
271
272         dbus_error_init(&error);
273
274         if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") && introspection) {
275
276                 if (!(reply = dbus_message_new_method_return(message)))
277                         goto oom;
278
279                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID))
280                         goto oom;
281
282         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Get") && bound_properties) {
283                 const char *interface, *property;
284                 const BusBoundProperties *bp;
285                 const BusProperty *p;
286                 void *data;
287                 DBusMessageIter iter, sub;
288
289                 if (!dbus_message_get_args(
290                             message,
291                             &error,
292                             DBUS_TYPE_STRING, &interface,
293                             DBUS_TYPE_STRING, &property,
294                             DBUS_TYPE_INVALID))
295                         return bus_send_error_reply(c, message, &error, -EINVAL);
296
297                 for (bp = bound_properties; bp->interface; bp++) {
298                         if (!streq(bp->interface, interface))
299                                 continue;
300
301                         for (p = bp->properties; p->property; p++)
302                                 if (streq(p->property, property))
303                                         goto get_prop;
304                 }
305
306                 /* no match */
307                 if (!nulstr_contains(interfaces, interface))
308                         dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface");
309                 else
310                         dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property");
311
312                 return bus_send_error_reply(c, message, &error, -EINVAL);
313
314 get_prop:
315                 reply = dbus_message_new_method_return(message);
316                 if (!reply)
317                         goto oom;
318
319                 dbus_message_iter_init_append(reply, &iter);
320
321                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, p->signature, &sub))
322                         goto oom;
323
324                 data = (char*)bp->base + p->offset;
325                 if (p->indirect)
326                         data = *(void**)data;
327                 r = p->append(&sub, property, data);
328                 if (r < 0) {
329                         if (r == -ENOMEM)
330                                 goto oom;
331
332                         dbus_message_unref(reply);
333                         return bus_send_error_reply(c, message, NULL, r);
334                 }
335
336                 if (!dbus_message_iter_close_container(&iter, &sub))
337                         goto oom;
338
339         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "GetAll") && bound_properties) {
340                 const char *interface;
341                 const BusBoundProperties *bp;
342                 const BusProperty *p;
343                 DBusMessageIter iter, sub, sub2, sub3;
344
345                 if (!dbus_message_get_args(
346                             message,
347                             &error,
348                             DBUS_TYPE_STRING, &interface,
349                             DBUS_TYPE_INVALID))
350                         return bus_send_error_reply(c, message, &error, -EINVAL);
351
352                 if (interface[0] && !nulstr_contains(interfaces, interface)) {
353                         dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface");
354                         return bus_send_error_reply(c, message, &error, -EINVAL);
355                 }
356
357                 if (!(reply = dbus_message_new_method_return(message)))
358                         goto oom;
359
360                 dbus_message_iter_init_append(reply, &iter);
361
362                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub))
363                         goto oom;
364
365                 for (bp = bound_properties; bp->interface; bp++) {
366                         if (interface[0] && !streq(bp->interface, interface))
367                                 continue;
368
369                         for (p = bp->properties; p->property; p++) {
370                                 void *data;
371
372                                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, NULL, &sub2) ||
373                                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &p->property) ||
374                                     !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, p->signature, &sub3))
375                                         goto oom;
376
377                                 data = (char*)bp->base + p->offset;
378                                 if (p->indirect)
379                                         data = *(void**)data;
380                                 r = p->append(&sub3, p->property, data);
381                                 if (r < 0) {
382                                         if (r == -ENOMEM)
383                                                 goto oom;
384
385                                         dbus_message_unref(reply);
386                                         return bus_send_error_reply(c, message, NULL, r);
387                                 }
388
389                                 if (!dbus_message_iter_close_container(&sub2, &sub3) ||
390                                     !dbus_message_iter_close_container(&sub, &sub2))
391                                         goto oom;
392                         }
393                 }
394
395                 if (!dbus_message_iter_close_container(&iter, &sub))
396                         goto oom;
397
398         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Set") && bound_properties) {
399                 const char *interface, *property;
400                 DBusMessageIter iter;
401                 const BusBoundProperties *bp;
402                 const BusProperty *p;
403                 DBusMessageIter sub;
404                 char *sig;
405                 void *data;
406                 DBusMessage *changed;
407
408                 if (!dbus_message_iter_init(message, &iter) ||
409                     dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
410                         return bus_send_error_reply(c, message, NULL, -EINVAL);
411
412                 dbus_message_iter_get_basic(&iter, &interface);
413
414                 if (!dbus_message_iter_next(&iter) ||
415                     dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
416                         return bus_send_error_reply(c, message, NULL, -EINVAL);
417
418                 dbus_message_iter_get_basic(&iter, &property);
419
420                 if (!dbus_message_iter_next(&iter) ||
421                     dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT ||
422                     dbus_message_iter_has_next(&iter))
423                         return bus_send_error_reply(c, message, NULL, -EINVAL);
424
425                 for (bp = bound_properties; bp->interface; bp++) {
426                         if (!streq(bp->interface, interface))
427                                 continue;
428
429                         for (p = bp->properties; p->property; p++)
430                                 if (streq(p->property, property))
431                                         goto set_prop;
432                 }
433
434                 /* no match */
435                 if (!nulstr_contains(interfaces, interface))
436                         dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface");
437                 else
438                         dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property");
439
440                 return bus_send_error_reply(c, message, &error, -EINVAL);
441
442 set_prop:
443                 if (!p->set) {
444                         dbus_set_error_const(&error, DBUS_ERROR_PROPERTY_READ_ONLY, "Property read-only");
445                         return bus_send_error_reply(c, message, &error, -EINVAL);
446                 }
447
448                 dbus_message_iter_recurse(&iter, &sub);
449
450                 sig = dbus_message_iter_get_signature(&sub);
451                 if (!sig)
452                         goto oom;
453
454                 if (!streq(sig, p->signature)) {
455                         dbus_free(sig);
456                         return bus_send_error_reply(c, message, NULL, -EINVAL);
457                 }
458                 dbus_free(sig);
459
460                 data = (uint8_t*) bp->base + p->offset;
461                 if (p->indirect)
462                         data = *(void**)data;
463
464                 r = p->set(&sub, property, data);
465                 if (r == -ENOMEM)
466                         goto oom;
467                 else if (r < 0)
468                         return bus_send_error_reply(c, message, NULL, r);
469
470                 reply = dbus_message_new_method_return(message);
471                 if (!reply)
472                         goto oom;
473
474                 /* Send out a signal about this, but it doesn't really
475                  * matter if this fails, so eat all errors */
476                 changed = bus_properties_changed_one_new(
477                                 dbus_message_get_path(message),
478                                 interface,
479                                 property);
480                 if (changed) {
481                         dbus_connection_send(c, changed, NULL);
482                         dbus_message_unref(changed);
483                 }
484
485
486         } else {
487                 const char *interface = dbus_message_get_interface(message);
488
489                 if (!interface || !nulstr_contains(interfaces, interface)) {
490                         dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface");
491                         return bus_send_error_reply(c, message, &error, -EINVAL);
492                 }
493         }
494
495         if (reply) {
496                 if (!dbus_connection_send(c, reply, NULL))
497                         goto oom;
498
499                 dbus_message_unref(reply);
500                 return DBUS_HANDLER_RESULT_HANDLED;
501         }
502
503         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
504
505 oom:
506         if (reply)
507                 dbus_message_unref(reply);
508
509         dbus_error_free(&error);
510
511         return DBUS_HANDLER_RESULT_NEED_MEMORY;
512 }
513
514 int bus_property_append_string(DBusMessageIter *i, const char *property, void *data) {
515         const char *t = data;
516
517         assert(i);
518         assert(property);
519
520         if (!t)
521                 t = "";
522
523         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
524                 return -ENOMEM;
525
526         return 0;
527 }
528
529 int bus_property_append_strv(DBusMessageIter *i, const char *property, void *data) {
530         char **t = data;
531
532         assert(i);
533         assert(property);
534
535         return bus_append_strv_iter(i, t);
536 }
537
538 int bus_property_append_bool(DBusMessageIter *i, const char *property, void *data) {
539         bool *b = data;
540         dbus_bool_t db;
541
542         assert(i);
543         assert(property);
544         assert(b);
545
546         db = *b;
547
548         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db))
549                 return -ENOMEM;
550
551         return 0;
552 }
553
554 int bus_property_append_tristate_false(DBusMessageIter *i, const char *property, void *data) {
555         int *b = data;
556         dbus_bool_t db;
557
558         assert(i);
559         assert(property);
560         assert(b);
561
562         db = *b > 0;
563
564         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db))
565                 return -ENOMEM;
566
567         return 0;
568 }
569
570 int bus_property_append_uint64(DBusMessageIter *i, const char *property, void *data) {
571         assert(i);
572         assert(property);
573         assert(data);
574
575         /* Let's ensure that usec_t is actually 64bit, and hence this
576          * function can be used for usec_t */
577         assert_cc(sizeof(uint64_t) == sizeof(usec_t));
578
579         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, data))
580                 return -ENOMEM;
581
582         return 0;
583 }
584
585 int bus_property_append_uint32(DBusMessageIter *i, const char *property, void *data) {
586         assert(i);
587         assert(property);
588         assert(data);
589
590         /* Let's ensure that pid_t, mode_t, uid_t, gid_t are actually
591          * 32bit, and hence this function can be used for
592          * pid_t/mode_t/uid_t/gid_t */
593         assert_cc(sizeof(uint32_t) == sizeof(pid_t));
594         assert_cc(sizeof(uint32_t) == sizeof(mode_t));
595         assert_cc(sizeof(uint32_t) == sizeof(unsigned));
596         assert_cc(sizeof(uint32_t) == sizeof(uid_t));
597         assert_cc(sizeof(uint32_t) == sizeof(gid_t));
598
599         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, data))
600                 return -ENOMEM;
601
602         return 0;
603 }
604
605 int bus_property_append_int32(DBusMessageIter *i, const char *property, void *data) {
606         assert(i);
607         assert(property);
608         assert(data);
609
610         assert_cc(sizeof(int32_t) == sizeof(int));
611
612         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, data))
613                 return -ENOMEM;
614
615         return 0;
616 }
617
618 int bus_property_append_size(DBusMessageIter *i, const char *property, void *data) {
619         uint64_t u;
620
621         assert(i);
622         assert(property);
623         assert(data);
624
625         u = (uint64_t) *(size_t*) data;
626
627         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
628                 return -ENOMEM;
629
630         return 0;
631 }
632
633 int bus_property_append_ul(DBusMessageIter *i, const char *property, void *data) {
634         uint64_t u;
635
636         assert(i);
637         assert(property);
638         assert(data);
639
640         u = (uint64_t) *(unsigned long*) data;
641
642         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
643                 return -ENOMEM;
644
645         return 0;
646 }
647
648 int bus_property_append_long(DBusMessageIter *i, const char *property, void *data) {
649         int64_t l;
650
651         assert(i);
652         assert(property);
653         assert(data);
654
655         l = (int64_t) *(long*) data;
656
657         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT64, &l))
658                 return -ENOMEM;
659
660         return 0;
661 }
662
663 int bus_property_set_uint64(DBusMessageIter *i, const char *property, void *data) {
664         uint64_t *t = data;
665
666         assert(i);
667         assert(property);
668
669         dbus_message_iter_get_basic(i, t);
670         return 0;
671 }
672
673 const char *bus_errno_to_dbus(int error) {
674
675         switch(error) {
676
677         case -EINVAL:
678                 return DBUS_ERROR_INVALID_ARGS;
679
680         case -ENOMEM:
681                 return DBUS_ERROR_NO_MEMORY;
682
683         case -EPERM:
684         case -EACCES:
685                 return DBUS_ERROR_ACCESS_DENIED;
686
687         case -ESRCH:
688                 return DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN;
689
690         case -ENOENT:
691                 return DBUS_ERROR_FILE_NOT_FOUND;
692
693         case -EEXIST:
694                 return DBUS_ERROR_FILE_EXISTS;
695
696         case -ETIMEDOUT:
697         case -ETIME:
698                 return DBUS_ERROR_TIMEOUT;
699
700         case -EIO:
701                 return DBUS_ERROR_IO_ERROR;
702
703         case -ENETRESET:
704         case -ECONNABORTED:
705         case -ECONNRESET:
706                 return DBUS_ERROR_DISCONNECTED;
707         }
708
709         return DBUS_ERROR_FAILED;
710 }
711
712 DBusHandlerResult bus_send_error_reply(DBusConnection *c, DBusMessage *message, DBusError *berror, int error) {
713         DBusMessage *reply = NULL;
714         const char *name, *text;
715
716         if (berror && dbus_error_is_set(berror)) {
717                 name = berror->name;
718                 text = berror->message;
719         } else {
720                 name = bus_errno_to_dbus(error);
721                 text = strerror(-error);
722         }
723
724         if (!(reply = dbus_message_new_error(message, name, text)))
725                 goto oom;
726
727         if (!dbus_connection_send(c, reply, NULL))
728                 goto oom;
729
730         dbus_message_unref(reply);
731
732         if (berror)
733                 dbus_error_free(berror);
734
735         return DBUS_HANDLER_RESULT_HANDLED;
736
737 oom:
738         if (reply)
739                 dbus_message_unref(reply);
740
741         if (berror)
742                 dbus_error_free(berror);
743
744         return DBUS_HANDLER_RESULT_NEED_MEMORY;
745 }
746
747 DBusMessage* bus_properties_changed_new(const char *path, const char *interface, const char *properties) {
748         DBusMessage *m;
749         DBusMessageIter iter, sub;
750         const char *i;
751
752         assert(interface);
753         assert(properties);
754
755         m = dbus_message_new_signal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
756         if (!m)
757                 goto oom;
758
759         dbus_message_iter_init_append(m, &iter);
760
761         /* We won't send any property values, since they might be
762          * large and sometimes not cheap to generated */
763
764         if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface) ||
765             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub) ||
766             !dbus_message_iter_close_container(&iter, &sub) ||
767             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub))
768                 goto oom;
769
770         NULSTR_FOREACH(i, properties)
771                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &i))
772                         goto oom;
773
774         if (!dbus_message_iter_close_container(&iter, &sub))
775                 goto oom;
776
777         return m;
778
779 oom:
780         if (m)
781                 dbus_message_unref(m);
782
783         return NULL;
784 }
785
786 DBusMessage* bus_properties_changed_one_new(const char *path, const char *interface, const char *property) {
787         DBusMessage *m;
788         DBusMessageIter iter, sub;
789
790         assert(interface);
791         assert(property);
792
793         m = dbus_message_new_signal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
794         if (!m)
795                 goto oom;
796
797         dbus_message_iter_init_append(m, &iter);
798
799         /* We won't send any property values, since they might be
800          * large and sometimes not cheap to generated */
801
802         if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface) ||
803             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub) ||
804             !dbus_message_iter_close_container(&iter, &sub) ||
805             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub))
806                 goto oom;
807
808         if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &property))
809                 goto oom;
810
811         if (!dbus_message_iter_close_container(&iter, &sub))
812                 goto oom;
813
814         return m;
815
816 oom:
817         if (m)
818                 dbus_message_unref(m);
819
820         return NULL;
821 }
822
823 uint32_t bus_flags_to_events(DBusWatch *bus_watch) {
824         unsigned flags;
825         uint32_t events = 0;
826
827         assert(bus_watch);
828
829         /* no watch flags for disabled watches */
830         if (!dbus_watch_get_enabled(bus_watch))
831                 return 0;
832
833         flags = dbus_watch_get_flags(bus_watch);
834
835         if (flags & DBUS_WATCH_READABLE)
836                 events |= EPOLLIN;
837         if (flags & DBUS_WATCH_WRITABLE)
838                 events |= EPOLLOUT;
839
840         return events | EPOLLHUP | EPOLLERR;
841 }
842
843 unsigned bus_events_to_flags(uint32_t events) {
844         unsigned flags = 0;
845
846         if (events & EPOLLIN)
847                 flags |= DBUS_WATCH_READABLE;
848         if (events & EPOLLOUT)
849                 flags |= DBUS_WATCH_WRITABLE;
850         if (events & EPOLLHUP)
851                 flags |= DBUS_WATCH_HANGUP;
852         if (events & EPOLLERR)
853                 flags |= DBUS_WATCH_ERROR;
854
855         return flags;
856 }
857
858 int bus_parse_strv(DBusMessage *m, char ***_l) {
859         DBusMessageIter iter;
860
861         assert(m);
862         assert(_l);
863
864         if (!dbus_message_iter_init(m, &iter))
865                 return -EINVAL;
866
867         return bus_parse_strv_iter(&iter, _l);
868 }
869
870 int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l) {
871         DBusMessageIter sub;
872         unsigned n = 0, i = 0;
873         char **l;
874
875         assert(iter);
876         assert(_l);
877
878         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
879             dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRING)
880             return -EINVAL;
881
882         dbus_message_iter_recurse(iter, &sub);
883
884         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
885                 n++;
886                 dbus_message_iter_next(&sub);
887         }
888
889         if (!(l = new(char*, n+1)))
890                 return -ENOMEM;
891
892         dbus_message_iter_recurse(iter, &sub);
893
894         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
895                 const char *s;
896
897                 assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
898                 dbus_message_iter_get_basic(&sub, &s);
899
900                 if (!(l[i++] = strdup(s))) {
901                         strv_free(l);
902                         return -ENOMEM;
903                 }
904
905                 dbus_message_iter_next(&sub);
906         }
907
908         assert(i == n);
909         l[i] = NULL;
910
911         if (_l)
912                 *_l = l;
913
914         return 0;
915 }
916
917 int bus_append_strv_iter(DBusMessageIter *iter, char **l) {
918         DBusMessageIter sub;
919
920         assert(iter);
921
922         if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &sub))
923                 return -ENOMEM;
924
925         STRV_FOREACH(l, l)
926                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, l))
927                         return -ENOMEM;
928
929         if (!dbus_message_iter_close_container(iter, &sub))
930                 return -ENOMEM;
931
932         return 0;
933 }
934
935 int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) {
936
937         assert(iter);
938         assert(data);
939
940         if (dbus_message_iter_get_arg_type(iter) != type)
941                 return -EIO;
942
943         dbus_message_iter_get_basic(iter, data);
944
945         if (!dbus_message_iter_next(iter) != !next)
946                 return -EIO;
947
948         return 0;
949 }
950
951 int generic_print_property(const char *name, DBusMessageIter *iter, bool all) {
952         assert(name);
953         assert(iter);
954
955         switch (dbus_message_iter_get_arg_type(iter)) {
956
957         case DBUS_TYPE_STRING: {
958                 const char *s;
959                 dbus_message_iter_get_basic(iter, &s);
960
961                 if (all || !isempty(s))
962                         printf("%s=%s\n", name, s);
963
964                 return 1;
965         }
966
967         case DBUS_TYPE_BOOLEAN: {
968                 dbus_bool_t b;
969
970                 dbus_message_iter_get_basic(iter, &b);
971                 printf("%s=%s\n", name, yes_no(b));
972
973                 return 1;
974         }
975
976         case DBUS_TYPE_UINT64: {
977                 uint64_t u;
978                 dbus_message_iter_get_basic(iter, &u);
979
980                 /* Yes, heuristics! But we can change this check
981                  * should it turn out to not be sufficient */
982
983                 if (endswith(name, "Timestamp")) {
984                         char timestamp[FORMAT_TIMESTAMP_MAX], *t;
985
986                         t = format_timestamp(timestamp, sizeof(timestamp), u);
987                         if (t || all)
988                                 printf("%s=%s\n", name, strempty(t));
989
990                 } else if (strstr(name, "USec")) {
991                         char timespan[FORMAT_TIMESPAN_MAX];
992
993                         printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u));
994                 } else
995                         printf("%s=%llu\n", name, (unsigned long long) u);
996
997                 return 1;
998         }
999
1000         case DBUS_TYPE_UINT32: {
1001                 uint32_t u;
1002                 dbus_message_iter_get_basic(iter, &u);
1003
1004                 if (strstr(name, "UMask") || strstr(name, "Mode"))
1005                         printf("%s=%04o\n", name, u);
1006                 else
1007                         printf("%s=%u\n", name, (unsigned) u);
1008
1009                 return 1;
1010         }
1011
1012         case DBUS_TYPE_INT32: {
1013                 int32_t i;
1014                 dbus_message_iter_get_basic(iter, &i);
1015
1016                 printf("%s=%i\n", name, (int) i);
1017                 return 1;
1018         }
1019
1020         case DBUS_TYPE_DOUBLE: {
1021                 double d;
1022                 dbus_message_iter_get_basic(iter, &d);
1023
1024                 printf("%s=%g\n", name, d);
1025                 return 1;
1026         }
1027
1028         case DBUS_TYPE_ARRAY:
1029
1030                 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
1031                         DBusMessageIter sub;
1032                         bool space = false;
1033
1034                         dbus_message_iter_recurse(iter, &sub);
1035                         if (all ||
1036                             dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1037                                 printf("%s=", name);
1038
1039                                 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1040                                         const char *s;
1041
1042                                         assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
1043                                         dbus_message_iter_get_basic(&sub, &s);
1044                                         printf("%s%s", space ? " " : "", s);
1045
1046                                         space = true;
1047                                         dbus_message_iter_next(&sub);
1048                                 }
1049
1050                                 puts("");
1051                         }
1052
1053                         return 1;
1054
1055                 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_BYTE) {
1056                         DBusMessageIter sub;
1057
1058                         dbus_message_iter_recurse(iter, &sub);
1059                         if (all ||
1060                             dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1061                                 printf("%s=", name);
1062
1063                                 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1064                                         uint8_t u;
1065
1066                                         assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE);
1067                                         dbus_message_iter_get_basic(&sub, &u);
1068                                         printf("%02x", u);
1069
1070                                         dbus_message_iter_next(&sub);
1071                                 }
1072
1073                                 puts("");
1074                         }
1075
1076                         return 1;
1077                 }
1078
1079                 break;
1080         }
1081
1082         return 0;
1083 }
1084
1085 static void release_name_pending_cb(DBusPendingCall *pending, void *userdata) {
1086         DBusMessage *reply;
1087         DBusConnection *bus = userdata;
1088
1089         assert_se(reply = dbus_pending_call_steal_reply(pending));
1090         dbus_message_unref(reply);
1091
1092         dbus_connection_close(bus);
1093 }
1094
1095 void bus_async_unregister_and_exit(DBusConnection *bus, const char *name) {
1096         DBusMessage *m = NULL;
1097         DBusPendingCall *pending = NULL;
1098
1099         assert(bus);
1100
1101         /* We unregister the name here, but we continue to process
1102          * requests, until we get the response for it, so that all
1103          * requests are guaranteed to be processed. */
1104
1105         m = dbus_message_new_method_call(
1106                         DBUS_SERVICE_DBUS,
1107                         DBUS_PATH_DBUS,
1108                         DBUS_INTERFACE_DBUS,
1109                         "ReleaseName");
1110         if (!m)
1111                 goto oom;
1112
1113         if (!dbus_message_append_args(
1114                             m,
1115                             DBUS_TYPE_STRING,
1116                             &name,
1117                             DBUS_TYPE_INVALID))
1118                 goto oom;
1119
1120         if (!dbus_connection_send_with_reply(bus, m, &pending, -1))
1121                 goto oom;
1122
1123         if (!dbus_pending_call_set_notify(pending, release_name_pending_cb, bus, NULL))
1124                 goto oom;
1125
1126         dbus_message_unref(m);
1127         dbus_pending_call_unref(pending);
1128
1129         return;
1130
1131 oom:
1132         log_error("Out of memory");
1133
1134         if (pending) {
1135                 dbus_pending_call_cancel(pending);
1136                 dbus_pending_call_unref(pending);
1137         }
1138
1139         if (m)
1140                 dbus_message_unref(m);
1141 }
1142
1143 DBusHandlerResult bus_exit_idle_filter(DBusConnection *bus, DBusMessage *m, void *userdata) {
1144         usec_t *remain_until = userdata;
1145
1146         assert(bus);
1147         assert(m);
1148         assert(remain_until);
1149
1150         /* Everytime we get a new message we reset out timeout */
1151         *remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
1152
1153         if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected"))
1154                 dbus_connection_close(bus);
1155
1156         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1157 }