chiark / gitweb /
bus: use internal helper to read org.freedesktop.DBus.Properties::GetAll variables
[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
24 #include "util.h"
25 #include "strv.h"
26 #include "macro.h"
27 #include "def.h"
28
29 #include "sd-event.h"
30 #include "sd-bus.h"
31 #include "bus-error.h"
32 #include "bus-message.h"
33
34 #include "bus-util.h"
35
36 static int quit_callback(sd_bus *bus, sd_bus_message *m, void *userdata) {
37         sd_event *e = userdata;
38
39         assert(bus);
40         assert(m);
41         assert(e);
42
43         sd_event_request_quit(e);
44         return 1;
45 }
46
47 int bus_async_unregister_and_quit(sd_event *e, sd_bus *bus, const char *name) {
48         _cleanup_free_ char *match = NULL;
49         int r;
50
51         assert(e);
52         assert(bus);
53         assert(name);
54
55         r = asprintf(&match, "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameLost',arg0='%s'", name);
56         if (r < 0)
57                 return r;
58
59         r = sd_bus_add_match(bus, match, quit_callback, e);
60         if (r < 0)
61                 return r;
62
63         r = sd_bus_release_name(bus, name);
64         if (r < 0)
65                 return r;
66
67         if (r != SD_BUS_NAME_RELEASED)
68                 return -EIO;
69
70         return 0;
71 }
72
73 int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t timeout) {
74         bool exiting = false;
75         int r;
76
77         assert(e);
78         assert(bus);
79         assert(name);
80
81         for (;;) {
82                 r = sd_event_get_state(e);
83                 if (r < 0)
84                         return r;
85
86                 if (r == SD_EVENT_FINISHED)
87                         break;
88
89                 r = sd_event_run(e, exiting ? (uint64_t) -1 : timeout);
90                 if (r < 0)
91                         return r;
92
93                 if (r == 0 && !exiting) {
94                         r = bus_async_unregister_and_quit(e, bus, name);
95                         if (r < 0)
96                                 return r;
97
98                         exiting = true;
99                 }
100         }
101
102         return 0;
103 }
104
105 int bus_property_get_tristate(
106                 sd_bus *bus,
107                 const char *path,
108                 const char *interface,
109                 const char *property,
110                 sd_bus_message *reply,
111                 sd_bus_error *error,
112                 void *userdata) {
113
114         int *tristate = userdata;
115         int r;
116
117         r = sd_bus_message_append(reply, "b", *tristate > 0);
118         if (r < 0)
119                 return r;
120
121         return 1;
122 }
123
124 int bus_verify_polkit(
125                 sd_bus *bus,
126                 sd_bus_message *m,
127                 const char *action,
128                 bool interactive,
129                 bool *_challenge,
130                 sd_bus_error *e) {
131
132         const char *sender;
133         uid_t uid;
134         int r;
135
136         assert(bus);
137         assert(m);
138         assert(action);
139
140         sender = sd_bus_message_get_sender(m);
141         if (!sender)
142                 return -EBADMSG;
143
144         r = sd_bus_get_owner_uid(bus, sender, &uid);
145         if (r < 0)
146                 return r;
147
148         if (uid == 0)
149                 return 1;
150
151 #ifdef ENABLE_POLKIT
152         else {
153                 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
154                 unsigned authorized = false, challenge = false;
155
156                 r = sd_bus_call_method(
157                                 bus,
158                                 "org.freedesktop.PolicyKit1",
159                                 "/org/freedesktop/PolicyKit1/Authority",
160                                 "org.freedesktop.PolicyKit1.Authority",
161                                 "CheckAuthorization",
162                                 e,
163                                 &reply,
164                                 "(sa{sv})sa{ss}us",
165                                 "system-bus-name", 1, "name", "s", sender,
166                                 action,
167                                 0,
168                                 interactive ? 1 : 0,
169                                 "");
170
171                 if (r < 0) {
172                         /* Treat no PK available as access denied */
173                         if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
174                                 sd_bus_error_free(e);
175                                 return -EACCES;
176                         }
177
178                         return r;
179                 }
180
181                 r = sd_bus_message_read(reply, "(bb)", &authorized, &challenge);
182                 if (r < 0)
183                         return r;
184
185                 if (authorized)
186                         return 1;
187
188                 if (_challenge) {
189                         *_challenge = challenge;
190                         return 0;
191                 }
192         }
193 #endif
194
195         return -EACCES;
196 }
197
198 #ifdef ENABLE_POLKIT
199
200 typedef struct AsyncPolkitQuery {
201         sd_bus_message *request, *reply;
202         sd_bus_message_handler_t callback;
203         void *userdata;
204         uint64_t serial;
205 } AsyncPolkitQuery;
206
207 static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userdata) {
208         AsyncPolkitQuery *q = userdata;
209         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
210         int r;
211
212         assert(bus);
213         assert(reply);
214         assert(q);
215
216         q->reply = sd_bus_message_ref(reply);
217         q->serial = 0;
218
219         m = sd_bus_message_ref(q->request);
220
221         r = sd_bus_message_rewind(m, true);
222         if (r < 0)
223                 return r;
224
225         r = q->callback(bus, m, q->userdata);
226         if (r < 0)
227                 return r;
228
229         return 1;
230 }
231
232 static void async_polkit_query_free(sd_bus *b, AsyncPolkitQuery *q) {
233
234         if (!q)
235                 return;
236
237         if (q->serial >  0 && b)
238                 sd_bus_send_with_reply_cancel(b, q->serial);
239
240         sd_bus_message_unref(q->request);
241         sd_bus_message_unref(q->reply);
242         free(q);
243 }
244
245 #endif
246
247 int bus_verify_polkit_async(
248                 sd_bus *bus,
249                 Hashmap **registry,
250                 sd_bus_message *m,
251                 const char *action,
252                 bool interactive,
253                 sd_bus_error *error,
254                 sd_bus_message_handler_t callback,
255                 void *userdata) {
256
257 #ifdef ENABLE_POLKIT
258         _cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
259         AsyncPolkitQuery *q;
260 #endif
261         const char *sender;
262         uid_t uid;
263         int r;
264
265         assert(bus);
266         assert(registry);
267         assert(m);
268         assert(action);
269
270 #ifdef ENABLE_POLKIT
271         q = hashmap_remove(*registry, m);
272         if (q) {
273                 unsigned authorized, challenge;
274
275                 /* This is the second invocation of this function, and
276                  * there's already a response from polkit, let's
277                  * process it */
278                 assert(q->reply);
279
280                 if (sd_bus_message_is_method_error(q->reply, NULL)) {
281                         const sd_bus_error *e;
282
283                         /* Treat no PK available as access denied */
284                         if (sd_bus_message_is_method_error(q->reply, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
285                                 async_polkit_query_free(bus, q);
286                                 return -EACCES;
287                         }
288
289                         e = sd_bus_message_get_error(q->reply);
290                         sd_bus_error_copy(error, e);
291                         r = sd_bus_error_get_errno(e);
292
293                         async_polkit_query_free(bus, q);
294                         return r;
295                 }
296
297                 r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
298                 if (r >= 0)
299                         r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
300
301                 async_polkit_query_free(bus, q);
302
303                 if (r < 0)
304                         return r;
305
306                 if (authorized)
307                         return 1;
308
309                 return -EACCES;
310         }
311 #endif
312
313         sender = sd_bus_message_get_sender(m);
314         if (!sender)
315                 return -EBADMSG;
316
317         r = sd_bus_get_owner_uid(bus, sender, &uid);
318         if (r < 0)
319                 return r;
320
321         if (uid == 0)
322                 return 1;
323 #ifdef ENABLE_POLKIT
324
325         r = hashmap_ensure_allocated(registry, trivial_hash_func, trivial_compare_func);
326         if (r < 0)
327                 return r;
328
329         r = sd_bus_message_new_method_call(
330                         bus,
331                         "org.freedesktop.PolicyKit1",
332                         "/org/freedesktop/PolicyKit1/Authority",
333                         "org.freedesktop.PolicyKit1.Authority",
334                         "CheckAuthorization",
335                         &pk);
336         if (r < 0)
337                 return r;
338
339         r = sd_bus_message_append(
340                         pk,
341                         "(sa{sv})sa{ss}us",
342                         "system-bus-name", 1, "name", "s", sender,
343                         action,
344                         0,
345                         interactive ? 1 : 0,
346                         "");
347         if (r < 0)
348                 return r;
349
350         q = new0(AsyncPolkitQuery, 1);
351         if (!q)
352                 return -ENOMEM;
353
354         q->request = sd_bus_message_ref(m);
355         q->callback = callback;
356         q->userdata = userdata;
357
358         r = hashmap_put(*registry, m, q);
359         if (r < 0) {
360                 async_polkit_query_free(bus, q);
361                 return r;
362         }
363
364         r = sd_bus_send_with_reply(bus, pk, async_polkit_callback, q, 0, &q->serial);
365         if (r < 0)
366                 return r;
367
368         return 0;
369 #endif
370
371         return -EACCES;
372 }
373
374 void bus_verify_polkit_async_registry_free(sd_bus *bus, Hashmap *registry) {
375 #ifdef ENABLE_POLKIT
376         AsyncPolkitQuery *q;
377
378         while ((q = hashmap_steal_first(registry)))
379                 async_polkit_query_free(bus, q);
380
381         hashmap_free(registry);
382 #endif
383 }
384
385 static int bus_check_peercred(sd_bus *c) {
386         struct ucred ucred;
387         socklen_t l;
388         int fd;
389
390         assert(c);
391
392         fd = sd_bus_get_fd(c);
393         if (fd < 0)
394                 return fd;
395
396         l = sizeof(struct ucred);
397         if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0)
398                 return -errno;
399
400         if (l != sizeof(struct ucred))
401                 return -E2BIG;
402
403         if (ucred.uid != 0 && ucred.uid != geteuid())
404                 return -EPERM;
405
406         return 1;
407 }
408
409 int bus_open_system_systemd(sd_bus **_bus) {
410         _cleanup_bus_unref_ sd_bus *bus = NULL;
411         int r;
412
413         assert(_bus);
414
415         if (geteuid() != 0)
416                 return sd_bus_open_system(_bus);
417
418         /* If we are root, then let's talk directly to the system
419          * instance, instead of going via the bus */
420
421         r = sd_bus_new(&bus);
422         if (r < 0)
423                 return r;
424
425         r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
426         if (r < 0)
427                 return r;
428
429         r = sd_bus_start(bus);
430         if (r < 0)
431                 return r;
432
433         r = bus_check_peercred(bus);
434         if (r < 0)
435                 return r;
436
437         *_bus = bus;
438         bus = NULL;
439
440         return 0;
441 }
442
443 int bus_generic_print_property(const char *name, sd_bus_message *property, bool all) {
444         char type;
445         const char *contents;
446
447         assert(name);
448         assert(property);
449
450         sd_bus_message_peek_type(property, &type, &contents);
451
452         switch (type) {
453
454         case SD_BUS_TYPE_STRING: {
455                 const char *s;
456                 sd_bus_message_read_basic(property, type, &s);
457
458                 if (all || !isempty(s))
459                         printf("%s=%s\n", name, s);
460
461                 return 1;
462         }
463
464         case SD_BUS_TYPE_BOOLEAN: {
465                 bool b;
466
467                 sd_bus_message_read_basic(property, type, &b);
468                 printf("%s=%s\n", name, yes_no(b));
469
470                 return 1;
471         }
472
473         case SD_BUS_TYPE_UINT64: {
474                 uint64_t u;
475
476                 sd_bus_message_read_basic(property, type, &u);
477
478                 /* Yes, heuristics! But we can change this check
479                  * should it turn out to not be sufficient */
480
481                 if (endswith(name, "Timestamp")) {
482                         char timestamp[FORMAT_TIMESTAMP_MAX], *t;
483
484                         t = format_timestamp(timestamp, sizeof(timestamp), u);
485                         if (t || all)
486                                 printf("%s=%s\n", name, strempty(t));
487
488                 } else if (strstr(name, "USec")) {
489                         char timespan[FORMAT_TIMESPAN_MAX];
490
491                         printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
492                 } else
493                         printf("%s=%llu\n", name, (unsigned long long) u);
494
495                 return 1;
496         }
497
498         case SD_BUS_TYPE_UINT32: {
499                 uint32_t u;
500
501                 sd_bus_message_read_basic(property, type, &u);
502
503                 if (strstr(name, "UMask") || strstr(name, "Mode"))
504                         printf("%s=%04o\n", name, u);
505                 else
506                         printf("%s=%u\n", name, (unsigned) u);
507
508                 return 1;
509         }
510
511         case SD_BUS_TYPE_INT32: {
512                 int32_t i;
513
514                 sd_bus_message_read_basic(property, type, &i);
515
516                 printf("%s=%i\n", name, (int) i);
517                 return 1;
518         }
519
520         case SD_BUS_TYPE_DOUBLE: {
521                 double d;
522
523                 sd_bus_message_read_basic(property, type, &d);
524
525                 printf("%s=%g\n", name, d);
526                 return 1;
527         }
528
529         case SD_BUS_TYPE_ARRAY:
530
531                 if (streq(contents, "s")) {
532                         bool space = false;
533                         char tp;
534                         const char *cnt;
535
536                         sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
537
538                         sd_bus_message_peek_type(property, &tp, &cnt);
539                         if (all || cnt) {
540                                 const char *str;
541
542                                 printf("%s=", name);
543
544
545                                 while(sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str) > 0) {
546                                         printf("%s%s", space ? " " : "", str);
547
548                                         space = true;
549                                 }
550
551                                 puts("");
552                         }
553
554                         sd_bus_message_exit_container(property);
555
556                         return 1;
557
558                 } else if (streq(contents, "y")) {
559                         const uint8_t *u;
560                         size_t n;
561
562                         sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
563                         if (all || n > 0) {
564                                 unsigned int i;
565
566                                 printf("%s=", name);
567
568                                 for (i = 0; i < n; i++)
569                                         printf("%02x", u[i]);
570
571                                 puts("");
572                         }
573
574                         return 1;
575
576                 } else if (streq(contents, "u")) {
577                         uint32_t *u;
578                         size_t n;
579
580                         sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
581                         if (all || n > 0) {
582                                 unsigned int i;
583
584                                 printf("%s=", name);
585
586                                 for (i = 0; i < n; i++)
587                                         printf("%08x", u[i]);
588
589                                 puts("");
590                         }
591
592                         return 1;
593                 }
594
595                 break;
596         }
597
598         return 0;
599 }
600
601 int bus_map_all_properties(sd_bus *bus,
602                            const char *destination,
603                            const char *path,
604                            const struct bus_properties_map *map) {
605         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
606         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
607         int r;
608
609         r = sd_bus_call_method( bus,
610                         destination,
611                         path,
612                         "org.freedesktop.DBus.Properties",
613                         "GetAll",
614                         &error,
615                         &m,
616                         "s", "");
617         if (r < 0) {
618                 log_error("Could not get properties: %s", bus_error_message(&error, -r));
619                 return r;
620         }
621
622         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
623         if (r < 0)
624                 return r;
625
626         while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
627                 const char *name;
628                 char type;
629                 const char *contents;
630                 unsigned i;
631
632                 r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &name);
633                 if (r < 0)
634                         return r;
635
636                 r = sd_bus_message_peek_type(m, NULL, &contents);
637                 if (r < 0)
638                         return r;
639
640                 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
641                 if (r < 0)
642                         return r;
643
644                 r = sd_bus_message_peek_type(m, &type, &contents);
645                 if (r < 0) {
646                         log_error("Could not determine type of message: %s", strerror(-r));
647                         return r;
648                 }
649
650                 switch (type) {
651                 case SD_BUS_TYPE_STRING: {
652                         const char *s;
653
654                         sd_bus_message_read_basic(m, type, &s);
655                         if (isempty(s))
656                                 break;
657
658                         for (i = 0; map[i].type; i++) {
659                                 const char **p;
660
661                                 if (!streq(map[i].type, "s"))
662                                         continue;
663                                 if (!streq(map[i].name, name))
664                                         continue;
665
666                                 p = map[i].ptr;
667                                 *p = strdup(s);
668                                 if (!*p) {
669                                         r = -ENOMEM;
670                                         goto fail;
671                                 }
672                         }
673                         break;
674                 }
675
676                 case SD_BUS_TYPE_ARRAY: {
677                         _cleanup_strv_free_ char **l = NULL;
678
679                         if (!streq(contents, "s"))
680                                 break;
681
682                         for (i = 0; map[i].type; i++) {
683                                 char ***p;
684
685                                 if (!streq(map[i].type, "as"))
686                                         continue;
687                                 if (!streq(map[i].name, name))
688                                         continue;
689
690                                 r = bus_message_read_strv_extend(m, &l);
691                                 if (r < 0)
692                                         break;
693
694                                 p = map[i].ptr;
695                                 strv_free(*p);
696                                 *p = l;
697                                 l = NULL;
698                         }
699                         break;
700                 }
701
702                 case SD_BUS_TYPE_BOOLEAN: {
703                         unsigned b;
704
705                         sd_bus_message_read_basic(m, type, &b);
706
707                         for (i = 0; map[i].type; i++) {
708                                 bool *p;
709
710                                 if (!streq(map[i].type, "b"))
711                                         continue;
712                                 if (!streq(map[i].name, name))
713                                         continue;
714
715                                 p = map[i].ptr;
716                                 *p = b;
717                         }
718                         break;
719                 }
720
721                 case SD_BUS_TYPE_UINT64: {
722                         uint64_t t;
723
724                         sd_bus_message_read_basic(m, type, &t);
725
726                         for (i = 0; map[i].type; i++) {
727                                 uint64_t *p;
728
729                                 if (!streq(map[i].type, "t"))
730                                         continue;
731                                 if (!streq(map[i].name, name))
732                                         continue;
733
734                                 p = map[i].ptr;
735                                 *p = t;
736                         }
737                         break;
738                 }
739
740                 default:
741                         break;
742                 }
743
744                 r = sd_bus_message_exit_container(m);
745                 if (r < 0)
746                         return r;
747
748                 r = sd_bus_message_exit_container(m);
749                 if (r < 0)
750                         return r;
751         }
752
753 fail:
754         return r;
755 }
756
757 int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
758         int r;
759
760         assert(transport >= 0);
761         assert(transport < _BUS_TRANSPORT_MAX);
762         assert(bus);
763
764         assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
765         assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP);
766
767         switch (transport) {
768
769         case BUS_TRANSPORT_LOCAL:
770                 if (user)
771                         r = sd_bus_open_user(bus);
772                 else
773                         r = sd_bus_open_system(bus);
774
775                 break;
776
777         case BUS_TRANSPORT_REMOTE:
778                 r = sd_bus_open_system_remote(host, bus);
779                 break;
780
781         case BUS_TRANSPORT_CONTAINER:
782                 r = sd_bus_open_system_container(host, bus);
783                 break;
784
785         default:
786                 assert_not_reached("Hmm, unknown transport type.");
787         }
788
789         return r;
790 }