chiark / gitweb /
cgroup: additional validity checks for cgroup attribute names
[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_append_strv_iter(DBusMessageIter *iter, char **l) {
931         DBusMessageIter sub;
932
933         assert(iter);
934
935         if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &sub))
936                 return -ENOMEM;
937
938         STRV_FOREACH(l, l)
939                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, l))
940                         return -ENOMEM;
941
942         if (!dbus_message_iter_close_container(iter, &sub))
943                 return -ENOMEM;
944
945         return 0;
946 }
947
948 int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) {
949
950         assert(iter);
951         assert(data);
952
953         if (dbus_message_iter_get_arg_type(iter) != type)
954                 return -EIO;
955
956         dbus_message_iter_get_basic(iter, data);
957
958         if (!dbus_message_iter_next(iter) != !next)
959                 return -EIO;
960
961         return 0;
962 }
963
964 int generic_print_property(const char *name, DBusMessageIter *iter, bool all) {
965         assert(name);
966         assert(iter);
967
968         switch (dbus_message_iter_get_arg_type(iter)) {
969
970         case DBUS_TYPE_STRING: {
971                 const char *s;
972                 dbus_message_iter_get_basic(iter, &s);
973
974                 if (all || !isempty(s))
975                         printf("%s=%s\n", name, s);
976
977                 return 1;
978         }
979
980         case DBUS_TYPE_BOOLEAN: {
981                 dbus_bool_t b;
982
983                 dbus_message_iter_get_basic(iter, &b);
984                 printf("%s=%s\n", name, yes_no(b));
985
986                 return 1;
987         }
988
989         case DBUS_TYPE_UINT64: {
990                 uint64_t u;
991                 dbus_message_iter_get_basic(iter, &u);
992
993                 /* Yes, heuristics! But we can change this check
994                  * should it turn out to not be sufficient */
995
996                 if (endswith(name, "Timestamp")) {
997                         char timestamp[FORMAT_TIMESTAMP_MAX], *t;
998
999                         t = format_timestamp(timestamp, sizeof(timestamp), u);
1000                         if (t || all)
1001                                 printf("%s=%s\n", name, strempty(t));
1002
1003                 } else if (strstr(name, "USec")) {
1004                         char timespan[FORMAT_TIMESPAN_MAX];
1005
1006                         printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u));
1007                 } else
1008                         printf("%s=%llu\n", name, (unsigned long long) u);
1009
1010                 return 1;
1011         }
1012
1013         case DBUS_TYPE_UINT32: {
1014                 uint32_t u;
1015                 dbus_message_iter_get_basic(iter, &u);
1016
1017                 if (strstr(name, "UMask") || strstr(name, "Mode"))
1018                         printf("%s=%04o\n", name, u);
1019                 else
1020                         printf("%s=%u\n", name, (unsigned) u);
1021
1022                 return 1;
1023         }
1024
1025         case DBUS_TYPE_INT32: {
1026                 int32_t i;
1027                 dbus_message_iter_get_basic(iter, &i);
1028
1029                 printf("%s=%i\n", name, (int) i);
1030                 return 1;
1031         }
1032
1033         case DBUS_TYPE_DOUBLE: {
1034                 double d;
1035                 dbus_message_iter_get_basic(iter, &d);
1036
1037                 printf("%s=%g\n", name, d);
1038                 return 1;
1039         }
1040
1041         case DBUS_TYPE_ARRAY:
1042
1043                 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
1044                         DBusMessageIter sub;
1045                         bool space = false;
1046
1047                         dbus_message_iter_recurse(iter, &sub);
1048                         if (all ||
1049                             dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1050                                 printf("%s=", name);
1051
1052                                 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1053                                         const char *s;
1054
1055                                         assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
1056                                         dbus_message_iter_get_basic(&sub, &s);
1057                                         printf("%s%s", space ? " " : "", s);
1058
1059                                         space = true;
1060                                         dbus_message_iter_next(&sub);
1061                                 }
1062
1063                                 puts("");
1064                         }
1065
1066                         return 1;
1067
1068                 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_BYTE) {
1069                         DBusMessageIter sub;
1070
1071                         dbus_message_iter_recurse(iter, &sub);
1072                         if (all ||
1073                             dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1074                                 printf("%s=", name);
1075
1076                                 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1077                                         uint8_t u;
1078
1079                                         assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE);
1080                                         dbus_message_iter_get_basic(&sub, &u);
1081                                         printf("%02x", u);
1082
1083                                         dbus_message_iter_next(&sub);
1084                                 }
1085
1086                                 puts("");
1087                         }
1088
1089                         return 1;
1090
1091                 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_UINT32) {
1092                         DBusMessageIter sub;
1093
1094                         dbus_message_iter_recurse(iter, &sub);
1095                         if (all ||
1096                             dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1097                                 printf("%s=", name);
1098
1099                                 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1100                                         uint32_t u;
1101
1102                                         assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32);
1103                                         dbus_message_iter_get_basic(&sub, &u);
1104                                         printf("%08x", u);
1105
1106                                         dbus_message_iter_next(&sub);
1107                                 }
1108
1109                                 puts("");
1110                         }
1111
1112                         return 1;
1113                 }
1114
1115                 break;
1116         }
1117
1118         return 0;
1119 }
1120
1121 static void release_name_pending_cb(DBusPendingCall *pending, void *userdata) {
1122         DBusMessage *reply;
1123         DBusConnection *bus = userdata;
1124
1125         assert_se(reply = dbus_pending_call_steal_reply(pending));
1126         dbus_message_unref(reply);
1127
1128         dbus_connection_close(bus);
1129 }
1130
1131 void bus_async_unregister_and_exit(DBusConnection *bus, const char *name) {
1132         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
1133         DBusPendingCall *pending = NULL;
1134
1135         assert(bus);
1136
1137         /* We unregister the name here, but we continue to process
1138          * requests, until we get the response for it, so that all
1139          * requests are guaranteed to be processed. */
1140
1141         m = dbus_message_new_method_call(
1142                         DBUS_SERVICE_DBUS,
1143                         DBUS_PATH_DBUS,
1144                         DBUS_INTERFACE_DBUS,
1145                         "ReleaseName");
1146         if (!m)
1147                 goto oom;
1148
1149         if (!dbus_message_append_args(
1150                             m,
1151                             DBUS_TYPE_STRING,
1152                             &name,
1153                             DBUS_TYPE_INVALID))
1154                 goto oom;
1155
1156         if (!dbus_connection_send_with_reply(bus, m, &pending, -1))
1157                 goto oom;
1158
1159         if (!dbus_pending_call_set_notify(pending, release_name_pending_cb, bus, NULL))
1160                 goto oom;
1161
1162         dbus_pending_call_unref(pending);
1163
1164         return;
1165
1166 oom:
1167         log_oom();
1168
1169         if (pending) {
1170                 dbus_pending_call_cancel(pending);
1171                 dbus_pending_call_unref(pending);
1172         }
1173 }
1174
1175 DBusHandlerResult bus_exit_idle_filter(DBusConnection *bus, DBusMessage *m, void *userdata) {
1176         usec_t *remain_until = userdata;
1177
1178         assert(bus);
1179         assert(m);
1180         assert(remain_until);
1181
1182         /* Every time we get a new message we reset out timeout */
1183         *remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
1184
1185         if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected"))
1186                 dbus_connection_close(bus);
1187
1188         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1189 }
1190
1191 /* This mimics dbus_bus_get_unix_user() */
1192 pid_t bus_get_unix_process_id(
1193                 DBusConnection *connection,
1194                 const char *name,
1195                 DBusError *error) {
1196
1197         _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
1198         uint32_t pid = 0;
1199
1200         m = dbus_message_new_method_call(
1201                         DBUS_SERVICE_DBUS,
1202                         DBUS_PATH_DBUS,
1203                         DBUS_INTERFACE_DBUS,
1204                         "GetConnectionUnixProcessID");
1205         if (!m) {
1206                 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
1207                 return 0;
1208         }
1209
1210         if (!dbus_message_append_args(
1211                             m,
1212                             DBUS_TYPE_STRING, &name,
1213                             DBUS_TYPE_INVALID)) {
1214                 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
1215                 return 0;
1216         }
1217
1218         reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
1219         if (!reply)
1220                 return 0;
1221
1222         if (dbus_set_error_from_message(error, reply))
1223                 return 0;
1224
1225         if (!dbus_message_get_args(
1226                             reply, error,
1227                             DBUS_TYPE_UINT32, &pid,
1228                             DBUS_TYPE_INVALID))
1229                 return 0;
1230
1231         return (pid_t) pid;
1232 }
1233
1234 bool bus_error_is_no_service(const DBusError *error) {
1235         assert(error);
1236
1237         if (!dbus_error_is_set(error))
1238                 return false;
1239
1240         if (dbus_error_has_name(error, DBUS_ERROR_NAME_HAS_NO_OWNER))
1241                 return true;
1242
1243         if (dbus_error_has_name(error, DBUS_ERROR_SERVICE_UNKNOWN))
1244                 return true;
1245
1246         return startswith(error->name, "org.freedesktop.DBus.Error.Spawn.");
1247 }
1248
1249 int bus_method_call_with_reply(
1250                 DBusConnection *bus,
1251                 const char *destination,
1252                 const char *path,
1253                 const char *interface,
1254                 const char *method,
1255                 DBusMessage **return_reply,
1256                 DBusError *return_error,
1257                 int first_arg_type, ...) {
1258
1259         DBusError error;
1260         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
1261         DBusMessage *reply;
1262         va_list ap;
1263         int r = 0;
1264
1265         dbus_error_init(&error);
1266         assert(bus);
1267
1268         m = dbus_message_new_method_call(destination, path, interface, method);
1269         if (!m) {
1270                 r = log_oom();
1271                 goto finish;
1272         }
1273
1274         va_start(ap, first_arg_type);
1275         if (!dbus_message_append_args_valist(m, first_arg_type, ap)) {
1276                 va_end(ap);
1277                 r = log_oom();
1278                 goto finish;
1279         }
1280         va_end(ap);
1281
1282         reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1283         if (!reply) {
1284                 if (!return_error)
1285                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1286
1287                 if (bus_error_is_no_service(&error))
1288                         r = -ENOENT;
1289                 else if (dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED))
1290                         r = -EACCES;
1291                 else if (dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY))
1292                         r = -ETIMEDOUT;
1293                 else
1294                         r = -EIO;
1295                 goto finish;
1296         }
1297
1298         if (return_reply)
1299                 *return_reply = reply;
1300         else
1301                 dbus_message_unref(reply);
1302
1303 finish:
1304         if (return_error)
1305                 *return_error = error;
1306         else
1307                 dbus_error_free(&error);
1308
1309         return r;
1310 }
1311
1312 void bus_message_unrefp(DBusMessage **reply) {
1313         if (!reply)
1314                 return;
1315
1316         if (!*reply)
1317                 return;
1318
1319         dbus_message_unref(*reply);
1320 }
1321
1322 const char *bus_message_get_sender_with_fallback(DBusMessage *m) {
1323         const char *s;
1324
1325         assert(m);
1326
1327         s = dbus_message_get_sender(m);
1328         if (s)
1329                 return s;
1330
1331         /* When the message came in from a direct connection the
1332          * message will have no sender. We fix that here. */
1333
1334         return ":no-sender";
1335 }