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