chiark / gitweb /
f988cc7c59e6dda8eec049a3950b40c4f6dbbc22
[elogind.git] / src / libsystemd-bus / bus-control.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 #ifdef HAVE_VALGRIND_MEMCHECK_H
23 #include <valgrind/memcheck.h>
24 #endif
25
26 #include <stddef.h>
27 #include <errno.h>
28
29 #include "strv.h"
30
31 #include "sd-bus.h"
32 #include "bus-internal.h"
33 #include "bus-message.h"
34 #include "bus-control.h"
35 #include "bus-bloom.h"
36 #include "bus-util.h"
37
38 _public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) {
39         int r;
40
41         if (!bus)
42                 return -EINVAL;
43         if (!unique)
44                 return -EINVAL;
45         if (bus_pid_changed(bus))
46                 return -ECHILD;
47
48         r = bus_ensure_running(bus);
49         if (r < 0)
50                 return r;
51
52         *unique = bus->unique_name;
53         return 0;
54 }
55
56 _public_ int sd_bus_request_name(sd_bus *bus, const char *name, int flags) {
57         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
58         uint32_t ret;
59         int r;
60
61         if (!bus)
62                 return -EINVAL;
63         if (!name)
64                 return -EINVAL;
65         if (!bus->bus_client)
66                 return -EINVAL;
67         if (!BUS_IS_OPEN(bus->state))
68                 return -ENOTCONN;
69         if (bus_pid_changed(bus))
70                 return -ECHILD;
71
72         if (bus->is_kernel) {
73                 struct kdbus_cmd_name *n;
74                 size_t l;
75
76                 l = strlen(name);
77                 n = alloca0(offsetof(struct kdbus_cmd_name, name) + l + 1);
78                 n->size = offsetof(struct kdbus_cmd_name, name) + l + 1;
79                 n->flags = flags;
80                 memcpy(n->name, name, l+1);
81
82 #ifdef HAVE_VALGRIND_MEMCHECK_H
83                 VALGRIND_MAKE_MEM_DEFINED(n, n->size);
84 #endif
85
86                 r = ioctl(bus->input_fd, KDBUS_CMD_NAME_ACQUIRE, n);
87                 if (r < 0)
88                         return -errno;
89
90                 return n->flags;
91         } else {
92                 r = sd_bus_call_method(
93                                 bus,
94                                 "org.freedesktop.DBus",
95                                 "/",
96                                 "org.freedesktop.DBus",
97                                 "RequestName",
98                                 NULL,
99                                 &reply,
100                                 "su",
101                                 name,
102                                 flags);
103                 if (r < 0)
104                         return r;
105
106                 r = sd_bus_message_read(reply, "u", &ret);
107                 if (r < 0)
108                         return r;
109
110                 return ret;
111         }
112 }
113
114 _public_ int sd_bus_release_name(sd_bus *bus, const char *name) {
115         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
116         uint32_t ret;
117         int r;
118
119         if (!bus)
120                 return -EINVAL;
121         if (!name)
122                 return -EINVAL;
123         if (!bus->bus_client)
124                 return -EINVAL;
125         if (!BUS_IS_OPEN(bus->state))
126                 return -ENOTCONN;
127         if (bus_pid_changed(bus))
128                 return -ECHILD;
129
130         if (bus->is_kernel) {
131                 struct kdbus_cmd_name *n;
132                 size_t l;
133
134                 l = strlen(name);
135                 n = alloca0(offsetof(struct kdbus_cmd_name, name) + l + 1);
136                 n->size = offsetof(struct kdbus_cmd_name, name) + l + 1;
137                 memcpy(n->name, name, l+1);
138
139 #ifdef HAVE_VALGRIND_MEMCHECK_H
140                 VALGRIND_MAKE_MEM_DEFINED(n, n->size);
141 #endif
142                 r = ioctl(bus->input_fd, KDBUS_CMD_NAME_RELEASE, n);
143                 if (r < 0)
144                         return -errno;
145
146                 return n->flags;
147         } else {
148                 r = sd_bus_call_method(
149                                 bus,
150                                 "org.freedesktop.DBus",
151                                 "/",
152                                 "org.freedesktop.DBus",
153                                 "ReleaseName",
154                                 NULL,
155                                 &reply,
156                                 "s",
157                                 name);
158                 if (r < 0)
159                         return r;
160
161                 r = sd_bus_message_read(reply, "u", &ret);
162                 if (r < 0)
163                         return r;
164         }
165
166         return ret;
167 }
168
169 _public_ int sd_bus_list_names(sd_bus *bus, char ***l) {
170         _cleanup_bus_message_unref_ sd_bus_message *reply1 = NULL, *reply2 = NULL;
171         char **x = NULL;
172         int r;
173
174         if (!bus)
175                 return -EINVAL;
176         if (!l)
177                 return -EINVAL;
178         if (!BUS_IS_OPEN(bus->state))
179                 return -ENOTCONN;
180         if (bus_pid_changed(bus))
181                 return -ECHILD;
182
183         if (bus->is_kernel) {
184                 _cleanup_free_ struct kdbus_cmd_names *names = NULL;
185                 struct kdbus_cmd_name *name;
186                 size_t size;
187
188                 /* assume 8k size first. If that doesn't suffice, kdbus will tell us
189                  * how big the buffer needs to be.  */
190                 size = 8192;
191
192                 for(;;) {
193                         names = realloc(names, size);
194                         if (!names)
195                                 return -ENOMEM;
196
197                         names->size = size;
198
199                         r = ioctl(sd_bus_get_fd(bus), KDBUS_CMD_NAME_LIST, names);
200                         if (r < 0) {
201                                 if (errno == ENOBUFS && size != names->size) {
202                                         size = names->size;
203                                         continue;
204                                 }
205
206                                 return -errno;
207                         }
208
209                         break;
210                 }
211
212                 KDBUS_PART_FOREACH(name, names, names) {
213                         r = strv_extend(&x, name->name);
214                         if (r < 0)
215                                 return -ENOMEM;
216                 }
217
218                 *l = x;
219         } else {
220                 r = sd_bus_call_method(
221                                 bus,
222                                 "org.freedesktop.DBus",
223                                 "/",
224                                 "org.freedesktop.DBus",
225                                 "ListNames",
226                                 NULL,
227                                 &reply1,
228                                 NULL);
229                 if (r < 0)
230                         return r;
231
232                 r = sd_bus_call_method(
233                                 bus,
234                                 "org.freedesktop.DBus",
235                                 "/",
236                                 "org.freedesktop.DBus",
237                                 "ListActivatableNames",
238                                 NULL,
239                                 &reply2,
240                                 NULL);
241                 if (r < 0)
242                         return r;
243
244                 r = bus_message_read_strv_extend(reply1, &x);
245                 if (r < 0) {
246                         strv_free(x);
247                         return r;
248                 }
249
250                 r = bus_message_read_strv_extend(reply2, &x);
251                 if (r < 0) {
252                         strv_free(x);
253                         return r;
254                 }
255
256                 *l = strv_uniq(x);
257         }
258
259         return 0;
260 }
261
262 _public_ int sd_bus_get_owner(
263                 sd_bus *bus,
264                 const char *name,
265                 uint64_t mask,
266                 char **owner,
267                 sd_bus_creds **creds) {
268
269         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
270         _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL;
271         _cleanup_free_ char *unique = NULL;
272         pid_t pid = 0;
273         int r;
274
275         assert_return(bus, -EINVAL);
276         assert_return(name, -EINVAL);
277         assert_return(mask <= _SD_BUS_CREDS_MAX, -ENOTSUP);
278         assert_return(mask == 0 || creds, -EINVAL);
279         assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
280         assert_return(!bus_pid_changed(bus), -ECHILD);
281
282         /* Only query the owner if the caller wants to know it or if
283          * the caller just wants to check whether a name exists */
284         if (owner || mask == 0) {
285                 const char *found;
286
287                 r = sd_bus_call_method(
288                                 bus,
289                                 "org.freedesktop.DBus",
290                                 "/",
291                                 "org.freedesktop.DBus",
292                                 "GetNameOwner",
293                                 NULL,
294                                 &reply,
295                                 "s",
296                                 name);
297                 if (r < 0)
298                         return r;
299
300                 r = sd_bus_message_read(reply, "s", &found);
301                 if (r < 0)
302                         return r;
303
304                 unique = strdup(found);
305                 if (!unique)
306                         return -ENOMEM;
307
308                 reply = sd_bus_message_unref(reply);
309         }
310
311         if (mask != 0) {
312                 c = bus_creds_new();
313                 if (!c)
314                         return -ENOMEM;
315
316                 if ((mask & SD_BUS_CREDS_PID) ||
317                     mask & ~(SD_BUS_CREDS_PID|SD_BUS_CREDS_UID|SD_BUS_CREDS_SELINUX_CONTEXT)) {
318                         uint32_t u;
319
320                         r = sd_bus_call_method(
321                                         bus,
322                                         "org.freedesktop.DBus",
323                                         "/",
324                                         "org.freedesktop.DBus",
325                                         "GetConnectionUnixProcessID",
326                                         NULL,
327                                         &reply,
328                                         "s",
329                                         name);
330                         if (r < 0)
331                                 return r;
332
333                         r = sd_bus_message_read(reply, "u", &u);
334                         if (r < 0)
335                                 return r;
336
337                         pid = u;
338                         if (mask & SD_BUS_CREDS_PID) {
339                                 c->pid = u;
340                                 c->mask |= SD_BUS_CREDS_PID;
341                         }
342
343                         reply = sd_bus_message_unref(reply);
344                 }
345
346                 if (mask & SD_BUS_CREDS_UID) {
347                         uint32_t u;
348
349                         r = sd_bus_call_method(
350                                         bus,
351                                         "org.freedesktop.DBus",
352                                         "/",
353                                         "org.freedesktop.DBus",
354                                         "GetConnectionUnixUser",
355                                         NULL,
356                                         &reply,
357                                         "s",
358                                         name);
359                         if (r < 0)
360                                 return r;
361
362                         r = sd_bus_message_read(reply, "u", &u);
363                         if (r < 0)
364                                 return r;
365
366                         c->uid = u;
367                         c->mask |= SD_BUS_CREDS_UID;
368
369                         reply = sd_bus_message_unref(reply);
370                 }
371
372                 if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) {
373                         const void *p;
374                         size_t sz;
375
376                         r = sd_bus_call_method(
377                                         bus,
378                                         "org.freedesktop.DBus",
379                                         "/",
380                                         "org.freedesktop.DBus",
381                                         "GetConnectionSELinuxSecurityContext",
382                                         NULL,
383                                         &reply,
384                                         "s",
385                                         name);
386                         if (r < 0)
387                                 return r;
388
389                         r = sd_bus_message_read_array(reply, 'y', &p, &sz);
390                         if (r < 0)
391                                 return r;
392
393                         c->label = strndup(p, sz);
394                         if (!c->label)
395                                 return -ENOMEM;
396
397                         c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
398                 }
399
400                 r = bus_creds_add_more(c, mask, pid, 0);
401                 if (r < 0)
402                         return r;
403         }
404
405         if (creds) {
406                 *creds = c;
407                 c = NULL;
408         }
409
410         if (owner) {
411                 *owner = unique;
412                 unique = NULL;
413         }
414
415         return 0;
416 }
417
418 int bus_add_match_internal(
419                 sd_bus *bus,
420                 const char *match,
421                 struct bus_match_component *components,
422                 unsigned n_components,
423                 uint64_t cookie) {
424
425         int r;
426
427         assert(bus);
428         assert(match);
429
430         if (bus->is_kernel) {
431                 struct kdbus_cmd_match *m;
432                 struct kdbus_item *item;
433                 uint64_t bloom[BLOOM_SIZE/8];
434                 size_t sz;
435                 const char *sender = NULL;
436                 size_t sender_length = 0;
437                 uint64_t src_id = KDBUS_MATCH_SRC_ID_ANY;
438                 bool using_bloom = false;
439                 unsigned i;
440
441                 zero(bloom);
442
443                 sz = offsetof(struct kdbus_cmd_match, items);
444
445                 for (i = 0; i < n_components; i++) {
446                         struct bus_match_component *c = &components[i];
447
448                         switch (c->type) {
449
450                         case BUS_MATCH_SENDER:
451                                 r = bus_kernel_parse_unique_name(c->value_str, &src_id);
452                                 if (r < 0)
453                                         return r;
454
455                                 if (r > 0) {
456                                         sender = c->value_str;
457                                         sender_length = strlen(sender);
458                                         sz += ALIGN8(offsetof(struct kdbus_item, str) + sender_length + 1);
459                                 }
460
461                                 break;
462
463                         case BUS_MATCH_MESSAGE_TYPE:
464                                 bloom_add_pair(bloom, "message-type", bus_message_type_to_string(c->value_u8));
465                                 using_bloom = true;
466                                 break;
467
468                         case BUS_MATCH_INTERFACE:
469                                 bloom_add_pair(bloom, "interface", c->value_str);
470                                 using_bloom = true;
471                                 break;
472
473                         case BUS_MATCH_MEMBER:
474                                 bloom_add_pair(bloom, "member", c->value_str);
475                                 using_bloom = true;
476                                 break;
477
478                         case BUS_MATCH_PATH:
479                                 bloom_add_pair(bloom, "path", c->value_str);
480                                 using_bloom = true;
481                                 break;
482
483                         case BUS_MATCH_PATH_NAMESPACE:
484                                 if (!streq(c->value_str, "/")) {
485                                         bloom_add_pair(bloom, "path-slash-prefix", c->value_str);
486                                         using_bloom = true;
487                                 }
488                                 break;
489
490                         case BUS_MATCH_ARG...BUS_MATCH_ARG_LAST: {
491                                 char buf[sizeof("arg")-1 + 2 + 1];
492
493                                 snprintf(buf, sizeof(buf), "arg%u", c->type - BUS_MATCH_ARG);
494                                 bloom_add_pair(bloom, buf, c->value_str);
495                                 using_bloom = true;
496                                 break;
497                         }
498
499                         case BUS_MATCH_ARG_PATH...BUS_MATCH_ARG_PATH_LAST: {
500                                 char buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")];
501
502                                 snprintf(buf, sizeof(buf), "arg%u-slash-prefix", c->type - BUS_MATCH_ARG_PATH);
503                                 bloom_add_pair(bloom, buf, c->value_str);
504                                 using_bloom = true;
505                                 break;
506                         }
507
508                         case BUS_MATCH_ARG_NAMESPACE...BUS_MATCH_ARG_NAMESPACE_LAST: {
509                                 char buf[sizeof("arg")-1 + 2 + sizeof("-dot-prefix")];
510
511                                 snprintf(buf, sizeof(buf), "arg%u-dot-prefix", c->type - BUS_MATCH_ARG_NAMESPACE);
512                                 bloom_add_pair(bloom, buf, c->value_str);
513                                 using_bloom = true;
514                                 break;
515                         }
516
517                         case BUS_MATCH_DESTINATION:
518                                 /* The bloom filter does not include
519                                    the destination, since it is only
520                                    available for broadcast messages
521                                    which do not carry a destination
522                                    since they are undirected. */
523                                 break;
524
525                         case BUS_MATCH_ROOT:
526                         case BUS_MATCH_VALUE:
527                         case BUS_MATCH_LEAF:
528                         case _BUS_MATCH_NODE_TYPE_MAX:
529                         case _BUS_MATCH_NODE_TYPE_INVALID:
530                                 assert_not_reached("Invalid match type?");
531                         }
532                 }
533
534                 if (using_bloom)
535                         sz += ALIGN8(offsetof(struct kdbus_item, data64) + BLOOM_SIZE);
536
537                 m = alloca0(sz);
538                 m->size = sz;
539                 m->cookie = cookie;
540                 m->src_id = src_id;
541
542                 item = m->items;
543
544                 if (using_bloom) {
545                         item->size = offsetof(struct kdbus_item, data64) + BLOOM_SIZE;
546                         item->type = KDBUS_MATCH_BLOOM;
547                         memcpy(item->data64, bloom, BLOOM_SIZE);
548
549                         item = KDBUS_PART_NEXT(item);
550                 }
551
552                 if (sender) {
553                         item->size = offsetof(struct kdbus_item, str) + sender_length + 1;
554                         item->type = KDBUS_MATCH_SRC_NAME;
555                         memcpy(item->str, sender, sender_length + 1);
556                 }
557
558                 r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
559                 if (r < 0)
560                         return -errno;
561
562         } else {
563                 return sd_bus_call_method(
564                                 bus,
565                                 "org.freedesktop.DBus",
566                                 "/",
567                                 "org.freedesktop.DBus",
568                                 "AddMatch",
569                                 NULL,
570                                 NULL,
571                                 "s",
572                                 match);
573         }
574
575         return 0;
576 }
577
578 int bus_remove_match_internal(
579                 sd_bus *bus,
580                 const char *match,
581                 uint64_t cookie) {
582
583         int r;
584
585         assert(bus);
586         assert(match);
587
588         if (bus->is_kernel) {
589                 struct kdbus_cmd_match m;
590
591                 zero(m);
592                 m.size = offsetof(struct kdbus_cmd_match, items);
593                 m.cookie = cookie;
594
595                 r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_REMOVE, &m);
596                 if (r < 0)
597                         return -errno;
598
599         } else {
600                 return sd_bus_call_method(
601                                 bus,
602                                 "org.freedesktop.DBus",
603                                 "/",
604                                 "org.freedesktop.DBus",
605                                 "RemoveMatch",
606                                 NULL,
607                                 NULL,
608                                 "s",
609                                 match);
610         }
611
612         return 0;
613 }
614
615 _public_ int sd_bus_get_owner_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) {
616         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
617         const char *mid;
618         int r;
619
620         assert_return(bus, -EINVAL);
621         assert_return(name, -EINVAL);
622         assert_return(machine, -EINVAL);
623         assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
624         assert_return(!bus_pid_changed(bus), -ECHILD);
625
626         if (streq_ptr(name, bus->unique_name))
627                 return sd_id128_get_machine(machine);
628
629         r = sd_bus_message_new_method_call(
630                         bus,
631                         name,
632                         "/",
633                         "org.freedesktop.DBus.Peer",
634                         "GetMachineId", &m);
635         if (r < 0)
636                 return r;
637
638         r = sd_bus_message_set_no_auto_start(m, true);
639         if (r < 0)
640                 return r;
641
642         r = sd_bus_call(bus, m, 0, NULL, &reply);
643         if (r < 0)
644                 return r;
645
646         r = sd_bus_message_read(reply, "s", &mid);
647         if (r < 0)
648                 return r;
649
650         return sd_id128_from_string(mid, machine);
651 }