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