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