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