chiark / gitweb /
fix a couple of things found with the llvm static analyzer
[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
407                 if (!dbus_message_iter_init(message, &iter) ||
408                     dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
409                         return bus_send_error_reply(c, message, NULL, -EINVAL);
410
411                 dbus_message_iter_get_basic(&iter, &interface);
412
413                 if (!dbus_message_iter_next(&iter) ||
414                     dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
415                         return bus_send_error_reply(c, message, NULL, -EINVAL);
416
417                 dbus_message_iter_get_basic(&iter, &property);
418
419                 if (!dbus_message_iter_next(&iter) ||
420                     dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT ||
421                     dbus_message_iter_has_next(&iter))
422                         return bus_send_error_reply(c, message, NULL, -EINVAL);
423
424                 for (bp = bound_properties; bp->interface; bp++) {
425                         if (!streq(bp->interface, interface))
426                                 continue;
427
428                         for (p = bp->properties; p->property; p++)
429                                 if (streq(p->property, property))
430                                         goto set_prop;
431                 }
432
433                 /* no match */
434                 if (!nulstr_contains(interfaces, interface))
435                         dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface");
436                 else
437                         dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property");
438
439                 return bus_send_error_reply(c, message, &error, -EINVAL);
440
441 set_prop:
442                 if (!p->set) {
443                         dbus_set_error_const(&error, DBUS_ERROR_PROPERTY_READ_ONLY, "Property read-only");
444                         return bus_send_error_reply(c, message, &error, -EINVAL);
445                 }
446
447                 dbus_message_iter_recurse(&iter, &sub);
448
449                 sig = dbus_message_iter_get_signature(&sub);
450                 if (!sig)
451                         goto oom;
452
453                 if (!streq(sig, p->signature)) {
454                         dbus_free(sig);
455                         return bus_send_error_reply(c, message, NULL, -EINVAL);
456                 }
457
458                 dbus_free(sig);
459
460                 data = (char*)bp->base + p->offset;
461                 if (p->indirect)
462                         data = *(void**)data;
463                 r = p->set(&sub, property, data);
464                 if (r < 0) {
465                         if (r == -ENOMEM)
466                                 goto oom;
467                         return bus_send_error_reply(c, message, NULL, r);
468                 }
469
470                 reply = dbus_message_new_method_return(message);
471                 if (!reply)
472                         goto oom;
473         } else {
474                 const char *interface = dbus_message_get_interface(message);
475
476                 if (!interface || !nulstr_contains(interfaces, interface)) {
477                         dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface");
478                         return bus_send_error_reply(c, message, &error, -EINVAL);
479                 }
480         }
481
482         if (reply) {
483                 if (!dbus_connection_send(c, reply, NULL))
484                         goto oom;
485
486                 dbus_message_unref(reply);
487                 return DBUS_HANDLER_RESULT_HANDLED;
488         }
489
490         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
491
492 oom:
493         if (reply)
494                 dbus_message_unref(reply);
495
496         dbus_error_free(&error);
497
498         return DBUS_HANDLER_RESULT_NEED_MEMORY;
499 }
500
501 int bus_property_append_string(DBusMessageIter *i, const char *property, void *data) {
502         const char *t = data;
503
504         assert(i);
505         assert(property);
506
507         if (!t)
508                 t = "";
509
510         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
511                 return -ENOMEM;
512
513         return 0;
514 }
515
516 int bus_property_append_strv(DBusMessageIter *i, const char *property, void *data) {
517         char **t = data;
518
519         assert(i);
520         assert(property);
521
522         return bus_append_strv_iter(i, t);
523 }
524
525 int bus_property_append_bool(DBusMessageIter *i, const char *property, void *data) {
526         bool *b = data;
527         dbus_bool_t db;
528
529         assert(i);
530         assert(property);
531         assert(b);
532
533         db = *b;
534
535         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db))
536                 return -ENOMEM;
537
538         return 0;
539 }
540
541 int bus_property_append_tristate_false(DBusMessageIter *i, const char *property, void *data) {
542         int *b = data;
543         dbus_bool_t db;
544
545         assert(i);
546         assert(property);
547         assert(b);
548
549         db = *b > 0;
550
551         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db))
552                 return -ENOMEM;
553
554         return 0;
555 }
556
557 int bus_property_append_uint64(DBusMessageIter *i, const char *property, void *data) {
558         assert(i);
559         assert(property);
560         assert(data);
561
562         /* Let's ensure that usec_t is actually 64bit, and hence this
563          * function can be used for usec_t */
564         assert_cc(sizeof(uint64_t) == sizeof(usec_t));
565
566         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, data))
567                 return -ENOMEM;
568
569         return 0;
570 }
571
572 int bus_property_append_uint32(DBusMessageIter *i, const char *property, void *data) {
573         assert(i);
574         assert(property);
575         assert(data);
576
577         /* Let's ensure that pid_t, mode_t, uid_t, gid_t are actually
578          * 32bit, and hence this function can be used for
579          * pid_t/mode_t/uid_t/gid_t */
580         assert_cc(sizeof(uint32_t) == sizeof(pid_t));
581         assert_cc(sizeof(uint32_t) == sizeof(mode_t));
582         assert_cc(sizeof(uint32_t) == sizeof(unsigned));
583         assert_cc(sizeof(uint32_t) == sizeof(uid_t));
584         assert_cc(sizeof(uint32_t) == sizeof(gid_t));
585
586         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, data))
587                 return -ENOMEM;
588
589         return 0;
590 }
591
592 int bus_property_append_int32(DBusMessageIter *i, const char *property, void *data) {
593         assert(i);
594         assert(property);
595         assert(data);
596
597         assert_cc(sizeof(int32_t) == sizeof(int));
598
599         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, data))
600                 return -ENOMEM;
601
602         return 0;
603 }
604
605 int bus_property_append_size(DBusMessageIter *i, const char *property, void *data) {
606         uint64_t u;
607
608         assert(i);
609         assert(property);
610         assert(data);
611
612         u = (uint64_t) *(size_t*) data;
613
614         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
615                 return -ENOMEM;
616
617         return 0;
618 }
619
620 int bus_property_append_ul(DBusMessageIter *i, const char *property, void *data) {
621         uint64_t u;
622
623         assert(i);
624         assert(property);
625         assert(data);
626
627         u = (uint64_t) *(unsigned long*) data;
628
629         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
630                 return -ENOMEM;
631
632         return 0;
633 }
634
635 int bus_property_append_long(DBusMessageIter *i, const char *property, void *data) {
636         int64_t l;
637
638         assert(i);
639         assert(property);
640         assert(data);
641
642         l = (int64_t) *(long*) data;
643
644         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT64, &l))
645                 return -ENOMEM;
646
647         return 0;
648 }
649
650 const char *bus_errno_to_dbus(int error) {
651
652         switch(error) {
653
654         case -EINVAL:
655                 return DBUS_ERROR_INVALID_ARGS;
656
657         case -ENOMEM:
658                 return DBUS_ERROR_NO_MEMORY;
659
660         case -EPERM:
661         case -EACCES:
662                 return DBUS_ERROR_ACCESS_DENIED;
663
664         case -ESRCH:
665                 return DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN;
666
667         case -ENOENT:
668                 return DBUS_ERROR_FILE_NOT_FOUND;
669
670         case -EEXIST:
671                 return DBUS_ERROR_FILE_EXISTS;
672
673         case -ETIMEDOUT:
674         case -ETIME:
675                 return DBUS_ERROR_TIMEOUT;
676
677         case -EIO:
678                 return DBUS_ERROR_IO_ERROR;
679
680         case -ENETRESET:
681         case -ECONNABORTED:
682         case -ECONNRESET:
683                 return DBUS_ERROR_DISCONNECTED;
684         }
685
686         return DBUS_ERROR_FAILED;
687 }
688
689 DBusHandlerResult bus_send_error_reply(DBusConnection *c, DBusMessage *message, DBusError *berror, int error) {
690         DBusMessage *reply = NULL;
691         const char *name, *text;
692
693         if (berror && dbus_error_is_set(berror)) {
694                 name = berror->name;
695                 text = berror->message;
696         } else {
697                 name = bus_errno_to_dbus(error);
698                 text = strerror(-error);
699         }
700
701         if (!(reply = dbus_message_new_error(message, name, text)))
702                 goto oom;
703
704         if (!dbus_connection_send(c, reply, NULL))
705                 goto oom;
706
707         dbus_message_unref(reply);
708
709         if (berror)
710                 dbus_error_free(berror);
711
712         return DBUS_HANDLER_RESULT_HANDLED;
713
714 oom:
715         if (reply)
716                 dbus_message_unref(reply);
717
718         if (berror)
719                 dbus_error_free(berror);
720
721         return DBUS_HANDLER_RESULT_NEED_MEMORY;
722 }
723
724 DBusMessage* bus_properties_changed_new(const char *path, const char *interface, const char *properties) {
725         DBusMessage *m;
726         DBusMessageIter iter, sub;
727         const char *i;
728
729         assert(interface);
730         assert(properties);
731
732         if (!(m = dbus_message_new_signal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged")))
733                 goto oom;
734
735         dbus_message_iter_init_append(m, &iter);
736
737         /* We won't send any property values, since they might be
738          * large and sometimes not cheap to generated */
739
740         if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface) ||
741             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub) ||
742             !dbus_message_iter_close_container(&iter, &sub) ||
743             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub))
744                 goto oom;
745
746         NULSTR_FOREACH(i, properties)
747                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &i))
748                         goto oom;
749
750         if (!dbus_message_iter_close_container(&iter, &sub))
751                 goto oom;
752
753         return m;
754
755 oom:
756         if (m)
757                 dbus_message_unref(m);
758
759         return NULL;
760 }
761
762 uint32_t bus_flags_to_events(DBusWatch *bus_watch) {
763         unsigned flags;
764         uint32_t events = 0;
765
766         assert(bus_watch);
767
768         /* no watch flags for disabled watches */
769         if (!dbus_watch_get_enabled(bus_watch))
770                 return 0;
771
772         flags = dbus_watch_get_flags(bus_watch);
773
774         if (flags & DBUS_WATCH_READABLE)
775                 events |= EPOLLIN;
776         if (flags & DBUS_WATCH_WRITABLE)
777                 events |= EPOLLOUT;
778
779         return events | EPOLLHUP | EPOLLERR;
780 }
781
782 unsigned bus_events_to_flags(uint32_t events) {
783         unsigned flags = 0;
784
785         if (events & EPOLLIN)
786                 flags |= DBUS_WATCH_READABLE;
787         if (events & EPOLLOUT)
788                 flags |= DBUS_WATCH_WRITABLE;
789         if (events & EPOLLHUP)
790                 flags |= DBUS_WATCH_HANGUP;
791         if (events & EPOLLERR)
792                 flags |= DBUS_WATCH_ERROR;
793
794         return flags;
795 }
796
797 int bus_parse_strv(DBusMessage *m, char ***_l) {
798         DBusMessageIter iter;
799
800         assert(m);
801         assert(_l);
802
803         if (!dbus_message_iter_init(m, &iter))
804                 return -EINVAL;
805
806         return bus_parse_strv_iter(&iter, _l);
807 }
808
809 int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l) {
810         DBusMessageIter sub;
811         unsigned n = 0, i = 0;
812         char **l;
813
814         assert(iter);
815         assert(_l);
816
817         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
818             dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRING)
819             return -EINVAL;
820
821         dbus_message_iter_recurse(iter, &sub);
822
823         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
824                 n++;
825                 dbus_message_iter_next(&sub);
826         }
827
828         if (!(l = new(char*, n+1)))
829                 return -ENOMEM;
830
831         dbus_message_iter_recurse(iter, &sub);
832
833         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
834                 const char *s;
835
836                 assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
837                 dbus_message_iter_get_basic(&sub, &s);
838
839                 if (!(l[i++] = strdup(s))) {
840                         strv_free(l);
841                         return -ENOMEM;
842                 }
843
844                 dbus_message_iter_next(&sub);
845         }
846
847         assert(i == n);
848         l[i] = NULL;
849
850         if (_l)
851                 *_l = l;
852
853         return 0;
854 }
855
856 int bus_append_strv_iter(DBusMessageIter *iter, char **l) {
857         DBusMessageIter sub;
858
859         assert(iter);
860
861         if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &sub))
862                 return -ENOMEM;
863
864         STRV_FOREACH(l, l)
865                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, l))
866                         return -ENOMEM;
867
868         if (!dbus_message_iter_close_container(iter, &sub))
869                 return -ENOMEM;
870
871         return 0;
872 }
873
874 int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) {
875
876         assert(iter);
877         assert(data);
878
879         if (dbus_message_iter_get_arg_type(iter) != type)
880                 return -EIO;
881
882         dbus_message_iter_get_basic(iter, data);
883
884         if (!dbus_message_iter_next(iter) != !next)
885                 return -EIO;
886
887         return 0;
888 }
889
890 int generic_print_property(const char *name, DBusMessageIter *iter, bool all) {
891         assert(name);
892         assert(iter);
893
894         switch (dbus_message_iter_get_arg_type(iter)) {
895
896         case DBUS_TYPE_STRING: {
897                 const char *s;
898                 dbus_message_iter_get_basic(iter, &s);
899
900                 if (all || !isempty(s))
901                         printf("%s=%s\n", name, s);
902
903                 return 1;
904         }
905
906         case DBUS_TYPE_BOOLEAN: {
907                 dbus_bool_t b;
908
909                 dbus_message_iter_get_basic(iter, &b);
910                 printf("%s=%s\n", name, yes_no(b));
911
912                 return 1;
913         }
914
915         case DBUS_TYPE_UINT64: {
916                 uint64_t u;
917                 dbus_message_iter_get_basic(iter, &u);
918
919                 /* Yes, heuristics! But we can change this check
920                  * should it turn out to not be sufficient */
921
922                 if (endswith(name, "Timestamp")) {
923                         char timestamp[FORMAT_TIMESTAMP_MAX], *t;
924
925                         t = format_timestamp(timestamp, sizeof(timestamp), u);
926                         if (t || all)
927                                 printf("%s=%s\n", name, strempty(t));
928
929                 } else if (strstr(name, "USec")) {
930                         char timespan[FORMAT_TIMESPAN_MAX];
931
932                         printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u));
933                 } else
934                         printf("%s=%llu\n", name, (unsigned long long) u);
935
936                 return 1;
937         }
938
939         case DBUS_TYPE_UINT32: {
940                 uint32_t u;
941                 dbus_message_iter_get_basic(iter, &u);
942
943                 if (strstr(name, "UMask") || strstr(name, "Mode"))
944                         printf("%s=%04o\n", name, u);
945                 else
946                         printf("%s=%u\n", name, (unsigned) u);
947
948                 return 1;
949         }
950
951         case DBUS_TYPE_INT32: {
952                 int32_t i;
953                 dbus_message_iter_get_basic(iter, &i);
954
955                 printf("%s=%i\n", name, (int) i);
956                 return 1;
957         }
958
959         case DBUS_TYPE_DOUBLE: {
960                 double d;
961                 dbus_message_iter_get_basic(iter, &d);
962
963                 printf("%s=%g\n", name, d);
964                 return 1;
965         }
966
967         case DBUS_TYPE_ARRAY:
968
969                 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
970                         DBusMessageIter sub;
971                         bool space = false;
972
973                         dbus_message_iter_recurse(iter, &sub);
974                         if (all ||
975                             dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
976                                 printf("%s=", name);
977
978                                 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
979                                         const char *s;
980
981                                         assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
982                                         dbus_message_iter_get_basic(&sub, &s);
983                                         printf("%s%s", space ? " " : "", s);
984
985                                         space = true;
986                                         dbus_message_iter_next(&sub);
987                                 }
988
989                                 puts("");
990                         }
991
992                         return 1;
993
994                 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_BYTE) {
995                         DBusMessageIter sub;
996
997                         dbus_message_iter_recurse(iter, &sub);
998                         if (all ||
999                             dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1000                                 printf("%s=", name);
1001
1002                                 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1003                                         uint8_t u;
1004
1005                                         assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE);
1006                                         dbus_message_iter_get_basic(&sub, &u);
1007                                         printf("%02x", u);
1008
1009                                         dbus_message_iter_next(&sub);
1010                                 }
1011
1012                                 puts("");
1013                         }
1014
1015                         return 1;
1016                 }
1017
1018                 break;
1019         }
1020
1021         return 0;
1022 }
1023
1024 static void release_name_pending_cb(DBusPendingCall *pending, void *userdata) {
1025         DBusMessage *reply;
1026         DBusConnection *bus = userdata;
1027
1028         assert_se(reply = dbus_pending_call_steal_reply(pending));
1029         dbus_message_unref(reply);
1030
1031         dbus_connection_close(bus);
1032 }
1033
1034 void bus_async_unregister_and_exit(DBusConnection *bus, const char *name) {
1035         DBusMessage *m = NULL;
1036         DBusPendingCall *pending = NULL;
1037
1038         assert(bus);
1039
1040         /* We unregister the name here, but we continue to process
1041          * requests, until we get the response for it, so that all
1042          * requests are guaranteed to be processed. */
1043
1044         m = dbus_message_new_method_call(
1045                         DBUS_SERVICE_DBUS,
1046                         DBUS_PATH_DBUS,
1047                         DBUS_INTERFACE_DBUS,
1048                         "ReleaseName");
1049         if (!m)
1050                 goto oom;
1051
1052         if (!dbus_message_append_args(
1053                             m,
1054                             DBUS_TYPE_STRING,
1055                             &name,
1056                             DBUS_TYPE_INVALID))
1057                 goto oom;
1058
1059         if (!dbus_connection_send_with_reply(bus, m, &pending, -1))
1060                 goto oom;
1061
1062         if (!dbus_pending_call_set_notify(pending, release_name_pending_cb, bus, NULL))
1063                 goto oom;
1064
1065         dbus_message_unref(m);
1066         dbus_pending_call_unref(pending);
1067
1068         return;
1069
1070 oom:
1071         log_error("Out of memory");
1072
1073         if (pending) {
1074                 dbus_pending_call_cancel(pending);
1075                 dbus_pending_call_unref(pending);
1076         }
1077
1078         if (m)
1079                 dbus_message_unref(m);
1080 }
1081
1082 DBusHandlerResult bus_exit_idle_filter(DBusConnection *bus, DBusMessage *m, void *userdata) {
1083         usec_t *remain_until = userdata;
1084
1085         assert(bus);
1086         assert(m);
1087         assert(remain_until);
1088
1089         /* Everytime we get a new message we reset out timeout */
1090         *remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
1091
1092         if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected"))
1093                 dbus_connection_close(bus);
1094
1095         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1096 }