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