chiark / gitweb /
bus: make PropertiesChanged emission work
[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
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 };
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 const sd_bus_vtable vtable[] = {
150         SD_BUS_VTABLE_START(0),
151         SD_BUS_METHOD("AlterSomething", "s", "s", 0, something_handler),
152         SD_BUS_METHOD("Exit", "", "", 0, exit_handler),
153         SD_BUS_WRITABLE_PROPERTY("Something", "s", get_handler, set_handler, 0, 0),
154         SD_BUS_VTABLE_END
155 };
156
157 static const sd_bus_vtable vtable2[] = {
158         SD_BUS_VTABLE_START(0),
159         SD_BUS_METHOD("NotifyTest", "", "", 0, notify_test),
160         SD_BUS_PROPERTY("Value", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
161         SD_BUS_VTABLE_END
162 };
163
164 static int enumerator_callback(sd_bus *b, const char *path, char ***nodes, void *userdata) {
165
166         if (object_path_startswith("/value", path))
167                 assert_se(*nodes = strv_new("/value/a", "/value/b", "/value/c", NULL));
168
169         return 1;
170 }
171
172 static void *server(void *p) {
173         struct context *c = p;
174         sd_bus *bus = NULL;
175         sd_id128_t id;
176         int r;
177
178         c->quit = false;
179
180         assert_se(sd_id128_randomize(&id) >= 0);
181
182         assert_se(sd_bus_new(&bus) >= 0);
183         assert_se(sd_bus_set_fd(bus, c->fds[0], c->fds[0]) >= 0);
184         assert_se(sd_bus_set_server(bus, 1, id) >= 0);
185
186         assert_se(sd_bus_add_object_vtable(bus, "/foo", "org.freedesktop.systemd.test", vtable, c) >= 0);
187         assert_se(sd_bus_add_object_vtable(bus, "/foo", "org.freedesktop.systemd.test2", vtable, c) >= 0);
188         assert_se(sd_bus_add_fallback_vtable(bus, "/value", "org.freedesktop.systemd.ValueTest", vtable2, NULL, UINT_TO_PTR(20)) >= 0);
189         assert_se(sd_bus_add_node_enumerator(bus, "/value", enumerator_callback, NULL) >= 0);
190         assert_se(sd_bus_add_object_manager(bus, "/value") >= 0);
191
192         assert_se(sd_bus_start(bus) >= 0);
193
194         log_error("Entering event loop on server");
195
196         while (!c->quit) {
197                 log_error("Loop!");
198
199                 r = sd_bus_process(bus, NULL);
200                 if (r < 0) {
201                         log_error("Failed to process requests: %s", strerror(-r));
202                         goto fail;
203                 }
204
205                 if (r == 0) {
206                         r = sd_bus_wait(bus, (uint64_t) -1);
207                         if (r < 0) {
208                                 log_error("Failed to wait: %s", strerror(-r));
209                                 goto fail;
210                         }
211
212                         continue;
213                 }
214         }
215
216         r = 0;
217
218 fail:
219         if (bus) {
220                 sd_bus_flush(bus);
221                 sd_bus_unref(bus);
222         }
223
224         return INT_TO_PTR(r);
225 }
226
227 static int client(struct context *c) {
228         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
229         _cleanup_bus_unref_ sd_bus *bus = NULL;
230         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
231         const char *s;
232         int r;
233
234         assert_se(sd_bus_new(&bus) >= 0);
235         assert_se(sd_bus_set_fd(bus, c->fds[1], c->fds[1]) >= 0);
236         assert_se(sd_bus_start(bus) >= 0);
237
238         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "s", "hallo");
239         assert_se(r >= 0);
240
241         r = sd_bus_message_read(reply, "s", &s);
242         assert_se(r >= 0);
243         assert_se(streq(s, "<<<hallo>>>"));
244
245         sd_bus_message_unref(reply);
246         reply = NULL;
247
248         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Doesntexist", &error, &reply, "");
249         assert_se(r < 0);
250         assert_se(sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.UnknownMethod"));
251
252         sd_bus_error_free(&error);
253
254         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "as", 1, "hallo");
255         assert_se(r < 0);
256         assert_se(sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.InvalidArgs"));
257
258         sd_bus_error_free(&error);
259
260         r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, &reply, "s");
261         assert_se(r >= 0);
262
263         r = sd_bus_message_read(reply, "s", &s);
264         assert_se(r >= 0);
265         assert_se(streq(s, "<<<hallo>>>"));
266
267         sd_bus_message_unref(reply);
268         reply = NULL;
269
270         r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, "s", "test");
271         assert_se(r >= 0);
272
273         r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, &reply, "s");
274         assert_se(r >= 0);
275
276         r = sd_bus_message_read(reply, "s", &s);
277         assert_se(r >= 0);
278         assert_se(streq(s, "test"));
279
280         sd_bus_message_unref(reply);
281         reply = NULL;
282
283         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
284         assert_se(r >= 0);
285
286         r = sd_bus_message_read(reply, "s", &s);
287         assert_se(r >= 0);
288         fputs(s, stdout);
289
290         sd_bus_message_unref(reply);
291         reply = NULL;
292
293         r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/value/xuzz", "org.freedesktop.systemd.ValueTest", "Value", &error, &reply, "s");
294         assert_se(r >= 0);
295
296         r = sd_bus_message_read(reply, "s", &s);
297         assert_se(r >= 0);
298         log_info("read %s", s);
299
300         sd_bus_message_unref(reply);
301         reply = NULL;
302
303         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
304         assert_se(r >= 0);
305
306         r = sd_bus_message_read(reply, "s", &s);
307         assert_se(r >= 0);
308         fputs(s, stdout);
309
310         sd_bus_message_unref(reply);
311         reply = NULL;
312
313         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
314         assert_se(r >= 0);
315
316         r = sd_bus_message_read(reply, "s", &s);
317         assert_se(r >= 0);
318         fputs(s, stdout);
319
320         sd_bus_message_unref(reply);
321         reply = NULL;
322
323         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "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_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "");
334         assert_se(r >= 0);
335
336         bus_message_dump(reply);
337
338         sd_bus_message_unref(reply);
339         reply = NULL;
340
341         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "org.freedesktop.systemd.ValueTest2");
342         assert_se(r < 0);
343         assert_se(sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.UnknownInterface"));
344         sd_bus_error_free(&error);
345
346         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, "");
347         assert_se(r < 0);
348         assert_se(sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.UnknownMethod"));
349         sd_bus_error_free(&error);
350
351         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, "");
352         assert_se(r >= 0);
353
354         bus_message_dump(reply);
355
356         sd_bus_message_unref(reply);
357         reply = NULL;
358
359         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.systemd.ValueTest", "NotifyTest", &error, NULL, "");
360         assert_se(r >= 0);
361
362         r = sd_bus_process(bus, &reply);
363         assert_se(r > 0);
364
365         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged"));
366         bus_message_dump(reply);
367
368         sd_bus_message_unref(reply);
369         reply = NULL;
370
371         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Exit", &error, NULL, "");
372         assert_se(r >= 0);
373
374         sd_bus_flush(bus);
375
376         return 0;
377 }
378
379 int main(int argc, char *argv[]) {
380         struct context c;
381         pthread_t s;
382         void *p;
383         int r, q;
384
385         zero(c);
386
387         assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, c.fds) >= 0);
388
389         r = pthread_create(&s, NULL, server, &c);
390         if (r != 0)
391                 return -r;
392
393         r = client(&c);
394
395         q = pthread_join(s, &p);
396         if (q != 0)
397                 return -q;
398
399         if (r < 0)
400                 return r;
401
402         if (PTR_TO_INT(p) < 0)
403                 return PTR_TO_INT(p);
404
405         free(c.something);
406
407         return EXIT_SUCCESS;
408 }