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