chiark / gitweb /
bus: fix parsing of AcquireName() response
[elogind.git] / src / libsystemd-bus / bus-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 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 <sys/socket.h>
23 #include <sys/capability.h>
24
25 #include "util.h"
26 #include "strv.h"
27 #include "macro.h"
28 #include "def.h"
29 #include "missing.h"
30
31 #include "sd-event.h"
32 #include "sd-bus.h"
33 #include "bus-error.h"
34 #include "bus-message.h"
35 #include "bus-util.h"
36 #include "bus-internal.h"
37
38 static int quit_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
39         sd_event *e = userdata;
40
41         assert(bus);
42         assert(m);
43         assert(e);
44
45         sd_event_request_quit(e);
46         return 1;
47 }
48
49 int bus_async_unregister_and_quit(sd_event *e, sd_bus *bus, const char *name) {
50         _cleanup_free_ char *match = NULL;
51         int r;
52
53         assert(e);
54         assert(bus);
55         assert(name);
56
57         r = asprintf(&match, "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameLost',arg0='%s'", name);
58         if (r < 0)
59                 return r;
60
61         r = sd_bus_add_match(bus, match, quit_callback, e);
62         if (r < 0)
63                 return r;
64
65         r = sd_bus_release_name(bus, name);
66         if (r < 0)
67                 return r;
68
69         if (r != BUS_NAME_RELEASED)
70                 return -EIO;
71
72         return 0;
73 }
74
75 int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t timeout) {
76         bool exiting = false;
77         int r;
78
79         assert(e);
80         assert(bus);
81         assert(name);
82
83         for (;;) {
84                 r = sd_event_get_state(e);
85                 if (r < 0)
86                         return r;
87
88                 if (r == SD_EVENT_FINISHED)
89                         break;
90
91                 r = sd_event_run(e, exiting ? (uint64_t) -1 : timeout);
92                 if (r < 0)
93                         return r;
94
95                 if (r == 0 && !exiting) {
96                         r = bus_async_unregister_and_quit(e, bus, name);
97                         if (r < 0)
98                                 return r;
99
100                         exiting = true;
101                 }
102         }
103
104         return 0;
105 }
106
107 int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) {
108         _cleanup_bus_message_unref_ sd_bus_message *rep = NULL;
109         int r, has_owner = 0;
110
111         assert(c);
112         assert(name);
113
114         r = sd_bus_call_method(c,
115                                "org.freedesktop.DBus",
116                                "/org/freedesktop/dbus",
117                                "org.freedesktop.DBus",
118                                "NameHasOwner",
119                                error,
120                                &rep,
121                                "s",
122                                name);
123         if (r < 0)
124                 return r;
125
126         r = sd_bus_message_read_basic(rep, 'b', &has_owner);
127         if (r < 0)
128                 return sd_bus_error_set_errno(error, r);
129
130         return has_owner;
131 }
132
133 int bus_verify_polkit(
134                 sd_bus *bus,
135                 sd_bus_message *m,
136                 const char *action,
137                 bool interactive,
138                 bool *_challenge,
139                 sd_bus_error *e) {
140
141         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
142         uid_t uid;
143         int r;
144
145         assert(bus);
146         assert(m);
147         assert(action);
148
149         r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID, &creds);
150         if (r < 0)
151                 return r;
152
153         r = sd_bus_creds_get_uid(creds, &uid);
154         if (r < 0)
155                 return r;
156
157         if (uid == 0)
158                 return 1;
159
160 #ifdef ENABLE_POLKIT
161         else {
162                 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
163                 int authorized = false, challenge = false;
164                 const char *sender;
165
166                 sender = sd_bus_message_get_sender(m);
167                 if (!sender)
168                         return -EBADMSG;
169
170                 r = sd_bus_call_method(
171                                 bus,
172                                 "org.freedesktop.PolicyKit1",
173                                 "/org/freedesktop/PolicyKit1/Authority",
174                                 "org.freedesktop.PolicyKit1.Authority",
175                                 "CheckAuthorization",
176                                 e,
177                                 &reply,
178                                 "(sa{sv})sa{ss}us",
179                                 "system-bus-name", 1, "name", "s", sender,
180                                 action,
181                                 0,
182                                 interactive ? 1 : 0,
183                                 "");
184
185                 if (r < 0) {
186                         /* Treat no PK available as access denied */
187                         if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
188                                 sd_bus_error_free(e);
189                                 return -EACCES;
190                         }
191
192                         return r;
193                 }
194
195                 r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
196                 if (r >= 0)
197                         r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
198
199                 if (authorized)
200                         return 1;
201
202                 if (_challenge) {
203                         *_challenge = challenge;
204                         return 0;
205                 }
206         }
207 #endif
208
209         return -EACCES;
210 }
211
212 #ifdef ENABLE_POLKIT
213
214 typedef struct AsyncPolkitQuery {
215         sd_bus_message *request, *reply;
216         sd_bus_message_handler_t callback;
217         void *userdata;
218         uint64_t serial;
219         Hashmap *registry;
220 } AsyncPolkitQuery;
221
222 static void async_polkit_query_free(sd_bus *b, AsyncPolkitQuery *q) {
223
224         if (!q)
225                 return;
226
227         if (q->serial > 0 && b)
228                 sd_bus_call_async_cancel(b, q->serial);
229
230         if (q->registry && q->request)
231                 hashmap_remove(q->registry, q->request);
232
233         sd_bus_message_unref(q->request);
234         sd_bus_message_unref(q->reply);
235
236         free(q);
237 }
238
239 static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
240         _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
241         AsyncPolkitQuery *q = userdata;
242         int r;
243
244         assert(bus);
245         assert(reply);
246         assert(q);
247
248         q->reply = sd_bus_message_ref(reply);
249         q->serial = 0;
250
251         r = sd_bus_message_rewind(q->request, true);
252         if (r < 0) {
253                 r = sd_bus_reply_method_errno(q->request, r, NULL);
254                 goto finish;
255         }
256
257         r = q->callback(bus, q->request, q->userdata, &error_buffer);
258         r = bus_maybe_reply_error(q->request, r, &error_buffer);
259
260 finish:
261         async_polkit_query_free(bus, q);
262         return r;
263 }
264
265 #endif
266
267 int bus_verify_polkit_async(
268                 sd_bus *bus,
269                 Hashmap **registry,
270                 sd_bus_message *m,
271                 const char *action,
272                 bool interactive,
273                 sd_bus_error *error,
274                 sd_bus_message_handler_t callback,
275                 void *userdata) {
276
277 #ifdef ENABLE_POLKIT
278         _cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
279         AsyncPolkitQuery *q;
280         const char *sender;
281 #endif
282         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
283         uid_t uid;
284         int r;
285
286         assert(bus);
287         assert(registry);
288         assert(m);
289         assert(action);
290
291 #ifdef ENABLE_POLKIT
292         q = hashmap_get(*registry, m);
293         if (q) {
294                 int authorized, challenge;
295
296                 /* This is the second invocation of this function, and
297                  * there's already a response from polkit, let's
298                  * process it */
299                 assert(q->reply);
300
301                 if (sd_bus_message_is_method_error(q->reply, NULL)) {
302                         const sd_bus_error *e;
303
304                         /* Copy error from polkit reply */
305                         e = sd_bus_message_get_error(q->reply);
306                         sd_bus_error_copy(error, e);
307
308                         /* Treat no PK available as access denied */
309                         if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN))
310                                 return -EACCES;
311
312                         return sd_bus_error_get_errno(e);
313                 }
314
315                 r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
316                 if (r >= 0)
317                         r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
318
319                 if (r < 0)
320                         return r;
321
322                 if (authorized)
323                         return 1;
324
325                 return -EACCES;
326         }
327 #endif
328
329         r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID, &creds);
330         if (r < 0)
331                 return r;
332
333         r = sd_bus_creds_get_uid(creds, &uid);
334         if (r < 0)
335                 return r;
336
337         if (uid == 0)
338                 return 1;
339
340 #ifdef ENABLE_POLKIT
341         sender = sd_bus_message_get_sender(m);
342         if (!sender)
343                 return -EBADMSG;
344
345         r = hashmap_ensure_allocated(registry, trivial_hash_func, trivial_compare_func);
346         if (r < 0)
347                 return r;
348
349         r = sd_bus_message_new_method_call(
350                         bus,
351                         "org.freedesktop.PolicyKit1",
352                         "/org/freedesktop/PolicyKit1/Authority",
353                         "org.freedesktop.PolicyKit1.Authority",
354                         "CheckAuthorization",
355                         &pk);
356         if (r < 0)
357                 return r;
358
359         r = sd_bus_message_append(
360                         pk,
361                         "(sa{sv})sa{ss}us",
362                         "system-bus-name", 1, "name", "s", sender,
363                         action,
364                         0,
365                         interactive ? 1 : 0,
366                         NULL);
367         if (r < 0)
368                 return r;
369
370         q = new0(AsyncPolkitQuery, 1);
371         if (!q)
372                 return -ENOMEM;
373
374         q->request = sd_bus_message_ref(m);
375         q->callback = callback;
376         q->userdata = userdata;
377
378         r = hashmap_put(*registry, m, q);
379         if (r < 0) {
380                 async_polkit_query_free(bus, q);
381                 return r;
382         }
383
384         q->registry = *registry;
385
386         r = sd_bus_call_async(bus, pk, async_polkit_callback, q, 0, &q->serial);
387         if (r < 0) {
388                 async_polkit_query_free(bus, q);
389                 return r;
390         }
391
392         return 0;
393 #endif
394
395         return -EACCES;
396 }
397
398 void bus_verify_polkit_async_registry_free(sd_bus *bus, Hashmap *registry) {
399 #ifdef ENABLE_POLKIT
400         AsyncPolkitQuery *q;
401
402         while ((q = hashmap_steal_first(registry)))
403                 async_polkit_query_free(bus, q);
404
405         hashmap_free(registry);
406 #endif
407 }
408
409 int bus_check_peercred(sd_bus *c) {
410         struct ucred ucred;
411         socklen_t l;
412         int fd;
413
414         assert(c);
415
416         fd = sd_bus_get_fd(c);
417         if (fd < 0)
418                 return fd;
419
420         l = sizeof(struct ucred);
421         if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0)
422                 return -errno;
423
424         if (l != sizeof(struct ucred))
425                 return -E2BIG;
426
427         if (ucred.uid != 0 && ucred.uid != geteuid())
428                 return -EPERM;
429
430         return 1;
431 }
432
433 int bus_open_system_systemd(sd_bus **_bus) {
434         _cleanup_bus_unref_ sd_bus *bus = NULL;
435         int r;
436
437         assert(_bus);
438
439         if (geteuid() != 0)
440                 return sd_bus_open_system(_bus);
441
442         /* If we are root and kdbus is not available, then let's talk
443          * directly to the system instance, instead of going via the
444          * bus */
445
446 #ifdef ENABLE_KDBUS
447         r = sd_bus_new(&bus);
448         if (r < 0)
449                 return r;
450
451         r = sd_bus_set_address(bus, "kernel:path=/dev/kdbus/0-system/bus");
452         if (r < 0)
453                 return r;
454
455         bus->bus_client = true;
456
457         r = sd_bus_start(bus);
458         if (r >= 0) {
459                 *_bus = bus;
460                 bus = NULL;
461                 return 0;
462         }
463
464         bus = sd_bus_unref(bus);
465 #endif
466
467         r = sd_bus_new(&bus);
468         if (r < 0)
469                 return r;
470
471         r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
472         if (r < 0)
473                 return r;
474
475         r = sd_bus_start(bus);
476         if (r < 0)
477                 return sd_bus_open_system(_bus);
478
479         r = bus_check_peercred(bus);
480         if (r < 0)
481                 return r;
482
483         *_bus = bus;
484         bus = NULL;
485
486         return 0;
487 }
488
489 int bus_open_user_systemd(sd_bus **_bus) {
490         _cleanup_bus_unref_ sd_bus *bus = NULL;
491         _cleanup_free_ char *ee = NULL;
492         const char *e;
493         int r;
494
495         /* Try via kdbus first, and then directly */
496
497         assert(_bus);
498
499 #ifdef ENABLE_KDBUS
500         r = sd_bus_new(&bus);
501         if (r < 0)
502                 return r;
503
504         if (asprintf(&bus->address, "kernel:path=/dev/kdbus/%lu-user/bus", (unsigned long) getuid()) < 0)
505                 return -ENOMEM;
506
507         bus->bus_client = true;
508
509         r = sd_bus_start(bus);
510         if (r >= 0) {
511                 *_bus = bus;
512                 bus = NULL;
513                 return 0;
514         }
515
516         bus = sd_bus_unref(bus);
517 #endif
518
519         e = secure_getenv("XDG_RUNTIME_DIR");
520         if (!e)
521                 return sd_bus_open_system(_bus);
522
523         ee = bus_address_escape(e);
524         if (!ee)
525                 return -ENOMEM;
526
527         r = sd_bus_new(&bus);
528         if (r < 0)
529                 return r;
530
531         bus->address = strjoin("unix:path=", ee, "/systemd/private", NULL);
532         if (!bus->address)
533                 return -ENOMEM;
534
535         r = sd_bus_start(bus);
536         if (r < 0)
537                 return sd_bus_open_system(_bus);
538
539         r = bus_check_peercred(bus);
540         if (r < 0)
541                 return r;
542
543         *_bus = bus;
544         bus = NULL;
545
546         return 0;
547 }
548
549 int bus_print_property(const char *name, sd_bus_message *property, bool all) {
550         char type;
551         const char *contents;
552         int r;
553
554         assert(name);
555         assert(property);
556
557         r = sd_bus_message_peek_type(property, &type, &contents);
558         if (r < 0)
559                 return r;
560
561         switch (type) {
562
563         case SD_BUS_TYPE_STRING: {
564                 const char *s;
565
566                 r = sd_bus_message_read_basic(property, type, &s);
567                 if (r < 0)
568                         return r;
569
570                 if (all || !isempty(s))
571                         printf("%s=%s\n", name, s);
572
573                 return 1;
574         }
575
576         case SD_BUS_TYPE_BOOLEAN: {
577                 bool b;
578
579                 r = sd_bus_message_read_basic(property, type, &b);
580                 if (r < 0)
581                         return r;
582
583                 printf("%s=%s\n", name, yes_no(b));
584
585                 return 1;
586         }
587
588         case SD_BUS_TYPE_UINT64: {
589                 uint64_t u;
590
591                 r = sd_bus_message_read_basic(property, type, &u);
592                 if (r < 0)
593                         return r;
594
595                 /* Yes, heuristics! But we can change this check
596                  * should it turn out to not be sufficient */
597
598                 if (endswith(name, "Timestamp")) {
599                         char timestamp[FORMAT_TIMESTAMP_MAX], *t;
600
601                         t = format_timestamp(timestamp, sizeof(timestamp), u);
602                         if (t || all)
603                                 printf("%s=%s\n", name, strempty(t));
604
605                 } else if (strstr(name, "USec")) {
606                         char timespan[FORMAT_TIMESPAN_MAX];
607
608                         printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
609                 } else
610                         printf("%s=%llu\n", name, (unsigned long long) u);
611
612                 return 1;
613         }
614
615         case SD_BUS_TYPE_UINT32: {
616                 uint32_t u;
617
618                 r = sd_bus_message_read_basic(property, type, &u);
619                 if (r < 0)
620                         return r;
621
622                 if (strstr(name, "UMask") || strstr(name, "Mode"))
623                         printf("%s=%04o\n", name, u);
624                 else
625                         printf("%s=%u\n", name, (unsigned) u);
626
627                 return 1;
628         }
629
630         case SD_BUS_TYPE_INT32: {
631                 int32_t i;
632
633                 r = sd_bus_message_read_basic(property, type, &i);
634                 if (r < 0)
635                         return r;
636
637                 printf("%s=%i\n", name, (int) i);
638                 return 1;
639         }
640
641         case SD_BUS_TYPE_DOUBLE: {
642                 double d;
643
644                 r = sd_bus_message_read_basic(property, type, &d);
645                 if (r < 0)
646                         return r;
647
648                 printf("%s=%g\n", name, d);
649                 return 1;
650         }
651
652         case SD_BUS_TYPE_ARRAY:
653                 if (streq(contents, "s")) {
654                         bool first = true;
655                         const char *str;
656
657                         r = sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
658                         if (r < 0)
659                                 return r;
660
661                         while((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) {
662                                 if (first)
663                                         printf("%s=", name);
664
665                                 printf("%s%s", first ? "" : " ", str);
666
667                                 first = false;
668                         }
669                         if (r < 0)
670                                 return r;
671
672                         if (first && all)
673                                 printf("%s=", name);
674                         if (!first || all)
675                                 puts("");
676
677                         r = sd_bus_message_exit_container(property);
678                         if (r < 0)
679                                 return r;
680
681                         return 1;
682
683                 } else if (streq(contents, "y")) {
684                         const uint8_t *u;
685                         size_t n;
686
687                         r = sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
688                         if (r < 0)
689                                 return r;
690
691                         if (all || n > 0) {
692                                 unsigned int i;
693
694                                 printf("%s=", name);
695
696                                 for (i = 0; i < n; i++)
697                                         printf("%02x", u[i]);
698
699                                 puts("");
700                         }
701
702                         return 1;
703
704                 } else if (streq(contents, "u")) {
705                         uint32_t *u;
706                         size_t n;
707
708                         r = sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
709                         if (r < 0)
710                                 return r;
711
712                         if (all || n > 0) {
713                                 unsigned int i;
714
715                                 printf("%s=", name);
716
717                                 for (i = 0; i < n; i++)
718                                         printf("%08x", u[i]);
719
720                                 puts("");
721                         }
722
723                         return 1;
724                 }
725
726                 break;
727         }
728
729         return 0;
730 }
731
732 int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all) {
733         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
734         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
735         int r;
736
737         assert(bus);
738         assert(path);
739
740         r = sd_bus_call_method(bus,
741                         dest,
742                         path,
743                         "org.freedesktop.DBus.Properties",
744                         "GetAll",
745                         &error,
746                         &reply,
747                         "s", "");
748         if (r < 0)
749                 return r;
750
751         r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
752         if (r < 0)
753                 return r;
754
755         while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
756                 const char *name;
757                 const char *contents;
758
759                 r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name);
760                 if (r < 0)
761                         return r;
762
763                 if (!filter || strv_find(filter, name)) {
764                         r = sd_bus_message_peek_type(reply, NULL, &contents);
765                         if (r < 0)
766                                 return r;
767
768                         r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
769                         if (r < 0)
770                                 return r;
771
772                         r = bus_print_property(name, reply, all);
773                         if (r < 0)
774                                 return r;
775                         if (r == 0) {
776                                 if (all)
777                                         printf("%s=[unprintable]\n", name);
778                                 /* skip what we didn't read */
779                                 r = sd_bus_message_skip(reply, contents);
780                                 if (r < 0)
781                                         return r;
782                         }
783
784                         r = sd_bus_message_exit_container(reply);
785                         if (r < 0)
786                                 return r;
787                 } else {
788                         r = sd_bus_message_skip(reply, "v");
789                         if (r < 0)
790                                 return r;
791                 }
792
793                 r = sd_bus_message_exit_container(reply);
794                 if (r < 0)
795                         return r;
796         }
797         if (r < 0)
798                 return r;
799
800         r = sd_bus_message_exit_container(reply);
801         if (r < 0)
802                 return r;
803
804         return 0;
805 }
806
807 int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
808         sd_id128_t *p = userdata;
809         const void *v;
810         size_t n;
811         int r;
812
813         r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n);
814         if (r < 0)
815                 return r;
816
817         if (n == 0)
818                 *p = SD_ID128_NULL;
819         else if (n == 16)
820                 memcpy((*p).bytes, v, n);
821         else
822                 return -EINVAL;
823
824         return 0;
825 }
826
827 static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
828         char type;
829         int r;
830
831         r = sd_bus_message_peek_type(m, &type, NULL);
832         if (r < 0)
833                 return r;
834
835         switch (type) {
836         case SD_BUS_TYPE_STRING: {
837                 const char *s;
838                 char *str;
839                 char **p = userdata;
840
841                 r = sd_bus_message_read_basic(m, type, &s);
842                 if (r < 0)
843                         break;
844
845                 if (isempty(s))
846                         break;
847
848                 str = strdup(s);
849                 if (!str) {
850                         r = -ENOMEM;
851                         break;
852                 }
853                 free(*p);
854                 *p = str;
855
856                 break;
857         }
858
859         case SD_BUS_TYPE_ARRAY: {
860                _cleanup_strv_free_ char **l = NULL;
861                char ***p = userdata;
862
863                 r = bus_message_read_strv_extend(m, &l);
864                 if (r < 0)
865                         break;
866
867                 strv_free(*p);
868                 *p = l;
869                 l = NULL;
870
871                 break;
872         }
873
874         case SD_BUS_TYPE_BOOLEAN: {
875                 unsigned b;
876                 bool *p = userdata;
877
878                 r = sd_bus_message_read_basic(m, type, &b);
879                 if (r < 0)
880                         break;
881
882                 *p = b;
883
884                 break;
885         }
886
887         case SD_BUS_TYPE_UINT32: {
888                 uint64_t u;
889                 uint32_t *p = userdata;
890
891                 r = sd_bus_message_read_basic(m, type, &u);
892                 if (r < 0)
893                         break;
894
895                 *p = u;
896
897                 break;
898         }
899
900         case SD_BUS_TYPE_UINT64: {
901                 uint64_t t;
902                 uint64_t *p = userdata;
903
904                 r = sd_bus_message_read_basic(m, type, &t);
905                 if (r < 0)
906                         break;
907
908                 *p = t;
909
910                 break;
911         }
912
913         default:
914                 break;
915         }
916
917         return r;
918 }
919
920 int bus_map_all_properties(sd_bus *bus,
921                            const char *destination,
922                            const char *path,
923                            const struct bus_properties_map *map,
924                            void *userdata) {
925         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
926         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
927         int r;
928
929         assert(bus);
930         assert(destination);
931         assert(path);
932         assert(map);
933
934         r = sd_bus_call_method( bus,
935                         destination,
936                         path,
937                         "org.freedesktop.DBus.Properties",
938                         "GetAll",
939                         &error,
940                         &m,
941                         "s", "");
942         if (r < 0)
943                 return r;
944
945         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
946         if (r < 0)
947                 return r;
948
949         while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
950                 const struct bus_properties_map *prop;
951                 const char *member;
952                 const char *contents;
953                 void *v;
954                 unsigned i;
955
956                 r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member);
957                 if (r < 0)
958                         return r;
959
960                 for (i = 0, prop = NULL; map[i].member; i++)
961                         if (streq(map[i].member, member)) {
962                                 prop = &map[i];
963                                 break;
964                         }
965
966                 if (prop) {
967                         r = sd_bus_message_peek_type(m, NULL, &contents);
968                         if (r < 0)
969                                 return r;
970
971                         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
972                         if (r < 0)
973                                 return r;
974
975                         v = (uint8_t *)userdata + prop->offset;
976                         if (map[i].set)
977                                 r = prop->set(bus, member, m, &error, v);
978                         else
979                                 r = map_basic(bus, member, m, &error, v);
980
981                         r = sd_bus_message_exit_container(m);
982                         if (r < 0)
983                                 return r;
984                 } else {
985                         r = sd_bus_message_skip(m, "v");
986                         if (r < 0)
987                                 return r;
988                 }
989
990                 r = sd_bus_message_exit_container(m);
991                 if (r < 0)
992                         return r;
993         }
994
995         return r;
996 }
997
998 int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
999         int r;
1000
1001         assert(transport >= 0);
1002         assert(transport < _BUS_TRANSPORT_MAX);
1003         assert(bus);
1004
1005         assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
1006         assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP);
1007
1008         switch (transport) {
1009
1010         case BUS_TRANSPORT_LOCAL:
1011                 if (user)
1012                         r = sd_bus_default_user(bus);
1013                 else
1014                         r = sd_bus_default_system(bus);
1015
1016                 break;
1017
1018         case BUS_TRANSPORT_REMOTE:
1019                 r = sd_bus_open_system_remote(host, bus);
1020                 break;
1021
1022         case BUS_TRANSPORT_CONTAINER:
1023                 r = sd_bus_open_system_container(host, bus);
1024                 break;
1025
1026         default:
1027                 assert_not_reached("Hmm, unknown transport type.");
1028         }
1029
1030         return r;
1031 }
1032
1033 int bus_open_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) {
1034         int r;
1035
1036         assert(transport >= 0);
1037         assert(transport < _BUS_TRANSPORT_MAX);
1038         assert(bus);
1039
1040         assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
1041         assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP);
1042
1043         switch (transport) {
1044
1045         case BUS_TRANSPORT_LOCAL:
1046                 if (user)
1047                         r = bus_open_user_systemd(bus);
1048                 else
1049                         r = bus_open_system_systemd(bus);
1050
1051                 break;
1052
1053         case BUS_TRANSPORT_REMOTE:
1054                 r = sd_bus_open_system_remote(host, bus);
1055                 break;
1056
1057         case BUS_TRANSPORT_CONTAINER:
1058                 r = sd_bus_open_system_container(host, bus);
1059                 break;
1060
1061         default:
1062                 assert_not_reached("Hmm, unknown transport type.");
1063         }
1064
1065         return r;
1066 }
1067
1068 int bus_property_get_tristate(
1069                 sd_bus *bus,
1070                 const char *path,
1071                 const char *interface,
1072                 const char *property,
1073                 sd_bus_message *reply,
1074                 void *userdata,
1075                 sd_bus_error *error) {
1076
1077         int *tristate = userdata;
1078
1079         return sd_bus_message_append(reply, "b", *tristate > 0);
1080 }
1081
1082 int bus_property_get_bool(
1083                 sd_bus *bus,
1084                 const char *path,
1085                 const char *interface,
1086                 const char *property,
1087                 sd_bus_message *reply,
1088                 void *userdata,
1089                 sd_bus_error *error) {
1090
1091         int b = *(bool*) userdata;
1092
1093         return sd_bus_message_append_basic(reply, 'b', &b);
1094 }
1095
1096 #if __SIZEOF_SIZE_T__ != 8
1097 int bus_property_get_size(
1098                 sd_bus *bus,
1099                 const char *path,
1100                 const char *interface,
1101                 const char *property,
1102                 sd_bus_message *reply,
1103                 void *userdata,
1104                 sd_bus_error *error) {
1105
1106         uint64_t sz = *(size_t*) userdata;
1107
1108         return sd_bus_message_append_basic(reply, 't', &sz);
1109 }
1110 #endif
1111
1112 #if __SIZEOF_LONG__ != 8
1113 int bus_property_get_long(
1114                 sd_bus *bus,
1115                 const char *path,
1116                 const char *interface,
1117                 const char *property,
1118                 sd_bus_message *reply,
1119                 void *userdata,
1120                 sd_bus_error *error) {
1121
1122         int64_t l = *(long*) userdata;
1123
1124         return sd_bus_message_append_basic(reply, 'x', &l);
1125 }
1126
1127 int bus_property_get_ulong(
1128                 sd_bus *bus,
1129                 const char *path,
1130                 const char *interface,
1131                 const char *property,
1132                 sd_bus_message *reply,
1133                 void *userdata,
1134                 sd_bus_error *error) {
1135
1136         uint64_t ul = *(unsigned long*) userdata;
1137
1138         return sd_bus_message_append_basic(reply, 't', &ul);
1139 }
1140 #endif
1141
1142 int bus_log_parse_error(int r) {
1143         log_error("Failed to parse message: %s", strerror(-r));
1144         return r;
1145 }
1146
1147 int bus_log_create_error(int r) {
1148         log_error("Failed to create message: %s", strerror(-r));
1149         return r;
1150 }
1151
1152 int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
1153         assert(message);
1154         assert(u);
1155
1156         return sd_bus_message_read(
1157                         message,
1158                         "(ssssssouso)",
1159                         &u->id,
1160                         &u->description,
1161                         &u->load_state,
1162                         &u->active_state,
1163                         &u->sub_state,
1164                         &u->following,
1165                         &u->unit_path,
1166                         &u->job_id,
1167                         &u->job_type,
1168                         &u->job_path);
1169 }
1170
1171 int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) {
1172         assert(m);
1173
1174         if (r < 0) {
1175                 if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
1176                         sd_bus_reply_method_errno(m, r, error);
1177
1178         } else if (sd_bus_error_is_set(error)) {
1179                 if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
1180                         sd_bus_reply_method_error(m, error);
1181         } else
1182                 return r;
1183
1184         log_debug("Failed to process message [type=%s sender=%s path=%s interface=%s member=%s signature=%s]: %s",
1185                   bus_message_type_to_string(m->header->type),
1186                   strna(m->sender),
1187                   strna(m->path),
1188                   strna(m->interface),
1189                   strna(m->member),
1190                   strna(m->root_container.signature),
1191                   bus_error_message(error, r));
1192
1193         return 1;
1194 }