chiark / gitweb /
dbus: introduce parse_unit_info
[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 (!bus_maybe_send_reply(c, message, reply))
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 dbus_bool_t bus_maybe_send_reply (DBusConnection   *c,
717                                   DBusMessage *message,
718                                   DBusMessage *reply)
719 {
720         if (dbus_message_get_no_reply (message))
721                 return TRUE;
722         return dbus_connection_send (c, reply, NULL);
723 }
724
725 DBusHandlerResult bus_send_error_reply(DBusConnection *c, DBusMessage *message, DBusError *berror, int error) {
726         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
727         const char *name, *text;
728
729         if (berror && dbus_error_is_set(berror)) {
730                 name = berror->name;
731                 text = berror->message;
732         } else {
733                 name = bus_errno_to_dbus(error);
734                 text = strerror(-error);
735         }
736
737         reply = dbus_message_new_error(message, name, text);
738         if (!reply)
739                 goto oom;
740
741         if (!bus_maybe_send_reply(c, message, reply))
742                 goto oom;
743
744         if (berror)
745                 dbus_error_free(berror);
746
747         return DBUS_HANDLER_RESULT_HANDLED;
748
749 oom:
750         if (reply)
751                 dbus_message_unref(reply);
752
753         if (berror)
754                 dbus_error_free(berror);
755
756         return DBUS_HANDLER_RESULT_NEED_MEMORY;
757 }
758
759 DBusMessage* bus_properties_changed_new(const char *path, const char *interface, const char *properties) {
760         DBusMessage *m;
761         DBusMessageIter iter, sub;
762         const char *i;
763
764         assert(interface);
765         assert(properties);
766
767         m = dbus_message_new_signal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
768         if (!m)
769                 goto oom;
770
771         dbus_message_iter_init_append(m, &iter);
772
773         /* We won't send any property values, since they might be
774          * large and sometimes not cheap to generated */
775
776         if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface) ||
777             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub) ||
778             !dbus_message_iter_close_container(&iter, &sub) ||
779             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub))
780                 goto oom;
781
782         NULSTR_FOREACH(i, properties)
783                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &i))
784                         goto oom;
785
786         if (!dbus_message_iter_close_container(&iter, &sub))
787                 goto oom;
788
789         return m;
790
791 oom:
792         if (m)
793                 dbus_message_unref(m);
794
795         return NULL;
796 }
797
798 DBusMessage* bus_properties_changed_one_new(const char *path, const char *interface, const char *property) {
799         DBusMessage *m;
800         DBusMessageIter iter, sub;
801
802         assert(interface);
803         assert(property);
804
805         m = dbus_message_new_signal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
806         if (!m)
807                 goto oom;
808
809         dbus_message_iter_init_append(m, &iter);
810
811         /* We won't send any property values, since they might be
812          * large and sometimes not cheap to generated */
813
814         if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface) ||
815             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub) ||
816             !dbus_message_iter_close_container(&iter, &sub) ||
817             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub))
818                 goto oom;
819
820         if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &property))
821                 goto oom;
822
823         if (!dbus_message_iter_close_container(&iter, &sub))
824                 goto oom;
825
826         return m;
827
828 oom:
829         if (m)
830                 dbus_message_unref(m);
831
832         return NULL;
833 }
834
835 uint32_t bus_flags_to_events(DBusWatch *bus_watch) {
836         unsigned flags;
837         uint32_t events = 0;
838
839         assert(bus_watch);
840
841         /* no watch flags for disabled watches */
842         if (!dbus_watch_get_enabled(bus_watch))
843                 return 0;
844
845         flags = dbus_watch_get_flags(bus_watch);
846
847         if (flags & DBUS_WATCH_READABLE)
848                 events |= EPOLLIN;
849         if (flags & DBUS_WATCH_WRITABLE)
850                 events |= EPOLLOUT;
851
852         return events | EPOLLHUP | EPOLLERR;
853 }
854
855 unsigned bus_events_to_flags(uint32_t events) {
856         unsigned flags = 0;
857
858         if (events & EPOLLIN)
859                 flags |= DBUS_WATCH_READABLE;
860         if (events & EPOLLOUT)
861                 flags |= DBUS_WATCH_WRITABLE;
862         if (events & EPOLLHUP)
863                 flags |= DBUS_WATCH_HANGUP;
864         if (events & EPOLLERR)
865                 flags |= DBUS_WATCH_ERROR;
866
867         return flags;
868 }
869
870 int bus_parse_strv(DBusMessage *m, char ***_l) {
871         DBusMessageIter iter;
872
873         assert(m);
874         assert(_l);
875
876         if (!dbus_message_iter_init(m, &iter))
877                 return -EINVAL;
878
879         return bus_parse_strv_iter(&iter, _l);
880 }
881
882 int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l) {
883         DBusMessageIter sub;
884         unsigned n = 0, i = 0;
885         char **l;
886
887         assert(iter);
888         assert(_l);
889
890         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
891             dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRING)
892             return -EINVAL;
893
894         dbus_message_iter_recurse(iter, &sub);
895
896         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
897                 n++;
898                 dbus_message_iter_next(&sub);
899         }
900
901         l = new(char*, n+1);
902         if (!l)
903                 return -ENOMEM;
904
905         dbus_message_iter_recurse(iter, &sub);
906
907         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
908                 const char *s;
909
910                 assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
911                 dbus_message_iter_get_basic(&sub, &s);
912
913                 if (!(l[i++] = strdup(s))) {
914                         strv_free(l);
915                         return -ENOMEM;
916                 }
917
918                 dbus_message_iter_next(&sub);
919         }
920
921         assert(i == n);
922         l[i] = NULL;
923
924         if (_l)
925                 *_l = l;
926
927         return 0;
928 }
929
930 int bus_parse_strv_pairs_iter(DBusMessageIter *iter, char ***_l) {
931         DBusMessageIter sub, sub2;
932         unsigned n = 0, i = 0;
933         char **l;
934
935         assert(iter);
936         assert(_l);
937
938         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
939             dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRUCT)
940             return -EINVAL;
941
942         dbus_message_iter_recurse(iter, &sub);
943
944         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
945                 n++;
946                 dbus_message_iter_next(&sub);
947         }
948
949         l = new(char*, n*2+1);
950         if (!l)
951                 return -ENOMEM;
952
953         dbus_message_iter_recurse(iter, &sub);
954
955         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
956                 const char *a, *b;
957
958                 assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT);
959
960                 dbus_message_iter_recurse(&sub, &sub2);
961
962                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &a, true) < 0 ||
963                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &b, false) < 0)
964                         return -EINVAL;
965
966                 l[i] = strdup(a);
967                 if (!l[i]) {
968                         strv_free(l);
969                         return -ENOMEM;
970                 }
971
972                 l[++i] = strdup(b);
973                 if (!l[i]) {
974                         strv_free(l);
975                         return -ENOMEM;
976                 }
977
978                 i++;
979                 dbus_message_iter_next(&sub);
980         }
981
982         assert(i == n*2);
983         l[i] = NULL;
984
985         if (_l)
986                 *_l = l;
987
988         return 0;
989 }
990
991 int bus_parse_unit_info(DBusMessageIter *iter, struct unit_info *u) {
992         DBusMessageIter sub;
993
994         assert(iter);
995         assert(u);
996
997         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRUCT)
998                 return -EINVAL;
999
1000         dbus_message_iter_recurse(iter, &sub);
1001
1002         if (bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->id, true) < 0 ||
1003             bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->description, true) < 0 ||
1004             bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->load_state, true) < 0 ||
1005             bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->active_state, true) < 0 ||
1006             bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->sub_state, true) < 0 ||
1007             bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->following, true) < 0 ||
1008             bus_iter_get_basic_and_next(&sub, DBUS_TYPE_OBJECT_PATH, &u->unit_path, true) < 0 ||
1009             bus_iter_get_basic_and_next(&sub, DBUS_TYPE_UINT32, &u->job_id, true) < 0 ||
1010             bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &u->job_type, true) < 0 ||
1011             bus_iter_get_basic_and_next(&sub, DBUS_TYPE_OBJECT_PATH, &u->job_path, false) < 0) {
1012                 log_error("Failed to parse reply.");
1013                 return -EIO;
1014         }
1015
1016         return 0;
1017 }
1018
1019 int bus_append_strv_iter(DBusMessageIter *iter, char **l) {
1020         DBusMessageIter sub;
1021
1022         assert(iter);
1023
1024         if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &sub))
1025                 return -ENOMEM;
1026
1027         STRV_FOREACH(l, l)
1028                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, l))
1029                         return -ENOMEM;
1030
1031         if (!dbus_message_iter_close_container(iter, &sub))
1032                 return -ENOMEM;
1033
1034         return 0;
1035 }
1036
1037 int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) {
1038
1039         assert(iter);
1040         assert(data);
1041
1042         if (dbus_message_iter_get_arg_type(iter) != type)
1043                 return -EIO;
1044
1045         dbus_message_iter_get_basic(iter, data);
1046
1047         if (!dbus_message_iter_next(iter) != !next)
1048                 return -EIO;
1049
1050         return 0;
1051 }
1052
1053 int generic_print_property(const char *name, DBusMessageIter *iter, bool all) {
1054         assert(name);
1055         assert(iter);
1056
1057         switch (dbus_message_iter_get_arg_type(iter)) {
1058
1059         case DBUS_TYPE_STRING: {
1060                 const char *s;
1061                 dbus_message_iter_get_basic(iter, &s);
1062
1063                 if (all || !isempty(s))
1064                         printf("%s=%s\n", name, s);
1065
1066                 return 1;
1067         }
1068
1069         case DBUS_TYPE_BOOLEAN: {
1070                 dbus_bool_t b;
1071
1072                 dbus_message_iter_get_basic(iter, &b);
1073                 printf("%s=%s\n", name, yes_no(b));
1074
1075                 return 1;
1076         }
1077
1078         case DBUS_TYPE_UINT64: {
1079                 uint64_t u;
1080                 dbus_message_iter_get_basic(iter, &u);
1081
1082                 /* Yes, heuristics! But we can change this check
1083                  * should it turn out to not be sufficient */
1084
1085                 if (endswith(name, "Timestamp")) {
1086                         char timestamp[FORMAT_TIMESTAMP_MAX], *t;
1087
1088                         t = format_timestamp(timestamp, sizeof(timestamp), u);
1089                         if (t || all)
1090                                 printf("%s=%s\n", name, strempty(t));
1091
1092                 } else if (strstr(name, "USec")) {
1093                         char timespan[FORMAT_TIMESPAN_MAX];
1094
1095                         printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u));
1096                 } else
1097                         printf("%s=%llu\n", name, (unsigned long long) u);
1098
1099                 return 1;
1100         }
1101
1102         case DBUS_TYPE_UINT32: {
1103                 uint32_t u;
1104                 dbus_message_iter_get_basic(iter, &u);
1105
1106                 if (strstr(name, "UMask") || strstr(name, "Mode"))
1107                         printf("%s=%04o\n", name, u);
1108                 else
1109                         printf("%s=%u\n", name, (unsigned) u);
1110
1111                 return 1;
1112         }
1113
1114         case DBUS_TYPE_INT32: {
1115                 int32_t i;
1116                 dbus_message_iter_get_basic(iter, &i);
1117
1118                 printf("%s=%i\n", name, (int) i);
1119                 return 1;
1120         }
1121
1122         case DBUS_TYPE_DOUBLE: {
1123                 double d;
1124                 dbus_message_iter_get_basic(iter, &d);
1125
1126                 printf("%s=%g\n", name, d);
1127                 return 1;
1128         }
1129
1130         case DBUS_TYPE_ARRAY:
1131
1132                 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
1133                         DBusMessageIter sub;
1134                         bool space = false;
1135
1136                         dbus_message_iter_recurse(iter, &sub);
1137                         if (all ||
1138                             dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1139                                 printf("%s=", name);
1140
1141                                 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1142                                         const char *s;
1143
1144                                         assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
1145                                         dbus_message_iter_get_basic(&sub, &s);
1146                                         printf("%s%s", space ? " " : "", s);
1147
1148                                         space = true;
1149                                         dbus_message_iter_next(&sub);
1150                                 }
1151
1152                                 puts("");
1153                         }
1154
1155                         return 1;
1156
1157                 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_BYTE) {
1158                         DBusMessageIter sub;
1159
1160                         dbus_message_iter_recurse(iter, &sub);
1161                         if (all ||
1162                             dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1163                                 printf("%s=", name);
1164
1165                                 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1166                                         uint8_t u;
1167
1168                                         assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE);
1169                                         dbus_message_iter_get_basic(&sub, &u);
1170                                         printf("%02x", u);
1171
1172                                         dbus_message_iter_next(&sub);
1173                                 }
1174
1175                                 puts("");
1176                         }
1177
1178                         return 1;
1179
1180                 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_UINT32) {
1181                         DBusMessageIter sub;
1182
1183                         dbus_message_iter_recurse(iter, &sub);
1184                         if (all ||
1185                             dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1186                                 printf("%s=", name);
1187
1188                                 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1189                                         uint32_t u;
1190
1191                                         assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32);
1192                                         dbus_message_iter_get_basic(&sub, &u);
1193                                         printf("%08x", u);
1194
1195                                         dbus_message_iter_next(&sub);
1196                                 }
1197
1198                                 puts("");
1199                         }
1200
1201                         return 1;
1202                 }
1203
1204                 break;
1205         }
1206
1207         return 0;
1208 }
1209
1210 static void release_name_pending_cb(DBusPendingCall *pending, void *userdata) {
1211         DBusMessage *reply;
1212         DBusConnection *bus = userdata;
1213
1214         assert_se(reply = dbus_pending_call_steal_reply(pending));
1215         dbus_message_unref(reply);
1216
1217         dbus_connection_close(bus);
1218 }
1219
1220 void bus_async_unregister_and_exit(DBusConnection *bus, const char *name) {
1221         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
1222         DBusPendingCall *pending = NULL;
1223
1224         assert(bus);
1225
1226         /* We unregister the name here, but we continue to process
1227          * requests, until we get the response for it, so that all
1228          * requests are guaranteed to be processed. */
1229
1230         m = dbus_message_new_method_call(
1231                         DBUS_SERVICE_DBUS,
1232                         DBUS_PATH_DBUS,
1233                         DBUS_INTERFACE_DBUS,
1234                         "ReleaseName");
1235         if (!m)
1236                 goto oom;
1237
1238         if (!dbus_message_append_args(
1239                             m,
1240                             DBUS_TYPE_STRING,
1241                             &name,
1242                             DBUS_TYPE_INVALID))
1243                 goto oom;
1244
1245         if (!dbus_connection_send_with_reply(bus, m, &pending, -1))
1246                 goto oom;
1247
1248         if (!dbus_pending_call_set_notify(pending, release_name_pending_cb, bus, NULL))
1249                 goto oom;
1250
1251         dbus_pending_call_unref(pending);
1252
1253         return;
1254
1255 oom:
1256         log_oom();
1257
1258         if (pending) {
1259                 dbus_pending_call_cancel(pending);
1260                 dbus_pending_call_unref(pending);
1261         }
1262 }
1263
1264 DBusHandlerResult bus_exit_idle_filter(DBusConnection *bus, DBusMessage *m, void *userdata) {
1265         usec_t *remain_until = userdata;
1266
1267         assert(bus);
1268         assert(m);
1269         assert(remain_until);
1270
1271         /* Every time we get a new message we reset out timeout */
1272         *remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
1273
1274         if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected"))
1275                 dbus_connection_close(bus);
1276
1277         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1278 }
1279
1280 /* This mimics dbus_bus_get_unix_user() */
1281 pid_t bus_get_unix_process_id(
1282                 DBusConnection *connection,
1283                 const char *name,
1284                 DBusError *error) {
1285
1286         _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
1287         uint32_t pid = 0;
1288
1289         m = dbus_message_new_method_call(
1290                         DBUS_SERVICE_DBUS,
1291                         DBUS_PATH_DBUS,
1292                         DBUS_INTERFACE_DBUS,
1293                         "GetConnectionUnixProcessID");
1294         if (!m) {
1295                 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
1296                 return 0;
1297         }
1298
1299         if (!dbus_message_append_args(
1300                             m,
1301                             DBUS_TYPE_STRING, &name,
1302                             DBUS_TYPE_INVALID)) {
1303                 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
1304                 return 0;
1305         }
1306
1307         reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
1308         if (!reply)
1309                 return 0;
1310
1311         if (dbus_set_error_from_message(error, reply))
1312                 return 0;
1313
1314         if (!dbus_message_get_args(
1315                             reply, error,
1316                             DBUS_TYPE_UINT32, &pid,
1317                             DBUS_TYPE_INVALID))
1318                 return 0;
1319
1320         return (pid_t) pid;
1321 }
1322
1323 bool bus_error_is_no_service(const DBusError *error) {
1324         assert(error);
1325
1326         if (!dbus_error_is_set(error))
1327                 return false;
1328
1329         if (dbus_error_has_name(error, DBUS_ERROR_NAME_HAS_NO_OWNER))
1330                 return true;
1331
1332         if (dbus_error_has_name(error, DBUS_ERROR_SERVICE_UNKNOWN))
1333                 return true;
1334
1335         return startswith(error->name, "org.freedesktop.DBus.Error.Spawn.");
1336 }
1337
1338 int bus_method_call_with_reply(
1339                 DBusConnection *bus,
1340                 const char *destination,
1341                 const char *path,
1342                 const char *interface,
1343                 const char *method,
1344                 DBusMessage **return_reply,
1345                 DBusError *return_error,
1346                 int first_arg_type, ...) {
1347
1348         DBusError error;
1349         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
1350         DBusMessage *reply;
1351         va_list ap;
1352         int r = 0;
1353
1354         dbus_error_init(&error);
1355         assert(bus);
1356
1357         m = dbus_message_new_method_call(destination, path, interface, method);
1358         if (!m) {
1359                 r = log_oom();
1360                 goto finish;
1361         }
1362
1363         va_start(ap, first_arg_type);
1364         if (!dbus_message_append_args_valist(m, first_arg_type, ap)) {
1365                 va_end(ap);
1366                 r = log_oom();
1367                 goto finish;
1368         }
1369         va_end(ap);
1370
1371         reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1372         if (!reply) {
1373                 if (!return_error)
1374                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1375
1376                 if (bus_error_is_no_service(&error))
1377                         r = -ENOENT;
1378                 else if (dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED))
1379                         r = -EACCES;
1380                 else if (dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY))
1381                         r = -ETIMEDOUT;
1382                 else
1383                         r = -EIO;
1384                 goto finish;
1385         }
1386
1387         if (return_reply)
1388                 *return_reply = reply;
1389         else
1390                 dbus_message_unref(reply);
1391
1392 finish:
1393         if (return_error)
1394                 *return_error = error;
1395         else
1396                 dbus_error_free(&error);
1397
1398         return r;
1399 }
1400
1401 void bus_message_unrefp(DBusMessage **reply) {
1402         if (!reply)
1403                 return;
1404
1405         if (!*reply)
1406                 return;
1407
1408         dbus_message_unref(*reply);
1409 }
1410
1411 const char *bus_message_get_sender_with_fallback(DBusMessage *m) {
1412         const char *s;
1413
1414         assert(m);
1415
1416         s = dbus_message_get_sender(m);
1417         if (s)
1418                 return s;
1419
1420         /* When the message came in from a direct connection the
1421          * message will have no sender. We fix that here. */
1422
1423         return ":no-sender";
1424 }