chiark / gitweb /
bus: benchmark - adjust printf and MAX_SIZE
[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->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->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->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_MESSAGE_TYPE:
389                                 bloom_add_pair(bloom, "message-type", bus_message_type_to_string(c->value_u8));
390                                 using_bloom = true;
391                                 break;
392
393                         case BUS_MATCH_INTERFACE:
394                                 bloom_add_pair(bloom, "interface", c->value_str);
395                                 using_bloom = true;
396                                 break;
397
398                         case BUS_MATCH_MEMBER:
399                                 bloom_add_pair(bloom, "member", c->value_str);
400                                 using_bloom = true;
401                                 break;
402
403                         case BUS_MATCH_PATH:
404                                 bloom_add_pair(bloom, "path", c->value_str);
405                                 using_bloom = true;
406                                 break;
407
408                         case BUS_MATCH_PATH_NAMESPACE:
409                                 if (!streq(c->value_str, "/")) {
410                                         bloom_add_pair(bloom, "path-slash-prefix", c->value_str);
411                                         using_bloom = true;
412                                 }
413                                 break;
414
415                         case BUS_MATCH_ARG...BUS_MATCH_ARG_LAST: {
416                                 char buf[sizeof("arg")-1 + 2 + 1];
417
418                                 snprintf(buf, sizeof(buf), "arg%u", c->type - BUS_MATCH_ARG);
419                                 bloom_add_pair(bloom, buf, c->value_str);
420                                 using_bloom = true;
421                                 break;
422                         }
423
424                         case BUS_MATCH_ARG_PATH...BUS_MATCH_ARG_PATH_LAST: {
425                                 char buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")];
426
427                                 snprintf(buf, sizeof(buf), "arg%u-slash-prefix", c->type - BUS_MATCH_ARG_PATH);
428                                 bloom_add_pair(bloom, buf, c->value_str);
429                                 using_bloom = true;
430                                 break;
431                         }
432
433                         case BUS_MATCH_ARG_NAMESPACE...BUS_MATCH_ARG_NAMESPACE_LAST: {
434                                 char buf[sizeof("arg")-1 + 2 + sizeof("-dot-prefix")];
435
436                                 snprintf(buf, sizeof(buf), "arg%u-dot-prefix", c->type - BUS_MATCH_ARG_NAMESPACE);
437                                 bloom_add_pair(bloom, buf, c->value_str);
438                                 using_bloom = true;
439                                 break;
440                         }
441
442                         case BUS_MATCH_DESTINATION:
443                                 /* The bloom filter does not include
444                                    the destination, since it is only
445                                    available for broadcast messages
446                                    which do not carry a destination
447                                    since they are undirected. */
448                                 break;
449
450                         case BUS_MATCH_ROOT:
451                         case BUS_MATCH_VALUE:
452                         case BUS_MATCH_LEAF:
453                         case _BUS_MATCH_NODE_TYPE_MAX:
454                         case _BUS_MATCH_NODE_TYPE_INVALID:
455                                 assert_not_reached("Invalid match type?");
456                         }
457                 }
458
459                 if (using_bloom)
460                         sz += ALIGN8(offsetof(struct kdbus_item, data64) + BLOOM_SIZE);
461
462                 m = alloca0(sz);
463                 m->size = sz;
464                 m->cookie = cookie;
465                 m->src_id = src_id;
466
467                 item = m->items;
468
469                 if (using_bloom) {
470                         item->size = offsetof(struct kdbus_item, data64) + BLOOM_SIZE;
471                         item->type = KDBUS_MATCH_BLOOM;
472                         memcpy(item->data64, bloom, BLOOM_SIZE);
473
474                         item = KDBUS_ITEM_NEXT(item);
475                 }
476
477                 if (sender) {
478                         item->size = offsetof(struct kdbus_item, str) + sender_length + 1;
479                         item->type = KDBUS_MATCH_SRC_NAME;
480                         memcpy(item->str, sender, sender_length + 1);
481                 }
482
483                 r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
484                 if (r < 0)
485                         return -errno;
486
487         } else {
488                 return sd_bus_call_method(
489                                 bus,
490                                 "org.freedesktop.DBus",
491                                 "/",
492                                 "org.freedesktop.DBus",
493                                 "AddMatch",
494                                 NULL,
495                                 NULL,
496                                 "s",
497                                 match);
498         }
499
500         return 0;
501 }
502
503 int bus_remove_match_internal(
504                 sd_bus *bus,
505                 const char *match,
506                 uint64_t cookie) {
507
508         int r;
509
510         assert(bus);
511         assert(match);
512
513         if (bus->is_kernel) {
514                 struct kdbus_cmd_match m;
515
516                 zero(m);
517                 m.size = offsetof(struct kdbus_cmd_match, items);
518                 m.cookie = cookie;
519
520                 r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_REMOVE, &m);
521                 if (r < 0)
522                         return -errno;
523
524         } else {
525                 return sd_bus_call_method(
526                                 bus,
527                                 "org.freedesktop.DBus",
528                                 "/",
529                                 "org.freedesktop.DBus",
530                                 "RemoveMatch",
531                                 NULL,
532                                 NULL,
533                                 "s",
534                                 match);
535         }
536
537         return 0;
538 }
539
540 int sd_bus_get_owner_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) {
541         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
542         const char *mid;
543         int r;
544
545         if (!bus)
546                 return -EINVAL;
547         if (!name)
548                 return -EINVAL;
549         if (!BUS_IS_OPEN(bus->state))
550                 return -ENOTCONN;
551         if (bus_pid_changed(bus))
552                 return -ECHILD;
553
554         if (streq_ptr(name, bus->unique_name))
555                 return sd_id128_get_machine(machine);
556
557         r = sd_bus_call_method(bus,
558                                name,
559                                "/",
560                                "org.freedesktop.DBus.Peer",
561                                "GetMachineId",
562                                NULL,
563                                &reply,
564                                NULL);
565
566         if (r < 0)
567                 return r;
568
569         r = sd_bus_message_read(reply, "s", &mid);
570         if (r < 0)
571                 return r;
572
573         return sd_id128_from_string(mid, machine);
574 }