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