chiark / gitweb /
sd-bus: fix path of object-manager signals
[elogind.git] / src / libelogind / sd-bus / test-bus-objects.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 <stdlib.h>
23 #include <pthread.h>
24
25 #include "log.h"
26 #include "util.h"
27 #include "macro.h"
28 #include "strv.h"
29
30 #include "sd-bus.h"
31 #include "bus-internal.h"
32 #include "bus-message.h"
33 #include "bus-util.h"
34 #include "bus-dump.h"
35
36 struct context {
37         int fds[2];
38         bool quit;
39         char *something;
40         char *automatic_string_property;
41         uint32_t automatic_integer_property;
42 };
43
44 static int something_handler(sd_bus_message *m, void *userdata, sd_bus_error *error) {
45         struct context *c = userdata;
46         const char *s;
47         char *n = NULL;
48         int r;
49
50         r = sd_bus_message_read(m, "s", &s);
51         assert_se(r > 0);
52
53         n = strjoin("<<<", s, ">>>", NULL);
54         assert_se(n);
55
56         free(c->something);
57         c->something = n;
58
59         log_info("AlterSomething() called, got %s, returning %s", s, n);
60
61         /* This should fail, since the return type doesn't match */
62         assert_se(sd_bus_reply_method_return(m, "u", 4711) == -ENOMSG);
63
64         r = sd_bus_reply_method_return(m, "s", n);
65         assert_se(r >= 0);
66
67         return 1;
68 }
69
70 static int exit_handler(sd_bus_message *m, void *userdata, sd_bus_error *error) {
71         struct context *c = userdata;
72         int r;
73
74         c->quit = true;
75
76         log_info("Exit called");
77
78         r = sd_bus_reply_method_return(m, "");
79         assert_se(r >= 0);
80
81         return 1;
82 }
83
84 static int get_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
85         struct context *c = userdata;
86         int r;
87
88         log_info("property get for %s called, returning \"%s\".", property, c->something);
89
90         r = sd_bus_message_append(reply, "s", c->something);
91         assert_se(r >= 0);
92
93         return 1;
94 }
95
96 static int set_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, void *userdata, sd_bus_error *error) {
97         struct context *c = userdata;
98         const char *s;
99         char *n;
100         int r;
101
102         log_info("property set for %s called", property);
103
104         r = sd_bus_message_read(value, "s", &s);
105         assert_se(r >= 0);
106
107         n = strdup(s);
108         assert_se(n);
109
110         free(c->something);
111         c->something = n;
112
113         return 1;
114 }
115
116 static int value_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
117         _cleanup_free_ char *s = NULL;
118         int r;
119
120         assert_se(asprintf(&s, "object %p, path %s", userdata, path) >= 0);
121         r = sd_bus_message_append(reply, "s", s);
122         assert_se(r >= 0);
123
124         assert_se(startswith(path, "/value/") != NULL || strcmp(path, "/value") == 0);
125
126         assert_se(PTR_TO_UINT(userdata) == 30);
127
128         return 1;
129 }
130
131 static int notify_test(sd_bus_message *m, void *userdata, sd_bus_error *error) {
132         int r;
133
134         assert_se(sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), m->path, "org.freedesktop.systemd.ValueTest", "Value", NULL) >= 0);
135
136         r = sd_bus_reply_method_return(m, NULL);
137         assert_se(r >= 0);
138
139         return 1;
140 }
141
142 static int notify_test2(sd_bus_message *m, void *userdata, sd_bus_error *error) {
143         int r;
144
145         assert_se(sd_bus_emit_properties_changed_strv(sd_bus_message_get_bus(m), m->path, "org.freedesktop.systemd.ValueTest", NULL) >= 0);
146
147         r = sd_bus_reply_method_return(m, NULL);
148         assert_se(r >= 0);
149
150         return 1;
151 }
152
153 static int emit_interfaces_added(sd_bus_message *m, void *userdata, sd_bus_error *error) {
154         int r;
155
156         assert_se(sd_bus_emit_interfaces_added(sd_bus_message_get_bus(m), "/value/a/x", "org.freedesktop.systemd.ValueTest", NULL) >= 0);
157
158         r = sd_bus_reply_method_return(m, NULL);
159         assert_se(r >= 0);
160
161         return 1;
162 }
163
164 static int emit_interfaces_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
165         int r;
166
167         assert_se(sd_bus_emit_interfaces_removed(sd_bus_message_get_bus(m), "/value/a/x", "org.freedesktop.systemd.ValueTest", NULL) >= 0);
168
169         r = sd_bus_reply_method_return(m, NULL);
170         assert_se(r >= 0);
171
172         return 1;
173 }
174
175 static int emit_object_added(sd_bus_message *m, void *userdata, sd_bus_error *error) {
176         int r;
177
178         assert_se(sd_bus_emit_object_added(sd_bus_message_get_bus(m), "/value/a/x") >= 0);
179
180         r = sd_bus_reply_method_return(m, NULL);
181         assert_se(r >= 0);
182
183         return 1;
184 }
185
186 static int emit_object_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
187         int r;
188
189         assert_se(sd_bus_emit_object_removed(sd_bus_message_get_bus(m), "/value/a/x") >= 0);
190
191         r = sd_bus_reply_method_return(m, NULL);
192         assert_se(r >= 0);
193
194         return 1;
195 }
196
197 static const sd_bus_vtable vtable[] = {
198         SD_BUS_VTABLE_START(0),
199         SD_BUS_METHOD("AlterSomething", "s", "s", something_handler, 0),
200         SD_BUS_METHOD("Exit", "", "", exit_handler, 0),
201         SD_BUS_WRITABLE_PROPERTY("Something", "s", get_handler, set_handler, 0, 0),
202         SD_BUS_WRITABLE_PROPERTY("AutomaticStringProperty", "s", NULL, NULL, offsetof(struct context, automatic_string_property), 0),
203         SD_BUS_WRITABLE_PROPERTY("AutomaticIntegerProperty", "u", NULL, NULL, offsetof(struct context, automatic_integer_property), 0),
204         SD_BUS_METHOD("NoOperation", NULL, NULL, NULL, 0),
205         SD_BUS_METHOD("EmitInterfacesAdded", NULL, NULL, emit_interfaces_added, 0),
206         SD_BUS_METHOD("EmitInterfacesRemoved", NULL, NULL, emit_interfaces_removed, 0),
207         SD_BUS_METHOD("EmitObjectAdded", NULL, NULL, emit_object_added, 0),
208         SD_BUS_METHOD("EmitObjectRemoved", NULL, NULL, emit_object_removed, 0),
209         SD_BUS_VTABLE_END
210 };
211
212 static const sd_bus_vtable vtable2[] = {
213         SD_BUS_VTABLE_START(0),
214         SD_BUS_METHOD("NotifyTest", "", "", notify_test, 0),
215         SD_BUS_METHOD("NotifyTest2", "", "", notify_test2, 0),
216         SD_BUS_PROPERTY("Value", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
217         SD_BUS_PROPERTY("Value2", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
218         SD_BUS_PROPERTY("Value3", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_CONST),
219         SD_BUS_PROPERTY("Value4", "s", value_handler, 10, 0),
220         SD_BUS_VTABLE_END
221 };
222
223 static int enumerator_callback(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
224
225         if (object_path_startswith("/value", path))
226                 assert_se(*nodes = strv_new("/value/a", "/value/b", "/value/c", NULL));
227
228         return 1;
229 }
230
231 static int enumerator2_callback(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
232
233         if (object_path_startswith("/value/a", path))
234                 assert_se(*nodes = strv_new("/value/a/x", "/value/a/y", "/value/a/z", NULL));
235
236         return 1;
237 }
238
239 static void *server(void *p) {
240         struct context *c = p;
241         sd_bus *bus = NULL;
242         sd_id128_t id;
243         int r;
244
245         c->quit = false;
246
247         assert_se(sd_id128_randomize(&id) >= 0);
248
249         assert_se(sd_bus_new(&bus) >= 0);
250         assert_se(sd_bus_set_fd(bus, c->fds[0], c->fds[0]) >= 0);
251         assert_se(sd_bus_set_server(bus, 1, id) >= 0);
252
253         assert_se(sd_bus_add_object_vtable(bus, NULL, "/foo", "org.freedesktop.systemd.test", vtable, c) >= 0);
254         assert_se(sd_bus_add_object_vtable(bus, NULL, "/foo", "org.freedesktop.systemd.test2", vtable, c) >= 0);
255         assert_se(sd_bus_add_fallback_vtable(bus, NULL, "/value", "org.freedesktop.systemd.ValueTest", vtable2, NULL, UINT_TO_PTR(20)) >= 0);
256         assert_se(sd_bus_add_node_enumerator(bus, NULL, "/value", enumerator_callback, NULL) >= 0);
257         assert_se(sd_bus_add_node_enumerator(bus, NULL, "/value/a", enumerator2_callback, NULL) >= 0);
258         assert_se(sd_bus_add_object_manager(bus, NULL, "/value") >= 0);
259         assert_se(sd_bus_add_object_manager(bus, NULL, "/value/a") >= 0);
260
261         assert_se(sd_bus_start(bus) >= 0);
262
263         log_error("Entering event loop on server");
264
265         while (!c->quit) {
266                 log_error("Loop!");
267
268                 r = sd_bus_process(bus, NULL);
269                 if (r < 0) {
270                         log_error_errno(r, "Failed to process requests: %m");
271                         goto fail;
272                 }
273
274                 if (r == 0) {
275                         r = sd_bus_wait(bus, (uint64_t) -1);
276                         if (r < 0) {
277                                 log_error_errno(r, "Failed to wait: %m");
278                                 goto fail;
279                         }
280
281                         continue;
282                 }
283         }
284
285         r = 0;
286
287 fail:
288         if (bus) {
289                 sd_bus_flush(bus);
290                 sd_bus_unref(bus);
291         }
292
293         return INT_TO_PTR(r);
294 }
295
296 static int client(struct context *c) {
297         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
298         _cleanup_bus_unref_ sd_bus *bus = NULL;
299         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
300         const char *s;
301         int r;
302
303         assert_se(sd_bus_new(&bus) >= 0);
304         assert_se(sd_bus_set_fd(bus, c->fds[1], c->fds[1]) >= 0);
305         assert_se(sd_bus_start(bus) >= 0);
306
307         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "NoOperation", &error, NULL, NULL);
308         assert_se(r >= 0);
309
310         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "s", "hallo");
311         assert_se(r >= 0);
312
313         r = sd_bus_message_read(reply, "s", &s);
314         assert_se(r >= 0);
315         assert_se(streq(s, "<<<hallo>>>"));
316
317         sd_bus_message_unref(reply);
318         reply = NULL;
319
320         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Doesntexist", &error, &reply, "");
321         assert_se(r < 0);
322         assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD));
323
324         sd_bus_error_free(&error);
325
326         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "as", 1, "hallo");
327         assert_se(r < 0);
328         assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS));
329
330         sd_bus_error_free(&error);
331
332         r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, &reply, "s");
333         assert_se(r >= 0);
334
335         r = sd_bus_message_read(reply, "s", &s);
336         assert_se(r >= 0);
337         assert_se(streq(s, "<<<hallo>>>"));
338
339         sd_bus_message_unref(reply);
340         reply = NULL;
341
342         r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, "s", "test");
343         assert_se(r >= 0);
344
345         r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, &reply, "s");
346         assert_se(r >= 0);
347
348         r = sd_bus_message_read(reply, "s", &s);
349         assert_se(r >= 0);
350         assert_se(streq(s, "test"));
351
352         sd_bus_message_unref(reply);
353         reply = NULL;
354
355         r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AutomaticIntegerProperty", &error, "u", 815);
356         assert_se(r >= 0);
357
358         assert_se(c->automatic_integer_property == 815);
359
360         r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AutomaticStringProperty", &error, "s", "Du Dödel, Du!");
361         assert_se(r >= 0);
362
363         assert_se(streq(c->automatic_string_property, "Du Dödel, Du!"));
364
365         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
366         assert_se(r >= 0);
367
368         r = sd_bus_message_read(reply, "s", &s);
369         assert_se(r >= 0);
370         fputs(s, stdout);
371
372         sd_bus_message_unref(reply);
373         reply = NULL;
374
375         r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/value/xuzz", "org.freedesktop.systemd.ValueTest", "Value", &error, &reply, "s");
376         assert_se(r >= 0);
377
378         r = sd_bus_message_read(reply, "s", &s);
379         assert_se(r >= 0);
380         log_info("read %s", s);
381
382         sd_bus_message_unref(reply);
383         reply = NULL;
384
385         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
386         assert_se(r >= 0);
387
388         r = sd_bus_message_read(reply, "s", &s);
389         assert_se(r >= 0);
390         fputs(s, stdout);
391
392         sd_bus_message_unref(reply);
393         reply = NULL;
394
395         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
396         assert_se(r >= 0);
397
398         r = sd_bus_message_read(reply, "s", &s);
399         assert_se(r >= 0);
400         fputs(s, stdout);
401
402         sd_bus_message_unref(reply);
403         reply = NULL;
404
405         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
406         assert_se(r >= 0);
407
408         r = sd_bus_message_read(reply, "s", &s);
409         assert_se(r >= 0);
410         fputs(s, stdout);
411
412         sd_bus_message_unref(reply);
413         reply = NULL;
414
415         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "");
416         assert_se(r >= 0);
417
418         bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
419
420         sd_bus_message_unref(reply);
421         reply = NULL;
422
423         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "org.freedesktop.systemd.ValueTest2");
424         assert_se(r < 0);
425         assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_INTERFACE));
426         sd_bus_error_free(&error);
427
428         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, "");
429         assert_se(r < 0);
430         assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD));
431         sd_bus_error_free(&error);
432
433         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, "");
434         assert_se(r >= 0);
435
436         bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
437
438         sd_bus_message_unref(reply);
439         reply = NULL;
440
441         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.systemd.ValueTest", "NotifyTest", &error, NULL, "");
442         assert_se(r >= 0);
443
444         r = sd_bus_process(bus, &reply);
445         assert_se(r > 0);
446
447         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged"));
448         bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
449
450         sd_bus_message_unref(reply);
451         reply = NULL;
452
453         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.systemd.ValueTest", "NotifyTest2", &error, NULL, "");
454         assert_se(r >= 0);
455
456         r = sd_bus_process(bus, &reply);
457         assert_se(r > 0);
458
459         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged"));
460         bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
461
462         sd_bus_message_unref(reply);
463         reply = NULL;
464
465         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesAdded", &error, NULL, "");
466         assert_se(r >= 0);
467
468         r = sd_bus_process(bus, &reply);
469         assert_se(r > 0);
470
471         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"));
472         bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
473
474         sd_bus_message_unref(reply);
475         reply = NULL;
476
477         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesRemoved", &error, NULL, "");
478         assert_se(r >= 0);
479
480         r = sd_bus_process(bus, &reply);
481         assert_se(r > 0);
482
483         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"));
484         bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
485
486         sd_bus_message_unref(reply);
487         reply = NULL;
488
489         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectAdded", &error, NULL, "");
490         assert_se(r >= 0);
491
492         r = sd_bus_process(bus, &reply);
493         assert_se(r > 0);
494
495         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"));
496         bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
497
498         sd_bus_message_unref(reply);
499         reply = NULL;
500
501         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectRemoved", &error, NULL, "");
502         assert_se(r >= 0);
503
504         r = sd_bus_process(bus, &reply);
505         assert_se(r > 0);
506
507         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"));
508         bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
509
510         sd_bus_message_unref(reply);
511         reply = NULL;
512
513         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Exit", &error, NULL, "");
514         assert_se(r >= 0);
515
516         sd_bus_flush(bus);
517
518         return 0;
519 }
520
521 int main(int argc, char *argv[]) {
522         struct context c = {};
523         pthread_t s;
524         void *p;
525         int r, q;
526
527         zero(c);
528
529         c.automatic_integer_property = 4711;
530         assert_se(c.automatic_string_property = strdup("dudeldu"));
531
532         assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, c.fds) >= 0);
533
534         r = pthread_create(&s, NULL, server, &c);
535         if (r != 0)
536                 return -r;
537
538         r = client(&c);
539
540         q = pthread_join(s, &p);
541         if (q != 0)
542                 return -q;
543
544         if (r < 0)
545                 return r;
546
547         if (PTR_TO_INT(p) < 0)
548                 return PTR_TO_INT(p);
549
550         free(c.something);
551         free(c.automatic_string_property);
552
553         return EXIT_SUCCESS;
554 }