chiark / gitweb /
dbus: automatically send out changed events for properties written to
[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 const char *bus_errno_to_dbus(int error) {
664
665         switch(error) {
666
667         case -EINVAL:
668                 return DBUS_ERROR_INVALID_ARGS;
669
670         case -ENOMEM:
671                 return DBUS_ERROR_NO_MEMORY;
672
673         case -EPERM:
674         case -EACCES:
675                 return DBUS_ERROR_ACCESS_DENIED;
676
677         case -ESRCH:
678                 return DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN;
679
680         case -ENOENT:
681                 return DBUS_ERROR_FILE_NOT_FOUND;
682
683         case -EEXIST:
684                 return DBUS_ERROR_FILE_EXISTS;
685
686         case -ETIMEDOUT:
687         case -ETIME:
688                 return DBUS_ERROR_TIMEOUT;
689
690         case -EIO:
691                 return DBUS_ERROR_IO_ERROR;
692
693         case -ENETRESET:
694         case -ECONNABORTED:
695         case -ECONNRESET:
696                 return DBUS_ERROR_DISCONNECTED;
697         }
698
699         return DBUS_ERROR_FAILED;
700 }
701
702 DBusHandlerResult bus_send_error_reply(DBusConnection *c, DBusMessage *message, DBusError *berror, int error) {
703         DBusMessage *reply = NULL;
704         const char *name, *text;
705
706         if (berror && dbus_error_is_set(berror)) {
707                 name = berror->name;
708                 text = berror->message;
709         } else {
710                 name = bus_errno_to_dbus(error);
711                 text = strerror(-error);
712         }
713
714         if (!(reply = dbus_message_new_error(message, name, text)))
715                 goto oom;
716
717         if (!dbus_connection_send(c, reply, NULL))
718                 goto oom;
719
720         dbus_message_unref(reply);
721
722         if (berror)
723                 dbus_error_free(berror);
724
725         return DBUS_HANDLER_RESULT_HANDLED;
726
727 oom:
728         if (reply)
729                 dbus_message_unref(reply);
730
731         if (berror)
732                 dbus_error_free(berror);
733
734         return DBUS_HANDLER_RESULT_NEED_MEMORY;
735 }
736
737 DBusMessage* bus_properties_changed_new(const char *path, const char *interface, const char *properties) {
738         DBusMessage *m;
739         DBusMessageIter iter, sub;
740         const char *i;
741
742         assert(interface);
743         assert(properties);
744
745         m = dbus_message_new_signal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
746         if (!m)
747                 goto oom;
748
749         dbus_message_iter_init_append(m, &iter);
750
751         /* We won't send any property values, since they might be
752          * large and sometimes not cheap to generated */
753
754         if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface) ||
755             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub) ||
756             !dbus_message_iter_close_container(&iter, &sub) ||
757             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub))
758                 goto oom;
759
760         NULSTR_FOREACH(i, properties)
761                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &i))
762                         goto oom;
763
764         if (!dbus_message_iter_close_container(&iter, &sub))
765                 goto oom;
766
767         return m;
768
769 oom:
770         if (m)
771                 dbus_message_unref(m);
772
773         return NULL;
774 }
775
776 DBusMessage* bus_properties_changed_one_new(const char *path, const char *interface, const char *property) {
777         DBusMessage *m;
778         DBusMessageIter iter, sub;
779
780         assert(interface);
781         assert(property);
782
783         m = dbus_message_new_signal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
784         if (!m)
785                 goto oom;
786
787         dbus_message_iter_init_append(m, &iter);
788
789         /* We won't send any property values, since they might be
790          * large and sometimes not cheap to generated */
791
792         if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface) ||
793             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub) ||
794             !dbus_message_iter_close_container(&iter, &sub) ||
795             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub))
796                 goto oom;
797
798         if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &property))
799                 goto oom;
800
801         if (!dbus_message_iter_close_container(&iter, &sub))
802                 goto oom;
803
804         return m;
805
806 oom:
807         if (m)
808                 dbus_message_unref(m);
809
810         return NULL;
811 }
812
813 uint32_t bus_flags_to_events(DBusWatch *bus_watch) {
814         unsigned flags;
815         uint32_t events = 0;
816
817         assert(bus_watch);
818
819         /* no watch flags for disabled watches */
820         if (!dbus_watch_get_enabled(bus_watch))
821                 return 0;
822
823         flags = dbus_watch_get_flags(bus_watch);
824
825         if (flags & DBUS_WATCH_READABLE)
826                 events |= EPOLLIN;
827         if (flags & DBUS_WATCH_WRITABLE)
828                 events |= EPOLLOUT;
829
830         return events | EPOLLHUP | EPOLLERR;
831 }
832
833 unsigned bus_events_to_flags(uint32_t events) {
834         unsigned flags = 0;
835
836         if (events & EPOLLIN)
837                 flags |= DBUS_WATCH_READABLE;
838         if (events & EPOLLOUT)
839                 flags |= DBUS_WATCH_WRITABLE;
840         if (events & EPOLLHUP)
841                 flags |= DBUS_WATCH_HANGUP;
842         if (events & EPOLLERR)
843                 flags |= DBUS_WATCH_ERROR;
844
845         return flags;
846 }
847
848 int bus_parse_strv(DBusMessage *m, char ***_l) {
849         DBusMessageIter iter;
850
851         assert(m);
852         assert(_l);
853
854         if (!dbus_message_iter_init(m, &iter))
855                 return -EINVAL;
856
857         return bus_parse_strv_iter(&iter, _l);
858 }
859
860 int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l) {
861         DBusMessageIter sub;
862         unsigned n = 0, i = 0;
863         char **l;
864
865         assert(iter);
866         assert(_l);
867
868         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
869             dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRING)
870             return -EINVAL;
871
872         dbus_message_iter_recurse(iter, &sub);
873
874         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
875                 n++;
876                 dbus_message_iter_next(&sub);
877         }
878
879         if (!(l = new(char*, n+1)))
880                 return -ENOMEM;
881
882         dbus_message_iter_recurse(iter, &sub);
883
884         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
885                 const char *s;
886
887                 assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
888                 dbus_message_iter_get_basic(&sub, &s);
889
890                 if (!(l[i++] = strdup(s))) {
891                         strv_free(l);
892                         return -ENOMEM;
893                 }
894
895                 dbus_message_iter_next(&sub);
896         }
897
898         assert(i == n);
899         l[i] = NULL;
900
901         if (_l)
902                 *_l = l;
903
904         return 0;
905 }
906
907 int bus_append_strv_iter(DBusMessageIter *iter, char **l) {
908         DBusMessageIter sub;
909
910         assert(iter);
911
912         if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &sub))
913                 return -ENOMEM;
914
915         STRV_FOREACH(l, l)
916                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, l))
917                         return -ENOMEM;
918
919         if (!dbus_message_iter_close_container(iter, &sub))
920                 return -ENOMEM;
921
922         return 0;
923 }
924
925 int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) {
926
927         assert(iter);
928         assert(data);
929
930         if (dbus_message_iter_get_arg_type(iter) != type)
931                 return -EIO;
932
933         dbus_message_iter_get_basic(iter, data);
934
935         if (!dbus_message_iter_next(iter) != !next)
936                 return -EIO;
937
938         return 0;
939 }
940
941 int generic_print_property(const char *name, DBusMessageIter *iter, bool all) {
942         assert(name);
943         assert(iter);
944
945         switch (dbus_message_iter_get_arg_type(iter)) {
946
947         case DBUS_TYPE_STRING: {
948                 const char *s;
949                 dbus_message_iter_get_basic(iter, &s);
950
951                 if (all || !isempty(s))
952                         printf("%s=%s\n", name, s);
953
954                 return 1;
955         }
956
957         case DBUS_TYPE_BOOLEAN: {
958                 dbus_bool_t b;
959
960                 dbus_message_iter_get_basic(iter, &b);
961                 printf("%s=%s\n", name, yes_no(b));
962
963                 return 1;
964         }
965
966         case DBUS_TYPE_UINT64: {
967                 uint64_t u;
968                 dbus_message_iter_get_basic(iter, &u);
969
970                 /* Yes, heuristics! But we can change this check
971                  * should it turn out to not be sufficient */
972
973                 if (endswith(name, "Timestamp")) {
974                         char timestamp[FORMAT_TIMESTAMP_MAX], *t;
975
976                         t = format_timestamp(timestamp, sizeof(timestamp), u);
977                         if (t || all)
978                                 printf("%s=%s\n", name, strempty(t));
979
980                 } else if (strstr(name, "USec")) {
981                         char timespan[FORMAT_TIMESPAN_MAX];
982
983                         printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u));
984                 } else
985                         printf("%s=%llu\n", name, (unsigned long long) u);
986
987                 return 1;
988         }
989
990         case DBUS_TYPE_UINT32: {
991                 uint32_t u;
992                 dbus_message_iter_get_basic(iter, &u);
993
994                 if (strstr(name, "UMask") || strstr(name, "Mode"))
995                         printf("%s=%04o\n", name, u);
996                 else
997                         printf("%s=%u\n", name, (unsigned) u);
998
999                 return 1;
1000         }
1001
1002         case DBUS_TYPE_INT32: {
1003                 int32_t i;
1004                 dbus_message_iter_get_basic(iter, &i);
1005
1006                 printf("%s=%i\n", name, (int) i);
1007                 return 1;
1008         }
1009
1010         case DBUS_TYPE_DOUBLE: {
1011                 double d;
1012                 dbus_message_iter_get_basic(iter, &d);
1013
1014                 printf("%s=%g\n", name, d);
1015                 return 1;
1016         }
1017
1018         case DBUS_TYPE_ARRAY:
1019
1020                 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
1021                         DBusMessageIter sub;
1022                         bool space = false;
1023
1024                         dbus_message_iter_recurse(iter, &sub);
1025                         if (all ||
1026                             dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1027                                 printf("%s=", name);
1028
1029                                 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1030                                         const char *s;
1031
1032                                         assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
1033                                         dbus_message_iter_get_basic(&sub, &s);
1034                                         printf("%s%s", space ? " " : "", s);
1035
1036                                         space = true;
1037                                         dbus_message_iter_next(&sub);
1038                                 }
1039
1040                                 puts("");
1041                         }
1042
1043                         return 1;
1044
1045                 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_BYTE) {
1046                         DBusMessageIter sub;
1047
1048                         dbus_message_iter_recurse(iter, &sub);
1049                         if (all ||
1050                             dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1051                                 printf("%s=", name);
1052
1053                                 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1054                                         uint8_t u;
1055
1056                                         assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE);
1057                                         dbus_message_iter_get_basic(&sub, &u);
1058                                         printf("%02x", u);
1059
1060                                         dbus_message_iter_next(&sub);
1061                                 }
1062
1063                                 puts("");
1064                         }
1065
1066                         return 1;
1067                 }
1068
1069                 break;
1070         }
1071
1072         return 0;
1073 }
1074
1075 static void release_name_pending_cb(DBusPendingCall *pending, void *userdata) {
1076         DBusMessage *reply;
1077         DBusConnection *bus = userdata;
1078
1079         assert_se(reply = dbus_pending_call_steal_reply(pending));
1080         dbus_message_unref(reply);
1081
1082         dbus_connection_close(bus);
1083 }
1084
1085 void bus_async_unregister_and_exit(DBusConnection *bus, const char *name) {
1086         DBusMessage *m = NULL;
1087         DBusPendingCall *pending = NULL;
1088
1089         assert(bus);
1090
1091         /* We unregister the name here, but we continue to process
1092          * requests, until we get the response for it, so that all
1093          * requests are guaranteed to be processed. */
1094
1095         m = dbus_message_new_method_call(
1096                         DBUS_SERVICE_DBUS,
1097                         DBUS_PATH_DBUS,
1098                         DBUS_INTERFACE_DBUS,
1099                         "ReleaseName");
1100         if (!m)
1101                 goto oom;
1102
1103         if (!dbus_message_append_args(
1104                             m,
1105                             DBUS_TYPE_STRING,
1106                             &name,
1107                             DBUS_TYPE_INVALID))
1108                 goto oom;
1109
1110         if (!dbus_connection_send_with_reply(bus, m, &pending, -1))
1111                 goto oom;
1112
1113         if (!dbus_pending_call_set_notify(pending, release_name_pending_cb, bus, NULL))
1114                 goto oom;
1115
1116         dbus_message_unref(m);
1117         dbus_pending_call_unref(pending);
1118
1119         return;
1120
1121 oom:
1122         log_error("Out of memory");
1123
1124         if (pending) {
1125                 dbus_pending_call_cancel(pending);
1126                 dbus_pending_call_unref(pending);
1127         }
1128
1129         if (m)
1130                 dbus_message_unref(m);
1131 }
1132
1133 DBusHandlerResult bus_exit_idle_filter(DBusConnection *bus, DBusMessage *m, void *userdata) {
1134         usec_t *remain_until = userdata;
1135
1136         assert(bus);
1137         assert(m);
1138         assert(remain_until);
1139
1140         /* Everytime we get a new message we reset out timeout */
1141         *remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
1142
1143         if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected"))
1144                 dbus_connection_close(bus);
1145
1146         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1147 }