chiark / gitweb /
bus: make sure that we always keep a ref to the bus when we dispatch callbacks
[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
37 /* Test:
38  *
39  *   Add in:
40  *
41  *   node hierarchy updates during dispatching
42  *
43  */
44
45 struct context {
46         int fds[2];
47         bool quit;
48         char *something;
49         char *automatic_string_property;
50         uint32_t automatic_integer_property;
51 };
52
53 static int something_handler(sd_bus *bus, sd_bus_message *m, void *userdata) {
54         struct context *c = userdata;
55         const char *s;
56         char *n = NULL;
57         int r;
58
59         r = sd_bus_message_read(m, "s", &s);
60         assert_se(r > 0);
61
62         n = strjoin("<<<", s, ">>>", NULL);
63         assert_se(n);
64
65         free(c->something);
66         c->something = n;
67
68         log_info("AlterSomething() called, got %s, returning %s", s, n);
69
70         r = sd_bus_reply_method_return(bus, m, "s", n);
71         assert_se(r >= 0);
72
73         return 1;
74 }
75
76 static int exit_handler(sd_bus *bus, sd_bus_message *m, void *userdata) {
77         struct context *c = userdata;
78         int r;
79
80         c->quit = true;
81
82         log_info("Exit called");
83
84         r = sd_bus_reply_method_return(bus, m, "");
85         assert_se(r >= 0);
86
87         return 1;
88 }
89
90 static int get_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, sd_bus_error *error, void *userdata) {
91         struct context *c = userdata;
92         int r;
93
94         log_info("property get for %s called", property);
95
96         r = sd_bus_message_append(reply, "s", c->something);
97         assert_se(r >= 0);
98
99         return 1;
100 }
101
102 static int set_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, sd_bus_error *error, void *userdata) {
103         struct context *c = userdata;
104         const char *s;
105         char *n;
106         int r;
107
108         log_info("property set for %s called", property);
109
110         r = sd_bus_message_read(value, "s", &s);
111         assert_se(r >= 0);
112
113         n = strdup(s);
114         assert_se(n);
115
116         free(c->something);
117         c->something = n;
118
119         return 1;
120 }
121
122 static int value_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, sd_bus_error *error, void *userdata) {
123         _cleanup_free_ char *s = NULL;
124         const char *x;
125         int r;
126
127         assert_se(asprintf(&s, "object %p, path %s", userdata, path) >= 0);
128         r = sd_bus_message_append(reply, "s", s);
129         assert_se(r >= 0);
130
131         assert_se(x = startswith(path, "/value/"));
132
133         assert_se(PTR_TO_UINT(userdata) == 30);
134
135         return 1;
136 }
137
138 static int notify_test(sd_bus *bus, sd_bus_message *m, void *userdata) {
139         int r;
140
141         assert_se(sd_bus_emit_properties_changed(bus, m->path, "org.freedesktop.systemd.ValueTest", "Value", NULL) >= 0);
142
143         r = sd_bus_reply_method_return(bus, m, NULL);
144         assert_se(r >= 0);
145
146         return 1;
147 }
148
149 static int emit_interfaces_added(sd_bus *bus, sd_bus_message *m, void *userdata) {
150         int r;
151
152         assert_se(sd_bus_emit_interfaces_added(bus, m->path, "org.freedesktop.systemd.test", NULL) >= 0);
153
154         r = sd_bus_reply_method_return(bus, m, NULL);
155         assert_se(r >= 0);
156
157         return 1;
158 }
159
160 static int emit_interfaces_removed(sd_bus *bus, sd_bus_message *m, void *userdata) {
161         int r;
162
163         assert_se(sd_bus_emit_interfaces_removed(bus, m->path, "org.freedesktop.systemd.test", NULL) >= 0);
164
165         r = sd_bus_reply_method_return(bus, m, NULL);
166         assert_se(r >= 0);
167
168         return 1;
169 }
170
171 static const sd_bus_vtable vtable[] = {
172         SD_BUS_VTABLE_START(0),
173         SD_BUS_METHOD("AlterSomething", "s", "s", something_handler, 0),
174         SD_BUS_METHOD("Exit", "", "", exit_handler, 0),
175         SD_BUS_WRITABLE_PROPERTY("Something", "s", get_handler, set_handler, 0, 0),
176         SD_BUS_WRITABLE_PROPERTY("AutomaticStringProperty", "s", NULL, NULL, offsetof(struct context, automatic_string_property), 0),
177         SD_BUS_WRITABLE_PROPERTY("AutomaticIntegerProperty", "u", NULL, NULL, offsetof(struct context, automatic_integer_property), 0),
178         SD_BUS_METHOD("NoOperation", NULL, NULL, NULL, 0),
179         SD_BUS_METHOD("EmitInterfacesAdded", NULL, NULL, emit_interfaces_added, 0),
180         SD_BUS_METHOD("EmitInterfacesRemoved", NULL, NULL, emit_interfaces_removed, 0),
181         SD_BUS_VTABLE_END
182 };
183
184 static const sd_bus_vtable vtable2[] = {
185         SD_BUS_VTABLE_START(0),
186         SD_BUS_METHOD("NotifyTest", "", "", notify_test, 0),
187         SD_BUS_PROPERTY("Value", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
188         SD_BUS_VTABLE_END
189 };
190
191 static int enumerator_callback(sd_bus *b, const char *path, char ***nodes, void *userdata) {
192
193         if (object_path_startswith("/value", path))
194                 assert_se(*nodes = strv_new("/value/a", "/value/b", "/value/c", NULL));
195
196         return 1;
197 }
198
199 static void *server(void *p) {
200         struct context *c = p;
201         sd_bus *bus = NULL;
202         sd_id128_t id;
203         int r;
204
205         c->quit = false;
206
207         assert_se(sd_id128_randomize(&id) >= 0);
208
209         assert_se(sd_bus_new(&bus) >= 0);
210         assert_se(sd_bus_set_fd(bus, c->fds[0], c->fds[0]) >= 0);
211         assert_se(sd_bus_set_server(bus, 1, id) >= 0);
212
213         assert_se(sd_bus_add_object_vtable(bus, "/foo", "org.freedesktop.systemd.test", vtable, c) >= 0);
214         assert_se(sd_bus_add_object_vtable(bus, "/foo", "org.freedesktop.systemd.test2", vtable, c) >= 0);
215         assert_se(sd_bus_add_fallback_vtable(bus, "/value", "org.freedesktop.systemd.ValueTest", vtable2, NULL, UINT_TO_PTR(20)) >= 0);
216         assert_se(sd_bus_add_node_enumerator(bus, "/value", enumerator_callback, NULL) >= 0);
217         assert_se(sd_bus_add_object_manager(bus, "/value") >= 0);
218
219         assert_se(sd_bus_start(bus) >= 0);
220
221         log_error("Entering event loop on server");
222
223         while (!c->quit) {
224                 log_error("Loop!");
225
226                 r = sd_bus_process(bus, NULL);
227                 if (r < 0) {
228                         log_error("Failed to process requests: %s", strerror(-r));
229                         goto fail;
230                 }
231
232                 if (r == 0) {
233                         r = sd_bus_wait(bus, (uint64_t) -1);
234                         if (r < 0) {
235                                 log_error("Failed to wait: %s", strerror(-r));
236                                 goto fail;
237                         }
238
239                         continue;
240                 }
241         }
242
243         r = 0;
244
245 fail:
246         if (bus) {
247                 sd_bus_flush(bus);
248                 sd_bus_unref(bus);
249         }
250
251         return INT_TO_PTR(r);
252 }
253
254 static int client(struct context *c) {
255         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
256         _cleanup_bus_unref_ sd_bus *bus = NULL;
257         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
258         const char *s;
259         int r;
260
261         assert_se(sd_bus_new(&bus) >= 0);
262         assert_se(sd_bus_set_fd(bus, c->fds[1], c->fds[1]) >= 0);
263         assert_se(sd_bus_start(bus) >= 0);
264
265         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "NoOperation", &error, NULL, NULL);
266         assert_se(r >= 0);
267
268         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "s", "hallo");
269         assert_se(r >= 0);
270
271         r = sd_bus_message_read(reply, "s", &s);
272         assert_se(r >= 0);
273         assert_se(streq(s, "<<<hallo>>>"));
274
275         sd_bus_message_unref(reply);
276         reply = NULL;
277
278         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Doesntexist", &error, &reply, "");
279         assert_se(r < 0);
280         assert_se(sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.UnknownMethod"));
281
282         sd_bus_error_free(&error);
283
284         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "as", 1, "hallo");
285         assert_se(r < 0);
286         assert_se(sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.InvalidArgs"));
287
288         sd_bus_error_free(&error);
289
290         r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, &reply, "s");
291         assert_se(r >= 0);
292
293         r = sd_bus_message_read(reply, "s", &s);
294         assert_se(r >= 0);
295         assert_se(streq(s, "<<<hallo>>>"));
296
297         sd_bus_message_unref(reply);
298         reply = NULL;
299
300         r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, "s", "test");
301         assert_se(r >= 0);
302
303         r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, &reply, "s");
304         assert_se(r >= 0);
305
306         r = sd_bus_message_read(reply, "s", &s);
307         assert_se(r >= 0);
308         assert_se(streq(s, "test"));
309
310         sd_bus_message_unref(reply);
311         reply = NULL;
312
313         r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AutomaticIntegerProperty", &error, "u", 815);
314         assert_se(r >= 0);
315
316         assert_se(c->automatic_integer_property == 815);
317
318         r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AutomaticStringProperty", &error, "s", "Du Dödel, Du!");
319         assert_se(r >= 0);
320
321         assert_se(streq(c->automatic_string_property, "Du Dödel, Du!"));
322
323         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
324         assert_se(r >= 0);
325
326         r = sd_bus_message_read(reply, "s", &s);
327         assert_se(r >= 0);
328         fputs(s, stdout);
329
330         sd_bus_message_unref(reply);
331         reply = NULL;
332
333         r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/value/xuzz", "org.freedesktop.systemd.ValueTest", "Value", &error, &reply, "s");
334         assert_se(r >= 0);
335
336         r = sd_bus_message_read(reply, "s", &s);
337         assert_se(r >= 0);
338         log_info("read %s", s);
339
340         sd_bus_message_unref(reply);
341         reply = NULL;
342
343         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
344         assert_se(r >= 0);
345
346         r = sd_bus_message_read(reply, "s", &s);
347         assert_se(r >= 0);
348         fputs(s, stdout);
349
350         sd_bus_message_unref(reply);
351         reply = NULL;
352
353         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
354         assert_se(r >= 0);
355
356         r = sd_bus_message_read(reply, "s", &s);
357         assert_se(r >= 0);
358         fputs(s, stdout);
359
360         sd_bus_message_unref(reply);
361         reply = NULL;
362
363         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
364         assert_se(r >= 0);
365
366         r = sd_bus_message_read(reply, "s", &s);
367         assert_se(r >= 0);
368         fputs(s, stdout);
369
370         sd_bus_message_unref(reply);
371         reply = NULL;
372
373         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "");
374         assert_se(r >= 0);
375
376         bus_message_dump(reply);
377
378         sd_bus_message_unref(reply);
379         reply = NULL;
380
381         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "org.freedesktop.systemd.ValueTest2");
382         assert_se(r < 0);
383         assert_se(sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.UnknownInterface"));
384         sd_bus_error_free(&error);
385
386         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, "");
387         assert_se(r < 0);
388         assert_se(sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.UnknownMethod"));
389         sd_bus_error_free(&error);
390
391         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, "");
392         assert_se(r >= 0);
393
394         bus_message_dump(reply);
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.systemd.ValueTest", "NotifyTest", &error, NULL, "");
400         assert_se(r >= 0);
401
402         r = sd_bus_process(bus, &reply);
403         assert_se(r > 0);
404
405         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged"));
406         bus_message_dump(reply);
407
408         sd_bus_message_unref(reply);
409         reply = NULL;
410
411         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesAdded", &error, NULL, "");
412         assert_se(r >= 0);
413
414         r = sd_bus_process(bus, &reply);
415         assert_se(r > 0);
416
417         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"));
418         bus_message_dump(reply);
419
420         sd_bus_message_unref(reply);
421         reply = NULL;
422
423         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesRemoved", &error, NULL, "");
424         assert_se(r >= 0);
425
426         r = sd_bus_process(bus, &reply);
427         assert_se(r > 0);
428
429         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"));
430         bus_message_dump(reply);
431
432         sd_bus_message_unref(reply);
433         reply = NULL;
434
435         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Exit", &error, NULL, "");
436         assert_se(r >= 0);
437
438         sd_bus_flush(bus);
439
440         return 0;
441 }
442
443 int main(int argc, char *argv[]) {
444         struct context c;
445         pthread_t s;
446         void *p;
447         int r, q;
448
449         zero(c);
450
451         c.automatic_integer_property = 4711;
452         assert_se(c.automatic_string_property = strdup("dudeldu"));
453
454         assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, c.fds) >= 0);
455
456         r = pthread_create(&s, NULL, server, &c);
457         if (r != 0)
458                 return -r;
459
460         r = client(&c);
461
462         q = pthread_join(s, &p);
463         if (q != 0)
464                 return -q;
465
466         if (r < 0)
467                 return r;
468
469         if (PTR_TO_INT(p) < 0)
470                 return PTR_TO_INT(p);
471
472         free(c.something);
473         free(c.automatic_string_property);
474
475         return EXIT_SUCCESS;
476 }