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