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