chiark / gitweb /
shell-completion: busctl
[elogind.git] / src / libsystemd-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 emit_interfaces_added(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
147         int r;
148
149         assert_se(sd_bus_emit_interfaces_added(bus, m->path, "org.freedesktop.systemd.test", 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_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
158         int r;
159
160         assert_se(sd_bus_emit_interfaces_removed(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 const sd_bus_vtable vtable[] = {
169         SD_BUS_VTABLE_START(0),
170         SD_BUS_METHOD("AlterSomething", "s", "s", something_handler, 0),
171         SD_BUS_METHOD("Exit", "", "", exit_handler, 0),
172         SD_BUS_WRITABLE_PROPERTY("Something", "s", get_handler, set_handler, 0, 0),
173         SD_BUS_WRITABLE_PROPERTY("AutomaticStringProperty", "s", NULL, NULL, offsetof(struct context, automatic_string_property), 0),
174         SD_BUS_WRITABLE_PROPERTY("AutomaticIntegerProperty", "u", NULL, NULL, offsetof(struct context, automatic_integer_property), 0),
175         SD_BUS_METHOD("NoOperation", NULL, NULL, NULL, 0),
176         SD_BUS_METHOD("EmitInterfacesAdded", NULL, NULL, emit_interfaces_added, 0),
177         SD_BUS_METHOD("EmitInterfacesRemoved", NULL, NULL, emit_interfaces_removed, 0),
178         SD_BUS_VTABLE_END
179 };
180
181 static const sd_bus_vtable vtable2[] = {
182         SD_BUS_VTABLE_START(0),
183         SD_BUS_METHOD("NotifyTest", "", "", notify_test, 0),
184         SD_BUS_PROPERTY("Value", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
185         SD_BUS_VTABLE_END
186 };
187
188 static int enumerator_callback(sd_bus *b, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
189
190         if (object_path_startswith("/value", path))
191                 assert_se(*nodes = strv_new("/value/a", "/value/b", "/value/c", NULL));
192
193         return 1;
194 }
195
196 static void *server(void *p) {
197         struct context *c = p;
198         sd_bus *bus = NULL;
199         sd_id128_t id;
200         int r;
201
202         c->quit = false;
203
204         assert_se(sd_id128_randomize(&id) >= 0);
205
206         assert_se(sd_bus_new(&bus) >= 0);
207         assert_se(sd_bus_set_fd(bus, c->fds[0], c->fds[0]) >= 0);
208         assert_se(sd_bus_set_server(bus, 1, id) >= 0);
209
210         assert_se(sd_bus_add_object_vtable(bus, "/foo", "org.freedesktop.systemd.test", vtable, c) >= 0);
211         assert_se(sd_bus_add_object_vtable(bus, "/foo", "org.freedesktop.systemd.test2", vtable, c) >= 0);
212         assert_se(sd_bus_add_fallback_vtable(bus, "/value", "org.freedesktop.systemd.ValueTest", vtable2, NULL, UINT_TO_PTR(20)) >= 0);
213         assert_se(sd_bus_add_node_enumerator(bus, "/value", enumerator_callback, NULL) >= 0);
214         assert_se(sd_bus_add_object_manager(bus, "/value") >= 0);
215
216         assert_se(sd_bus_start(bus) >= 0);
217
218         log_error("Entering event loop on server");
219
220         while (!c->quit) {
221                 log_error("Loop!");
222
223                 r = sd_bus_process(bus, NULL);
224                 if (r < 0) {
225                         log_error("Failed to process requests: %s", strerror(-r));
226                         goto fail;
227                 }
228
229                 if (r == 0) {
230                         r = sd_bus_wait(bus, (uint64_t) -1);
231                         if (r < 0) {
232                                 log_error("Failed to wait: %s", strerror(-r));
233                                 goto fail;
234                         }
235
236                         continue;
237                 }
238         }
239
240         r = 0;
241
242 fail:
243         if (bus) {
244                 sd_bus_flush(bus);
245                 sd_bus_unref(bus);
246         }
247
248         return INT_TO_PTR(r);
249 }
250
251 static int client(struct context *c) {
252         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
253         _cleanup_bus_unref_ sd_bus *bus = NULL;
254         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
255         const char *s;
256         int r;
257
258         assert_se(sd_bus_new(&bus) >= 0);
259         assert_se(sd_bus_set_fd(bus, c->fds[1], c->fds[1]) >= 0);
260         assert_se(sd_bus_start(bus) >= 0);
261
262         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "NoOperation", &error, NULL, NULL);
263         assert_se(r >= 0);
264
265         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "s", "hallo");
266         assert_se(r >= 0);
267
268         r = sd_bus_message_read(reply, "s", &s);
269         assert_se(r >= 0);
270         assert_se(streq(s, "<<<hallo>>>"));
271
272         sd_bus_message_unref(reply);
273         reply = NULL;
274
275         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Doesntexist", &error, &reply, "");
276         assert_se(r < 0);
277         assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD));
278
279         sd_bus_error_free(&error);
280
281         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "as", 1, "hallo");
282         assert_se(r < 0);
283         assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS));
284
285         sd_bus_error_free(&error);
286
287         r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, &reply, "s");
288         assert_se(r >= 0);
289
290         r = sd_bus_message_read(reply, "s", &s);
291         assert_se(r >= 0);
292         assert_se(streq(s, "<<<hallo>>>"));
293
294         sd_bus_message_unref(reply);
295         reply = NULL;
296
297         r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, "s", "test");
298         assert_se(r >= 0);
299
300         r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, &reply, "s");
301         assert_se(r >= 0);
302
303         r = sd_bus_message_read(reply, "s", &s);
304         assert_se(r >= 0);
305         assert_se(streq(s, "test"));
306
307         sd_bus_message_unref(reply);
308         reply = NULL;
309
310         r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AutomaticIntegerProperty", &error, "u", 815);
311         assert_se(r >= 0);
312
313         assert_se(c->automatic_integer_property == 815);
314
315         r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AutomaticStringProperty", &error, "s", "Du Dödel, Du!");
316         assert_se(r >= 0);
317
318         assert_se(streq(c->automatic_string_property, "Du Dödel, Du!"));
319
320         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
321         assert_se(r >= 0);
322
323         r = sd_bus_message_read(reply, "s", &s);
324         assert_se(r >= 0);
325         fputs(s, stdout);
326
327         sd_bus_message_unref(reply);
328         reply = NULL;
329
330         r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/value/xuzz", "org.freedesktop.systemd.ValueTest", "Value", &error, &reply, "s");
331         assert_se(r >= 0);
332
333         r = sd_bus_message_read(reply, "s", &s);
334         assert_se(r >= 0);
335         log_info("read %s", s);
336
337         sd_bus_message_unref(reply);
338         reply = NULL;
339
340         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
341         assert_se(r >= 0);
342
343         r = sd_bus_message_read(reply, "s", &s);
344         assert_se(r >= 0);
345         fputs(s, stdout);
346
347         sd_bus_message_unref(reply);
348         reply = NULL;
349
350         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
351         assert_se(r >= 0);
352
353         r = sd_bus_message_read(reply, "s", &s);
354         assert_se(r >= 0);
355         fputs(s, stdout);
356
357         sd_bus_message_unref(reply);
358         reply = NULL;
359
360         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
361         assert_se(r >= 0);
362
363         r = sd_bus_message_read(reply, "s", &s);
364         assert_se(r >= 0);
365         fputs(s, stdout);
366
367         sd_bus_message_unref(reply);
368         reply = NULL;
369
370         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "");
371         assert_se(r >= 0);
372
373         bus_message_dump(reply, stdout, true);
374
375         sd_bus_message_unref(reply);
376         reply = NULL;
377
378         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "org.freedesktop.systemd.ValueTest2");
379         assert_se(r < 0);
380         assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_INTERFACE));
381         sd_bus_error_free(&error);
382
383         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, "");
384         assert_se(r < 0);
385         assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD));
386         sd_bus_error_free(&error);
387
388         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, "");
389         assert_se(r >= 0);
390
391         bus_message_dump(reply, stdout, true);
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.systemd.ValueTest", "NotifyTest", &error, NULL, "");
397         assert_se(r >= 0);
398
399         r = sd_bus_process(bus, &reply);
400         assert_se(r > 0);
401
402         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged"));
403         bus_message_dump(reply, stdout, true);
404
405         sd_bus_message_unref(reply);
406         reply = NULL;
407
408         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesAdded", &error, NULL, "");
409         assert_se(r >= 0);
410
411         r = sd_bus_process(bus, &reply);
412         assert_se(r > 0);
413
414         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"));
415         bus_message_dump(reply, stdout, true);
416
417         sd_bus_message_unref(reply);
418         reply = NULL;
419
420         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesRemoved", &error, NULL, "");
421         assert_se(r >= 0);
422
423         r = sd_bus_process(bus, &reply);
424         assert_se(r > 0);
425
426         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"));
427         bus_message_dump(reply, stdout, true);
428
429         sd_bus_message_unref(reply);
430         reply = NULL;
431
432         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Exit", &error, NULL, "");
433         assert_se(r >= 0);
434
435         sd_bus_flush(bus);
436
437         return 0;
438 }
439
440 int main(int argc, char *argv[]) {
441         struct context c = {};
442         pthread_t s;
443         void *p;
444         int r, q;
445
446         zero(c);
447
448         c.automatic_integer_property = 4711;
449         assert_se(c.automatic_string_property = strdup("dudeldu"));
450
451         assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, c.fds) >= 0);
452
453         r = pthread_create(&s, NULL, server, &c);
454         if (r != 0)
455                 return -r;
456
457         r = client(&c);
458
459         q = pthread_join(s, &p);
460         if (q != 0)
461                 return -q;
462
463         if (r < 0)
464                 return r;
465
466         if (PTR_TO_INT(p) < 0)
467                 return PTR_TO_INT(p);
468
469         free(c.something);
470         free(c.automatic_string_property);
471
472         return EXIT_SUCCESS;
473 }