chiark / gitweb /
bus-util: simplify bus_verify_polkit_async() a bit
[elogind.git] / src / libsystemd / sd-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 "path-util.h"
30 #include "missing.h"
31
32 #include "sd-event.h"
33 #include "sd-bus.h"
34 #include "bus-error.h"
35 #include "bus-message.h"
36 #include "bus-util.h"
37 #include "bus-internal.h"
38
39 static int name_owner_change_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
40         sd_event *e = userdata;
41
42         assert(bus);
43         assert(m);
44         assert(e);
45
46         sd_bus_close(bus);
47         sd_event_exit(e, 0);
48
49         return 1;
50 }
51
52 int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) {
53         _cleanup_free_ char *match = NULL;
54         const char *unique;
55         int r;
56
57         assert(e);
58         assert(bus);
59         assert(name);
60
61         /* We unregister the name here and then wait for the
62          * NameOwnerChanged signal for this event to arrive before we
63          * quit. We do this in order to make sure that any queued
64          * requests are still processed before we really exit. */
65
66         r = sd_bus_get_unique_name(bus, &unique);
67         if (r < 0)
68                 return r;
69
70         r = asprintf(&match,
71                      "sender='org.freedesktop.DBus',"
72                      "type='signal',"
73                      "interface='org.freedesktop.DBus',"
74                      "member='NameOwnerChanged',"
75                      "path='/org/freedesktop/DBus',"
76                      "arg0='%s',"
77                      "arg1='%s',"
78                      "arg2=''", name, unique);
79         if (r < 0)
80                 return -ENOMEM;
81
82         r = sd_bus_add_match(bus, NULL, match, name_owner_change_callback, e);
83         if (r < 0)
84                 return r;
85
86         r = sd_bus_release_name(bus, name);
87         if (r < 0)
88                 return r;
89
90         return 0;
91 }
92
93 int bus_event_loop_with_idle(
94                 sd_event *e,
95                 sd_bus *bus,
96                 const char *name,
97                 usec_t timeout,
98                 check_idle_t check_idle,
99                 void *userdata) {
100         bool exiting = false;
101         int r, code;
102
103         assert(e);
104         assert(bus);
105         assert(name);
106
107         for (;;) {
108                 bool idle;
109
110                 r = sd_event_get_state(e);
111                 if (r < 0)
112                         return r;
113                 if (r == SD_EVENT_FINISHED)
114                         break;
115
116                 if (check_idle)
117                         idle = check_idle(userdata);
118                 else
119                         idle = true;
120
121                 r = sd_event_run(e, exiting || !idle ? (uint64_t) -1 : timeout);
122                 if (r < 0)
123                         return r;
124
125                 if (r == 0 && !exiting) {
126
127                         r = sd_bus_try_close(bus);
128                         if (r == -EBUSY)
129                                 continue;
130
131                         if (r == -ENOTSUP) {
132                                 /* Fallback for dbus1 connections: we
133                                  * unregister the name and wait for
134                                  * the response to come through for
135                                  * it */
136
137                                 r = bus_async_unregister_and_exit(e, bus, name);
138                                 if (r < 0)
139                                         return r;
140
141                                 exiting = true;
142                                 continue;
143                         }
144
145                         if (r < 0)
146                                 return r;
147
148                         sd_event_exit(e, 0);
149                         break;
150                 }
151         }
152
153         r = sd_event_get_exit_code(e, &code);
154         if (r < 0)
155                 return r;
156
157         return code;
158 }
159
160 int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) {
161         _cleanup_bus_message_unref_ sd_bus_message *rep = NULL;
162         int r, has_owner = 0;
163
164         assert(c);
165         assert(name);
166
167         r = sd_bus_call_method(c,
168                                "org.freedesktop.DBus",
169                                "/org/freedesktop/dbus",
170                                "org.freedesktop.DBus",
171                                "NameHasOwner",
172                                error,
173                                &rep,
174                                "s",
175                                name);
176         if (r < 0)
177                 return r;
178
179         r = sd_bus_message_read_basic(rep, 'b', &has_owner);
180         if (r < 0)
181                 return sd_bus_error_set_errno(error, r);
182
183         return has_owner;
184 }
185
186 int bus_verify_polkit(
187                 sd_bus_message *call,
188                 int capability,
189                 const char *action,
190                 bool interactive,
191                 bool *_challenge,
192                 sd_bus_error *e) {
193
194         int r;
195
196         assert(call);
197         assert(action);
198
199         r = sd_bus_query_sender_privilege(call, capability);
200         if (r < 0)
201                 return r;
202         else if (r > 0)
203                 return 1;
204 #ifdef ENABLE_POLKIT
205         else {
206                 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
207                 int authorized = false, challenge = false;
208                 const char *sender;
209
210                 sender = sd_bus_message_get_sender(call);
211                 if (!sender)
212                         return -EBADMSG;
213
214                 r = sd_bus_call_method(
215                                 call->bus,
216                                 "org.freedesktop.PolicyKit1",
217                                 "/org/freedesktop/PolicyKit1/Authority",
218                                 "org.freedesktop.PolicyKit1.Authority",
219                                 "CheckAuthorization",
220                                 e,
221                                 &reply,
222                                 "(sa{sv})sa{ss}us",
223                                 "system-bus-name", 1, "name", "s", sender,
224                                 action,
225                                 0,
226                                 interactive ? 1 : 0,
227                                 "");
228
229                 if (r < 0) {
230                         /* Treat no PK available as access denied */
231                         if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
232                                 sd_bus_error_free(e);
233                                 return -EACCES;
234                         }
235
236                         return r;
237                 }
238
239                 r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
240                 if (r < 0)
241                         return r;
242
243                 r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
244                 if (r < 0)
245                         return r;
246
247                 if (authorized)
248                         return 1;
249
250                 if (_challenge) {
251                         *_challenge = challenge;
252                         return 0;
253                 }
254         }
255 #endif
256
257         return -EACCES;
258 }
259
260 #ifdef ENABLE_POLKIT
261
262 typedef struct AsyncPolkitQuery {
263         sd_bus_message *request, *reply;
264         sd_bus_message_handler_t callback;
265         void *userdata;
266         sd_bus_slot *slot;
267         Hashmap *registry;
268 } AsyncPolkitQuery;
269
270 static void async_polkit_query_free(AsyncPolkitQuery *q) {
271
272         if (!q)
273                 return;
274
275         sd_bus_slot_unref(q->slot);
276
277         if (q->registry && q->request)
278                 hashmap_remove(q->registry, q->request);
279
280         sd_bus_message_unref(q->request);
281         sd_bus_message_unref(q->reply);
282
283         free(q);
284 }
285
286 static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
287         _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
288         AsyncPolkitQuery *q = userdata;
289         int r;
290
291         assert(bus);
292         assert(reply);
293         assert(q);
294
295         q->slot = sd_bus_slot_unref(q->slot);
296         q->reply = sd_bus_message_ref(reply);
297
298         r = sd_bus_message_rewind(q->request, true);
299         if (r < 0) {
300                 r = sd_bus_reply_method_errno(q->request, r, NULL);
301                 goto finish;
302         }
303
304         r = q->callback(bus, q->request, q->userdata, &error_buffer);
305         r = bus_maybe_reply_error(q->request, r, &error_buffer);
306
307 finish:
308         async_polkit_query_free(q);
309
310         return r;
311 }
312
313 #endif
314
315 int bus_verify_polkit_async(
316                 sd_bus_message *call,
317                 int capability,
318                 const char *action,
319                 bool interactive,
320                 Hashmap **registry,
321                 sd_bus_error *error) {
322
323 #ifdef ENABLE_POLKIT
324         _cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
325         _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
326         AsyncPolkitQuery *q;
327         const char *sender;
328         sd_bus_message_handler_t callback;
329         void *userdata;
330 #endif
331         int r;
332
333         assert(call);
334         assert(action);
335         assert(registry);
336
337 #ifdef ENABLE_POLKIT
338         q = hashmap_get(*registry, call);
339         if (q) {
340                 int authorized, challenge;
341
342                 /* This is the second invocation of this function, and
343                  * there's already a response from polkit, let's
344                  * process it */
345                 assert(q->reply);
346
347                 if (sd_bus_message_is_method_error(q->reply, NULL)) {
348                         const sd_bus_error *e;
349
350                         /* Copy error from polkit reply */
351                         e = sd_bus_message_get_error(q->reply);
352                         sd_bus_error_copy(error, e);
353
354                         /* Treat no PK available as access denied */
355                         if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN))
356                                 return -EACCES;
357
358                         return -sd_bus_error_get_errno(e);
359                 }
360
361                 r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
362                 if (r >= 0)
363                         r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
364
365                 if (r < 0)
366                         return r;
367
368                 if (authorized)
369                         return 1;
370
371                 return -EACCES;
372         }
373 #endif
374
375         r = sd_bus_query_sender_privilege(call, capability);
376         if (r < 0)
377                 return r;
378         else if (r > 0)
379                 return 1;
380
381 #ifdef ENABLE_POLKIT
382         if (sd_bus_get_current_message(call->bus) != call)
383                 return -EINVAL;
384
385         callback = sd_bus_get_current_handler(call->bus);
386         if (!callback)
387                 return -EINVAL;
388
389         userdata = sd_bus_get_current_userdata(call->bus);
390
391         sender = sd_bus_message_get_sender(call);
392         if (!sender)
393                 return -EBADMSG;
394
395         r = hashmap_ensure_allocated(registry, trivial_hash_func, trivial_compare_func);
396         if (r < 0)
397                 return r;
398
399         r = sd_bus_message_new_method_call(
400                         call->bus,
401                         &pk,
402                         "org.freedesktop.PolicyKit1",
403                         "/org/freedesktop/PolicyKit1/Authority",
404                         "org.freedesktop.PolicyKit1.Authority",
405                         "CheckAuthorization");
406         if (r < 0)
407                 return r;
408
409         r = sd_bus_message_append(
410                         pk,
411                         "(sa{sv})sa{ss}us",
412                         "system-bus-name", 1, "name", "s", sender,
413                         action,
414                         0,
415                         interactive ? 1 : 0,
416                         NULL);
417         if (r < 0)
418                 return r;
419
420         q = new0(AsyncPolkitQuery, 1);
421         if (!q)
422                 return -ENOMEM;
423
424         q->request = sd_bus_message_ref(call);
425         q->callback = callback;
426         q->userdata = userdata;
427
428         r = hashmap_put(*registry, call, q);
429         if (r < 0) {
430                 async_polkit_query_free(q);
431                 return r;
432         }
433
434         q->registry = *registry;
435
436         r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0);
437         if (r < 0) {
438                 async_polkit_query_free(q);
439                 return r;
440         }
441
442         return 0;
443 #endif
444
445         return -EACCES;
446 }
447
448 void bus_verify_polkit_async_registry_free(Hashmap *registry) {
449 #ifdef ENABLE_POLKIT
450         AsyncPolkitQuery *q;
451
452         while ((q = hashmap_steal_first(registry)))
453                 async_polkit_query_free(q);
454
455         hashmap_free(registry);
456 #endif
457 }
458
459 int bus_check_peercred(sd_bus *c) {
460         struct ucred ucred;
461         socklen_t l;
462         int fd;
463
464         assert(c);
465
466         fd = sd_bus_get_fd(c);
467         if (fd < 0)
468                 return fd;
469
470         l = sizeof(struct ucred);
471         if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0)
472                 return -errno;
473
474         if (l != sizeof(struct ucred))
475                 return -E2BIG;
476
477         if (ucred.uid != 0 && ucred.uid != geteuid())
478                 return -EPERM;
479
480         return 1;
481 }
482
483 int bus_open_system_systemd(sd_bus **_bus) {
484         _cleanup_bus_unref_ sd_bus *bus = NULL;
485         int r;
486
487         assert(_bus);
488
489         if (geteuid() != 0)
490                 return sd_bus_open_system(_bus);
491
492         /* If we are root and kdbus is not available, then let's talk
493          * directly to the system instance, instead of going via the
494          * bus */
495
496 #ifdef ENABLE_KDBUS
497         r = sd_bus_new(&bus);
498         if (r < 0)
499                 return r;
500
501         r = sd_bus_set_address(bus, KERNEL_SYSTEM_BUS_PATH);
502         if (r < 0)
503                 return r;
504
505         bus->bus_client = true;
506
507         r = sd_bus_start(bus);
508         if (r >= 0) {
509                 *_bus = bus;
510                 bus = NULL;
511                 return 0;
512         }
513
514         bus = sd_bus_unref(bus);
515 #endif
516
517         r = sd_bus_new(&bus);
518         if (r < 0)
519                 return r;
520
521         r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
522         if (r < 0)
523                 return r;
524
525         r = sd_bus_start(bus);
526         if (r < 0)
527                 return sd_bus_open_system(_bus);
528
529         r = bus_check_peercred(bus);
530         if (r < 0)
531                 return r;
532
533         *_bus = bus;
534         bus = NULL;
535
536         return 0;
537 }
538
539 int bus_open_user_systemd(sd_bus **_bus) {
540         _cleanup_bus_unref_ sd_bus *bus = NULL;
541         _cleanup_free_ char *ee = NULL;
542         const char *e;
543         int r;
544
545         /* Try via kdbus first, and then directly */
546
547         assert(_bus);
548
549 #ifdef ENABLE_KDBUS
550         r = sd_bus_new(&bus);
551         if (r < 0)
552                 return r;
553
554         if (asprintf(&bus->address, KERNEL_USER_BUS_FMT, getuid()) < 0)
555                 return -ENOMEM;
556
557         bus->bus_client = true;
558
559         r = sd_bus_start(bus);
560         if (r >= 0) {
561                 *_bus = bus;
562                 bus = NULL;
563                 return 0;
564         }
565
566         bus = sd_bus_unref(bus);
567 #endif
568
569         e = secure_getenv("XDG_RUNTIME_DIR");
570         if (!e)
571                 return sd_bus_open_user(_bus);
572
573         ee = bus_address_escape(e);
574         if (!ee)
575                 return -ENOMEM;
576
577         r = sd_bus_new(&bus);
578         if (r < 0)
579                 return r;
580
581         bus->address = strjoin("unix:path=", ee, "/systemd/private", NULL);
582         if (!bus->address)
583                 return -ENOMEM;
584
585         r = sd_bus_start(bus);
586         if (r < 0)
587                 return sd_bus_open_user(_bus);
588
589         r = bus_check_peercred(bus);
590         if (r < 0)
591                 return r;
592
593         *_bus = bus;
594         bus = NULL;
595
596         return 0;
597 }
598
599 int bus_print_property(const char *name, sd_bus_message *property, bool all) {
600         char type;
601         const char *contents;
602         int r;
603
604         assert(name);
605         assert(property);
606
607         r = sd_bus_message_peek_type(property, &type, &contents);
608         if (r < 0)
609                 return r;
610
611         switch (type) {
612
613         case SD_BUS_TYPE_STRING: {
614                 const char *s;
615
616                 r = sd_bus_message_read_basic(property, type, &s);
617                 if (r < 0)
618                         return r;
619
620                 if (all || !isempty(s))
621                         printf("%s=%s\n", name, s);
622
623                 return 1;
624         }
625
626         case SD_BUS_TYPE_BOOLEAN: {
627                 bool b;
628
629                 r = sd_bus_message_read_basic(property, type, &b);
630                 if (r < 0)
631                         return r;
632
633                 printf("%s=%s\n", name, yes_no(b));
634
635                 return 1;
636         }
637
638         case SD_BUS_TYPE_UINT64: {
639                 uint64_t u;
640
641                 r = sd_bus_message_read_basic(property, type, &u);
642                 if (r < 0)
643                         return r;
644
645                 /* Yes, heuristics! But we can change this check
646                  * should it turn out to not be sufficient */
647
648                 if (endswith(name, "Timestamp")) {
649                         char timestamp[FORMAT_TIMESTAMP_MAX], *t;
650
651                         t = format_timestamp(timestamp, sizeof(timestamp), u);
652                         if (t || all)
653                                 printf("%s=%s\n", name, strempty(t));
654
655                 } else if (strstr(name, "USec")) {
656                         char timespan[FORMAT_TIMESPAN_MAX];
657
658                         printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
659                 } else
660                         printf("%s=%llu\n", name, (unsigned long long) u);
661
662                 return 1;
663         }
664
665         case SD_BUS_TYPE_UINT32: {
666                 uint32_t u;
667
668                 r = sd_bus_message_read_basic(property, type, &u);
669                 if (r < 0)
670                         return r;
671
672                 if (strstr(name, "UMask") || strstr(name, "Mode"))
673                         printf("%s=%04o\n", name, u);
674                 else
675                         printf("%s=%u\n", name, (unsigned) u);
676
677                 return 1;
678         }
679
680         case SD_BUS_TYPE_INT32: {
681                 int32_t i;
682
683                 r = sd_bus_message_read_basic(property, type, &i);
684                 if (r < 0)
685                         return r;
686
687                 printf("%s=%i\n", name, (int) i);
688                 return 1;
689         }
690
691         case SD_BUS_TYPE_DOUBLE: {
692                 double d;
693
694                 r = sd_bus_message_read_basic(property, type, &d);
695                 if (r < 0)
696                         return r;
697
698                 printf("%s=%g\n", name, d);
699                 return 1;
700         }
701
702         case SD_BUS_TYPE_ARRAY:
703                 if (streq(contents, "s")) {
704                         bool first = true;
705                         const char *str;
706
707                         r = sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
708                         if (r < 0)
709                                 return r;
710
711                         while((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) {
712                                 if (first)
713                                         printf("%s=", name);
714
715                                 printf("%s%s", first ? "" : " ", str);
716
717                                 first = false;
718                         }
719                         if (r < 0)
720                                 return r;
721
722                         if (first && all)
723                                 printf("%s=", name);
724                         if (!first || all)
725                                 puts("");
726
727                         r = sd_bus_message_exit_container(property);
728                         if (r < 0)
729                                 return r;
730
731                         return 1;
732
733                 } else if (streq(contents, "y")) {
734                         const uint8_t *u;
735                         size_t n;
736
737                         r = sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
738                         if (r < 0)
739                                 return r;
740
741                         if (all || n > 0) {
742                                 unsigned int i;
743
744                                 printf("%s=", name);
745
746                                 for (i = 0; i < n; i++)
747                                         printf("%02x", u[i]);
748
749                                 puts("");
750                         }
751
752                         return 1;
753
754                 } else if (streq(contents, "u")) {
755                         uint32_t *u;
756                         size_t n;
757
758                         r = sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
759                         if (r < 0)
760                                 return r;
761
762                         if (all || n > 0) {
763                                 unsigned int i;
764
765                                 printf("%s=", name);
766
767                                 for (i = 0; i < n; i++)
768                                         printf("%08x", u[i]);
769
770                                 puts("");
771                         }
772
773                         return 1;
774                 }
775
776                 break;
777         }
778
779         return 0;
780 }
781
782 int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all) {
783         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
784         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
785         int r;
786
787         assert(bus);
788         assert(path);
789
790         r = sd_bus_call_method(bus,
791                         dest,
792                         path,
793                         "org.freedesktop.DBus.Properties",
794                         "GetAll",
795                         &error,
796                         &reply,
797                         "s", "");
798         if (r < 0)
799                 return r;
800
801         r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
802         if (r < 0)
803                 return r;
804
805         while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
806                 const char *name;
807                 const char *contents;
808
809                 r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name);
810                 if (r < 0)
811                         return r;
812
813                 if (!filter || strv_find(filter, name)) {
814                         r = sd_bus_message_peek_type(reply, NULL, &contents);
815                         if (r < 0)
816                                 return r;
817
818                         r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
819                         if (r < 0)
820                                 return r;
821
822                         r = bus_print_property(name, reply, all);
823                         if (r < 0)
824                                 return r;
825                         if (r == 0) {
826                                 if (all)
827                                         printf("%s=[unprintable]\n", name);
828                                 /* skip what we didn't read */
829                                 r = sd_bus_message_skip(reply, contents);
830                                 if (r < 0)
831                                         return r;
832                         }
833
834                         r = sd_bus_message_exit_container(reply);
835                         if (r < 0)
836                                 return r;
837                 } else {
838                         r = sd_bus_message_skip(reply, "v");
839                         if (r < 0)
840                                 return r;
841                 }
842
843                 r = sd_bus_message_exit_container(reply);
844                 if (r < 0)
845                         return r;
846         }
847         if (r < 0)
848                 return r;
849
850         r = sd_bus_message_exit_container(reply);
851         if (r < 0)
852                 return r;
853
854         return 0;
855 }
856
857 int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
858         sd_id128_t *p = userdata;
859         const void *v;
860         size_t n;
861         int r;
862
863         r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n);
864         if (r < 0)
865                 return r;
866
867         if (n == 0)
868                 *p = SD_ID128_NULL;
869         else if (n == 16)
870                 memcpy((*p).bytes, v, n);
871         else
872                 return -EINVAL;
873
874         return 0;
875 }
876
877 static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
878         char type;
879         int r;
880
881         r = sd_bus_message_peek_type(m, &type, NULL);
882         if (r < 0)
883                 return r;
884
885         switch (type) {
886         case SD_BUS_TYPE_STRING: {
887                 const char *s;
888                 char *str;
889                 char **p = userdata;
890
891                 r = sd_bus_message_read_basic(m, type, &s);
892                 if (r < 0)
893                         break;
894
895                 if (isempty(s))
896                         break;
897
898                 str = strdup(s);
899                 if (!str) {
900                         r = -ENOMEM;
901                         break;
902                 }
903                 free(*p);
904                 *p = str;
905
906                 break;
907         }
908
909         case SD_BUS_TYPE_ARRAY: {
910                _cleanup_strv_free_ char **l = NULL;
911                char ***p = userdata;
912
913                 r = bus_message_read_strv_extend(m, &l);
914                 if (r < 0)
915                         break;
916
917                 strv_free(*p);
918                 *p = l;
919                 l = NULL;
920
921                 break;
922         }
923
924         case SD_BUS_TYPE_BOOLEAN: {
925                 unsigned b;
926                 bool *p = userdata;
927
928                 r = sd_bus_message_read_basic(m, type, &b);
929                 if (r < 0)
930                         break;
931
932                 *p = b;
933
934                 break;
935         }
936
937         case SD_BUS_TYPE_UINT32: {
938                 uint64_t u;
939                 uint32_t *p = userdata;
940
941                 r = sd_bus_message_read_basic(m, type, &u);
942                 if (r < 0)
943                         break;
944
945                 *p = u;
946
947                 break;
948         }
949
950         case SD_BUS_TYPE_UINT64: {
951                 uint64_t t;
952                 uint64_t *p = userdata;
953
954                 r = sd_bus_message_read_basic(m, type, &t);
955                 if (r < 0)
956                         break;
957
958                 *p = t;
959
960                 break;
961         }
962
963         default:
964                 break;
965         }
966
967         return r;
968 }
969
970 int bus_map_all_properties(sd_bus *bus,
971                            const char *destination,
972                            const char *path,
973                            const struct bus_properties_map *map,
974                            void *userdata) {
975         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
976         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
977         int r;
978
979         assert(bus);
980         assert(destination);
981         assert(path);
982         assert(map);
983
984         r = sd_bus_call_method(
985                         bus,
986                         destination,
987                         path,
988                         "org.freedesktop.DBus.Properties",
989                         "GetAll",
990                         &error,
991                         &m,
992                         "s", "");
993         if (r < 0)
994                 return r;
995
996         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
997         if (r < 0)
998                 return r;
999
1000         while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
1001                 const struct bus_properties_map *prop;
1002                 const char *member;
1003                 const char *contents;
1004                 void *v;
1005                 unsigned i;
1006
1007                 r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member);
1008                 if (r < 0)
1009                         return r;
1010
1011                 for (i = 0, prop = NULL; map[i].member; i++)
1012                         if (streq(map[i].member, member)) {
1013                                 prop = &map[i];
1014                                 break;
1015                         }
1016
1017                 if (prop) {
1018                         r = sd_bus_message_peek_type(m, NULL, &contents);
1019                         if (r < 0)
1020                                 return r;
1021
1022                         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
1023                         if (r < 0)
1024                                 return r;
1025
1026                         v = (uint8_t *)userdata + prop->offset;
1027                         if (map[i].set)
1028                                 r = prop->set(bus, member, m, &error, v);
1029                         else
1030                                 r = map_basic(bus, member, m, &error, v);
1031                         if (r < 0)
1032                                 return r;
1033
1034                         r = sd_bus_message_exit_container(m);
1035                         if (r < 0)
1036                                 return r;
1037                 } else {
1038                         r = sd_bus_message_skip(m, "v");
1039                         if (r < 0)
1040                                 return r;
1041                 }
1042
1043                 r = sd_bus_message_exit_container(m);
1044                 if (r < 0)
1045                         return r;
1046         }
1047
1048         return r;
1049 }
1050
1051 int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
1052         int r;
1053
1054         assert(transport >= 0);
1055         assert(transport < _BUS_TRANSPORT_MAX);
1056         assert(bus);
1057
1058         assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
1059         assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP);
1060
1061         switch (transport) {
1062
1063         case BUS_TRANSPORT_LOCAL:
1064                 if (user)
1065                         r = sd_bus_default_user(bus);
1066                 else
1067                         r = sd_bus_default_system(bus);
1068
1069                 break;
1070
1071         case BUS_TRANSPORT_REMOTE:
1072                 r = sd_bus_open_system_remote(bus, host);
1073                 break;
1074
1075         case BUS_TRANSPORT_CONTAINER:
1076                 r = sd_bus_open_system_container(bus, host);
1077                 break;
1078
1079         default:
1080                 assert_not_reached("Hmm, unknown transport type.");
1081         }
1082
1083         return r;
1084 }
1085
1086 int bus_open_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) {
1087         int r;
1088
1089         assert(transport >= 0);
1090         assert(transport < _BUS_TRANSPORT_MAX);
1091         assert(bus);
1092
1093         assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
1094         assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP);
1095
1096         switch (transport) {
1097
1098         case BUS_TRANSPORT_LOCAL:
1099                 if (user)
1100                         r = bus_open_user_systemd(bus);
1101                 else
1102                         r = bus_open_system_systemd(bus);
1103
1104                 break;
1105
1106         case BUS_TRANSPORT_REMOTE:
1107                 r = sd_bus_open_system_remote(bus, host);
1108                 break;
1109
1110         case BUS_TRANSPORT_CONTAINER:
1111                 r = sd_bus_open_system_container(bus, host);
1112                 break;
1113
1114         default:
1115                 assert_not_reached("Hmm, unknown transport type.");
1116         }
1117
1118         return r;
1119 }
1120
1121 int bus_property_get_bool(
1122                 sd_bus *bus,
1123                 const char *path,
1124                 const char *interface,
1125                 const char *property,
1126                 sd_bus_message *reply,
1127                 void *userdata,
1128                 sd_bus_error *error) {
1129
1130         int b = *(bool*) userdata;
1131
1132         return sd_bus_message_append_basic(reply, 'b', &b);
1133 }
1134
1135 #if __SIZEOF_SIZE_T__ != 8
1136 int bus_property_get_size(
1137                 sd_bus *bus,
1138                 const char *path,
1139                 const char *interface,
1140                 const char *property,
1141                 sd_bus_message *reply,
1142                 void *userdata,
1143                 sd_bus_error *error) {
1144
1145         uint64_t sz = *(size_t*) userdata;
1146
1147         return sd_bus_message_append_basic(reply, 't', &sz);
1148 }
1149 #endif
1150
1151 #if __SIZEOF_LONG__ != 8
1152 int bus_property_get_long(
1153                 sd_bus *bus,
1154                 const char *path,
1155                 const char *interface,
1156                 const char *property,
1157                 sd_bus_message *reply,
1158                 void *userdata,
1159                 sd_bus_error *error) {
1160
1161         int64_t l = *(long*) userdata;
1162
1163         return sd_bus_message_append_basic(reply, 'x', &l);
1164 }
1165
1166 int bus_property_get_ulong(
1167                 sd_bus *bus,
1168                 const char *path,
1169                 const char *interface,
1170                 const char *property,
1171                 sd_bus_message *reply,
1172                 void *userdata,
1173                 sd_bus_error *error) {
1174
1175         uint64_t ul = *(unsigned long*) userdata;
1176
1177         return sd_bus_message_append_basic(reply, 't', &ul);
1178 }
1179 #endif
1180
1181 int bus_log_parse_error(int r) {
1182         log_error("Failed to parse bus message: %s", strerror(-r));
1183         return r;
1184 }
1185
1186 int bus_log_create_error(int r) {
1187         log_error("Failed to create bus message: %s", strerror(-r));
1188         return r;
1189 }
1190
1191 int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
1192         assert(message);
1193         assert(u);
1194
1195         u->machine = NULL;
1196
1197         return sd_bus_message_read(
1198                         message,
1199                         "(ssssssouso)",
1200                         &u->id,
1201                         &u->description,
1202                         &u->load_state,
1203                         &u->active_state,
1204                         &u->sub_state,
1205                         &u->following,
1206                         &u->unit_path,
1207                         &u->job_id,
1208                         &u->job_type,
1209                         &u->job_path);
1210 }
1211
1212 int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) {
1213         assert(m);
1214
1215         if (r < 0) {
1216                 if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
1217                         sd_bus_reply_method_errno(m, r, error);
1218
1219         } else if (sd_bus_error_is_set(error)) {
1220                 if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
1221                         sd_bus_reply_method_error(m, error);
1222         } else
1223                 return r;
1224
1225         log_debug("Failed to process message [type=%s sender=%s path=%s interface=%s member=%s signature=%s]: %s",
1226                   bus_message_type_to_string(m->header->type),
1227                   strna(m->sender),
1228                   strna(m->path),
1229                   strna(m->interface),
1230                   strna(m->member),
1231                   strna(m->root_container.signature),
1232                   bus_error_message(error, r));
1233
1234         return 1;
1235 }
1236
1237 int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) {
1238         const char *eq, *field;
1239         int r;
1240
1241         assert(m);
1242         assert(assignment);
1243
1244         eq = strchr(assignment, '=');
1245         if (!eq) {
1246                 log_error("Not an assignment: %s", assignment);
1247                 return -EINVAL;
1248         }
1249
1250         field = strndupa(assignment, eq - assignment);
1251         eq ++;
1252
1253         if (streq(field, "CPUQuota")) {
1254
1255                 if (isempty(eq)) {
1256
1257                         r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec");
1258                         if (r < 0)
1259                                 return bus_log_create_error(r);
1260
1261                         r = sd_bus_message_append(m, "v", "t", USEC_INFINITY);
1262
1263                 } else if (endswith(eq, "%")) {
1264                         double percent;
1265
1266                         if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) {
1267                                 log_error("CPU quota '%s' invalid.", eq);
1268                                 return -EINVAL;
1269                         }
1270
1271                         r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec");
1272                         if (r < 0)
1273                                 return bus_log_create_error(r);
1274
1275                         r = sd_bus_message_append(m, "v", "t", (usec_t) percent * USEC_PER_SEC / 100);
1276                 } else {
1277                         log_error("CPU quota needs to be in percent.");
1278                         return -EINVAL;
1279                 }
1280
1281                 if (r < 0)
1282                         return bus_log_create_error(r);
1283
1284                 return 0;
1285         }
1286
1287         r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1288         if (r < 0)
1289                 return bus_log_create_error(r);
1290
1291         if (STR_IN_SET(field,
1292                        "CPUAccounting", "MemoryAccounting", "BlockIOAccounting",
1293                        "SendSIGHUP", "SendSIGKILL")) {
1294
1295                 r = parse_boolean(eq);
1296                 if (r < 0) {
1297                         log_error("Failed to parse boolean assignment %s.", assignment);
1298                         return -EINVAL;
1299                 }
1300
1301                 r = sd_bus_message_append(m, "v", "b", r);
1302
1303         } else if (streq(field, "MemoryLimit")) {
1304                 off_t bytes;
1305
1306                 r = parse_size(eq, 1024, &bytes);
1307                 if (r < 0) {
1308                         log_error("Failed to parse bytes specification %s", assignment);
1309                         return -EINVAL;
1310                 }
1311
1312                 r = sd_bus_message_append(m, "v", "t", (uint64_t) bytes);
1313
1314         } else if (STR_IN_SET(field, "CPUShares", "BlockIOWeight")) {
1315                 uint64_t u;
1316
1317                 r = safe_atou64(eq, &u);
1318                 if (r < 0) {
1319                         log_error("Failed to parse %s value %s.", field, eq);
1320                         return -EINVAL;
1321                 }
1322
1323                 r = sd_bus_message_append(m, "v", "t", u);
1324
1325         } else if (STR_IN_SET(field, "User", "Group", "DevicePolicy", "KillMode"))
1326                 r = sd_bus_message_append(m, "v", "s", eq);
1327
1328         else if (streq(field, "DeviceAllow")) {
1329
1330                 if (isempty(eq))
1331                         r = sd_bus_message_append(m, "v", "a(ss)", 0);
1332                 else {
1333                         const char *path, *rwm, *e;
1334
1335                         e = strchr(eq, ' ');
1336                         if (e) {
1337                                 path = strndupa(eq, e - eq);
1338                                 rwm = e+1;
1339                         } else {
1340                                 path = eq;
1341                                 rwm = "";
1342                         }
1343
1344                         if (!path_startswith(path, "/dev")) {
1345                                 log_error("%s is not a device file in /dev.", path);
1346                                 return -EINVAL;
1347                         }
1348
1349                         r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm);
1350                 }
1351
1352         } else if (STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
1353
1354                 if (isempty(eq))
1355                         r = sd_bus_message_append(m, "v", "a(st)", 0);
1356                 else {
1357                         const char *path, *bandwidth, *e;
1358                         off_t bytes;
1359
1360                         e = strchr(eq, ' ');
1361                         if (e) {
1362                                 path = strndupa(eq, e - eq);
1363                                 bandwidth = e+1;
1364                         } else {
1365                                 log_error("Failed to parse %s value %s.", field, eq);
1366                                 return -EINVAL;
1367                         }
1368
1369                         if (!path_startswith(path, "/dev")) {
1370                                 log_error("%s is not a device file in /dev.", path);
1371                                 return -EINVAL;
1372                         }
1373
1374                         r = parse_size(bandwidth, 1000, &bytes);
1375                         if (r < 0) {
1376                                 log_error("Failed to parse byte value %s.", bandwidth);
1377                                 return -EINVAL;
1378                         }
1379
1380                         r = sd_bus_message_append(m, "v", "a(st)", 1, path, (uint64_t) bytes);
1381                 }
1382
1383         } else if (streq(field, "BlockIODeviceWeight")) {
1384
1385                 if (isempty(eq))
1386                         r = sd_bus_message_append(m, "v", "a(st)", 0);
1387                 else {
1388                         const char *path, *weight, *e;
1389                         uint64_t u;
1390
1391                         e = strchr(eq, ' ');
1392                         if (e) {
1393                                 path = strndupa(eq, e - eq);
1394                                 weight = e+1;
1395                         } else {
1396                                 log_error("Failed to parse %s value %s.", field, eq);
1397                                 return -EINVAL;
1398                         }
1399
1400                         if (!path_startswith(path, "/dev")) {
1401                                 log_error("%s is not a device file in /dev.", path);
1402                                 return -EINVAL;
1403                         }
1404
1405                         r = safe_atou64(weight, &u);
1406                         if (r < 0) {
1407                                 log_error("Failed to parse %s value %s.", field, weight);
1408                                 return -EINVAL;
1409                         }
1410                         r = sd_bus_message_append(m, "v", "a(st)", path, u);
1411                 }
1412
1413         } else if (rlimit_from_string(field) >= 0) {
1414                 uint64_t rl;
1415
1416                 if (streq(eq, "infinity"))
1417                         rl = (uint64_t) -1;
1418                 else {
1419                         r = safe_atou64(eq, &rl);
1420                         if (r < 0) {
1421                                 log_error("Invalid resource limit: %s", eq);
1422                                 return -EINVAL;
1423                         }
1424                 }
1425
1426                 r = sd_bus_message_append(m, "v", "t", rl);
1427
1428         } else if (streq(field, "Nice")) {
1429                 int32_t i;
1430
1431                 r = safe_atoi32(eq, &i);
1432                 if (r < 0) {
1433                         log_error("Failed to parse %s value %s.", field, eq);
1434                         return -EINVAL;
1435                 }
1436
1437                 r = sd_bus_message_append(m, "v", "i", i);
1438
1439         } else if (streq(field, "Environment")) {
1440
1441                 r = sd_bus_message_append(m, "v", "as", 1, eq);
1442
1443         } else if (streq(field, "KillSignal")) {
1444                 int sig;
1445
1446                 sig = signal_from_string_try_harder(eq);
1447                 if (sig < 0) {
1448                         log_error("Failed to parse %s value %s.", field, eq);
1449                         return -EINVAL;
1450                 }
1451
1452                 r = sd_bus_message_append(m, "v", "i", sig);
1453
1454         } else {
1455                 log_error("Unknown assignment %s.", assignment);
1456                 return -EINVAL;
1457         }
1458
1459         if (r < 0)
1460                 return bus_log_create_error(r);
1461
1462         return 0;
1463 }