chiark / gitweb /
bus: calculate bloom filter for match
[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
37 int sd_bus_get_unique_name(sd_bus *bus, const char **unique) {
38         int r;
39
40         if (!bus)
41                 return -EINVAL;
42         if (!unique)
43                 return -EINVAL;
44         if (bus_pid_changed(bus))
45                 return -ECHILD;
46
47         r = bus_ensure_running(bus);
48         if (r < 0)
49                 return r;
50
51         *unique = bus->unique_name;
52         return 0;
53 }
54
55 int sd_bus_request_name(sd_bus *bus, const char *name, int flags) {
56         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
57         uint32_t ret;
58         int r;
59
60         if (!bus)
61                 return -EINVAL;
62         if (!name)
63                 return -EINVAL;
64         if (!bus->bus_client)
65                 return -EINVAL;
66         if (!BUS_IS_OPEN(bus->state))
67                 return -ENOTCONN;
68         if (bus_pid_changed(bus))
69                 return -ECHILD;
70
71         if (bus->is_kernel) {
72                 struct kdbus_cmd_name *n;
73                 size_t l;
74
75                 l = strlen(name);
76                 n = alloca0(offsetof(struct kdbus_cmd_name, name) + l + 1);
77                 n->size = offsetof(struct kdbus_cmd_name, name) + l + 1;
78                 n->name_flags = flags;
79                 memcpy(n->name, name, l+1);
80
81 #ifdef HAVE_VALGRIND_MEMCHECK_H
82                 VALGRIND_MAKE_MEM_DEFINED(n, n->size);
83 #endif
84
85                 r = ioctl(bus->input_fd, KDBUS_CMD_NAME_ACQUIRE, n);
86                 if (r < 0)
87                         return -errno;
88
89                 return n->name_flags;
90         } else {
91                 r = sd_bus_call_method(
92                                 bus,
93                                 "org.freedesktop.DBus",
94                                 "/",
95                                 "org.freedesktop.DBus",
96                                 "RequestName",
97                                 NULL,
98                                 &reply,
99                                 "su",
100                                 name,
101                                 flags);
102                 if (r < 0)
103                         return r;
104
105                 r = sd_bus_message_read(reply, "u", &ret);
106                 if (r < 0)
107                         return r;
108
109                 return ret;
110         }
111 }
112
113 int sd_bus_release_name(sd_bus *bus, const char *name) {
114         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
115         uint32_t ret;
116         int r;
117
118         if (!bus)
119                 return -EINVAL;
120         if (!name)
121                 return -EINVAL;
122         if (!bus->bus_client)
123                 return -EINVAL;
124         if (!BUS_IS_OPEN(bus->state))
125                 return -ENOTCONN;
126         if (bus_pid_changed(bus))
127                 return -ECHILD;
128
129         if (bus->is_kernel) {
130                 struct kdbus_cmd_name *n;
131                 size_t l;
132
133                 l = strlen(name);
134                 n = alloca0(offsetof(struct kdbus_cmd_name, name) + l + 1);
135                 n->size = offsetof(struct kdbus_cmd_name, name) + l + 1;
136                 memcpy(n->name, name, l+1);
137
138 #ifdef HAVE_VALGRIND_MEMCHECK_H
139                 VALGRIND_MAKE_MEM_DEFINED(n, n->size);
140 #endif
141                 r = ioctl(bus->input_fd, KDBUS_CMD_NAME_RELEASE, n);
142                 if (r < 0)
143                         return -errno;
144
145                 return n->name_flags;
146         } else {
147                 r = sd_bus_call_method(
148                                 bus,
149                                 "org.freedesktop.DBus",
150                                 "/",
151                                 "org.freedesktop.DBus",
152                                 "ReleaseName",
153                                 NULL,
154                                 &reply,
155                                 "s",
156                                 name);
157                 if (r < 0)
158                         return r;
159
160                 r = sd_bus_message_read(reply, "u", &ret);
161                 if (r < 0)
162                         return r;
163         }
164
165         return ret;
166 }
167
168 int sd_bus_list_names(sd_bus *bus, char ***l) {
169         _cleanup_bus_message_unref_ sd_bus_message *reply1 = NULL, *reply2 = NULL;
170         char **x = NULL;
171         int r;
172
173         if (!bus)
174                 return -EINVAL;
175         if (!l)
176                 return -EINVAL;
177         if (!BUS_IS_OPEN(bus->state))
178                 return -ENOTCONN;
179         if (bus_pid_changed(bus))
180                 return -ECHILD;
181
182         r = sd_bus_call_method(
183                         bus,
184                         "org.freedesktop.DBus",
185                         "/",
186                         "org.freedesktop.DBus",
187                         "ListNames",
188                         NULL,
189                         &reply1,
190                         NULL);
191         if (r < 0)
192                 return r;
193
194         r = sd_bus_call_method(
195                         bus,
196                         "org.freedesktop.DBus",
197                         "/",
198                         "org.freedesktop.DBus",
199                         "ListActivatableNames",
200                         NULL,
201                         &reply2,
202                         NULL);
203         if (r < 0)
204                 return r;
205
206         r = bus_message_read_strv_extend(reply1, &x);
207         if (r < 0) {
208                 strv_free(x);
209                 return r;
210         }
211
212         r = bus_message_read_strv_extend(reply2, &x);
213         if (r < 0) {
214                 strv_free(x);
215                 return r;
216         }
217
218         *l = strv_uniq(x);
219         return 0;
220 }
221
222 int sd_bus_get_owner(sd_bus *bus, const char *name, char **owner) {
223         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
224         const char *found;
225         int r;
226
227         if (!bus)
228                 return -EINVAL;
229         if (!name)
230                 return -EINVAL;
231         if (!BUS_IS_OPEN(bus->state))
232                 return -ENOTCONN;
233         if (bus_pid_changed(bus))
234                 return -ECHILD;
235
236         r = sd_bus_call_method(
237                         bus,
238                         "org.freedesktop.DBus",
239                         "/",
240                         "org.freedesktop.DBus",
241                         "GetNameOwner",
242                         NULL,
243                         &reply,
244                         "s",
245                         name);
246         if (r < 0)
247                 return r;
248
249         r = sd_bus_message_read(reply, "s", &found);
250         if (r < 0)
251                 return r;
252
253         if (owner) {
254                 char *t;
255
256                 t = strdup(found);
257                 if (!t)
258                         return -ENOMEM;
259
260                 *owner = t;
261         }
262
263         return 0;
264 }
265
266 int sd_bus_get_owner_uid(sd_bus *bus, const char *name, uid_t *uid) {
267         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
268         uint32_t u;
269         int r;
270
271         if (!bus)
272                 return -EINVAL;
273         if (!name)
274                 return -EINVAL;
275         if (!uid)
276                 return -EINVAL;
277         if (!BUS_IS_OPEN(bus->state))
278                 return -ENOTCONN;
279         if (bus_pid_changed(bus))
280                 return -ECHILD;
281
282         r = sd_bus_call_method(
283                         bus,
284                         "org.freedesktop.DBus",
285                         "/",
286                         "org.freedesktop.DBus",
287                         "GetConnectionUnixUser",
288                         NULL,
289                         &reply,
290                         "s",
291                         name);
292         if (r < 0)
293                 return r;
294
295         r = sd_bus_message_read(reply, "u", &u);
296         if (r < 0)
297                 return r;
298
299         *uid = (uid_t) u;
300         return 0;
301 }
302
303 int sd_bus_get_owner_pid(sd_bus *bus, const char *name, pid_t *pid) {
304         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
305         uint32_t u;
306         int r;
307
308         if (!bus)
309                 return -EINVAL;
310         if (!name)
311                 return -EINVAL;
312         if (!pid)
313                 return -EINVAL;
314         if (!BUS_IS_OPEN(bus->state))
315                 return -ENOTCONN;
316         if (bus_pid_changed(bus))
317                 return -ECHILD;
318
319         r = sd_bus_call_method(
320                         bus,
321                         "org.freedesktop.DBus",
322                         "/",
323                         "org.freedesktop.DBus",
324                         "GetConnectionUnixProcessID",
325                         NULL,
326                         &reply,
327                         "s",
328                         name);
329         if (r < 0)
330                 return r;
331
332         r = sd_bus_message_read(reply, "u", &u);
333         if (r < 0)
334                 return r;
335
336         if (u == 0)
337                 return -EIO;
338
339         *pid = (uid_t) u;
340         return 0;
341 }
342
343 int bus_add_match_internal(
344                 sd_bus *bus,
345                 const char *match,
346                 struct bus_match_component *components,
347                 unsigned n_components,
348                 uint64_t cookie) {
349
350         int r;
351
352         assert(bus);
353         assert(match);
354
355         if (bus->is_kernel) {
356                 struct kdbus_cmd_match *m;
357                 struct kdbus_item *item;
358                 uint64_t bloom[BLOOM_SIZE/8];
359                 size_t sz;
360                 const char *sender = NULL;
361                 size_t sender_length = 0;
362                 uint64_t src_id = KDBUS_MATCH_SRC_ID_ANY;
363                 bool using_bloom = false;
364                 unsigned i;
365
366                 zero(bloom);
367
368                 sz = offsetof(struct kdbus_cmd_match, items);
369
370                 for (i = 0; i < n_components; i++) {
371                         struct bus_match_component *c = &components[i];
372
373                         switch (c->type) {
374
375                         case BUS_MATCH_SENDER:
376                                 r = bus_kernel_parse_unique_name(c->value_str, &src_id);
377                                 if (r < 0)
378                                         return r;
379
380                                 if (r > 0) {
381                                         sender = c->value_str;
382                                         sender_length = strlen(sender);
383                                         sz += ALIGN8(offsetof(struct kdbus_item, str) + sender_length + 1);
384                                 }
385
386                                 break;
387
388                         case BUS_MATCH_DESTINATION:
389                                 /* The bloom filter does not include
390                                    the destination, since it is only
391                                    available for broadcast messages
392                                    which do not carry a destination
393                                    since they are undirected. */
394                                 break;
395
396                         case BUS_MATCH_INTERFACE:
397                                 bloom_add_pair(bloom, "interface", c->value_str);
398                                 using_bloom = true;
399                                 break;
400
401                         case BUS_MATCH_MEMBER:
402                                 bloom_add_pair(bloom, "member", c->value_str);
403                                 using_bloom = true;
404                                 break;
405
406                         case BUS_MATCH_PATH:
407                                 bloom_add_pair(bloom, "path", c->value_str);
408                                 using_bloom = true;
409                                 break;
410
411                         case BUS_MATCH_PATH_NAMESPACE:
412                                 bloom_add_pair(bloom, "path-slash-prefix", c->value_str);
413                                 using_bloom = true;
414                                 break;
415
416                         case BUS_MATCH_ARG...BUS_MATCH_ARG_LAST:
417                         case BUS_MATCH_ARG_PATH...BUS_MATCH_ARG_PATH_LAST:
418                         case BUS_MATCH_ARG_NAMESPACE...BUS_MATCH_ARG_NAMESPACE_LAST:
419                         case BUS_MATCH_MESSAGE_TYPE:
420                                 assert_not_reached("FIXME!");
421                                 break;
422
423                         case BUS_MATCH_ROOT:
424                         case BUS_MATCH_VALUE:
425                         case BUS_MATCH_LEAF:
426                         case _BUS_MATCH_NODE_TYPE_MAX:
427                         case _BUS_MATCH_NODE_TYPE_INVALID:
428                                 assert_not_reached("Invalid match type?");
429                         }
430                 }
431
432                 if (using_bloom)
433                         sz += ALIGN8(offsetof(struct kdbus_item, data64) + BLOOM_SIZE);
434
435                 m = alloca0(sz);
436                 m->size = sz;
437                 m->cookie = cookie;
438                 m->src_id = src_id;
439
440                 item = m->items;
441
442                 if (using_bloom) {
443                         item->size = offsetof(struct kdbus_item, data64) + BLOOM_SIZE;
444                         item->type = KDBUS_MATCH_BLOOM;
445                         memcpy(item->data64, bloom, BLOOM_SIZE);
446
447                         item = KDBUS_ITEM_NEXT(item);
448                 }
449
450                 if (sender) {
451                         item->size = offsetof(struct kdbus_item, str) + sender_length + 1;
452                         item->type = KDBUS_MATCH_SRC_NAME;
453                         memcpy(item->str, sender, sender_length + 1);
454                 }
455
456                 r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
457                 if (r < 0)
458                         return -errno;
459
460         } else {
461                 return sd_bus_call_method(
462                                 bus,
463                                 "org.freedesktop.DBus",
464                                 "/",
465                                 "org.freedesktop.DBus",
466                                 "AddMatch",
467                                 NULL,
468                                 NULL,
469                                 "s",
470                                 match);
471         }
472
473         return 0;
474 }
475
476 int bus_remove_match_internal(
477                 sd_bus *bus,
478                 const char *match,
479                 uint64_t cookie) {
480
481         int r;
482
483         assert(bus);
484         assert(match);
485
486         if (bus->is_kernel) {
487                 struct kdbus_cmd_match m;
488
489                 zero(m);
490                 m.size = offsetof(struct kdbus_cmd_match, items);
491                 m.cookie = cookie;
492
493                 r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_REMOVE, &m);
494                 if (r < 0)
495                         return -errno;
496
497         } else {
498                 return sd_bus_call_method(
499                                 bus,
500                                 "org.freedesktop.DBus",
501                                 "/",
502                                 "org.freedesktop.DBus",
503                                 "RemoveMatch",
504                                 NULL,
505                                 NULL,
506                                 "s",
507                                 match);
508         }
509
510         return 0;
511 }
512
513 int sd_bus_get_owner_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) {
514         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
515         const char *mid;
516         int r;
517
518         if (!bus)
519                 return -EINVAL;
520         if (!name)
521                 return -EINVAL;
522         if (!BUS_IS_OPEN(bus->state))
523                 return -ENOTCONN;
524         if (bus_pid_changed(bus))
525                 return -ECHILD;
526
527         if (streq_ptr(name, bus->unique_name))
528                 return sd_id128_get_machine(machine);
529
530         r = sd_bus_call_method(bus,
531                                name,
532                                "/",
533                                "org.freedesktop.DBus.Peer",
534                                "GetMachineId",
535                                NULL,
536                                &reply,
537                                NULL);
538
539         if (r < 0)
540                 return r;
541
542         r = sd_bus_message_read(reply, "s", &mid);
543         if (r < 0)
544                 return r;
545
546         return sd_id128_from_string(mid, machine);
547 }