chiark / gitweb /
bus: implement object handler registry
[elogind.git] / src / libsystemd-bus / test-bus-chat.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
27 #include "log.h"
28 #include "util.h"
29
30 #include "sd-bus.h"
31 #include "bus-message.h"
32 #include "bus-error.h"
33
34 static int object_callback(sd_bus *bus, int error, sd_bus_message *m, void *userdata) {
35         int r;
36
37         assert(bus);
38
39         if (error != 0)
40                 return 0;
41
42         if (sd_bus_message_is_method_call(m, "org.object.test", "Foobar")) {
43                 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
44
45                 log_info("Invoked Foobar() on %s", sd_bus_message_get_path(m));
46
47                 r = sd_bus_message_new_method_return(bus, m, &reply);
48                 if (r < 0) {
49                         log_error("Failed to allocate return: %s", strerror(-r));
50                         return r;
51                 }
52
53                 r = sd_bus_send(bus, reply, NULL);
54                 if (r < 0) {
55                         log_error("Failed to send reply: %s", strerror(-r));
56                         return r;
57                 }
58
59                 return 1;
60         }
61
62         return 0;
63 }
64
65 static int server_init(sd_bus **_bus) {
66         sd_bus *bus = NULL;
67         sd_id128_t id;
68         int r;
69         const char *unique;
70
71         assert(_bus);
72
73         r = sd_bus_open_user(&bus);
74         if (r < 0) {
75                 log_error("Failed to connect to user bus: %s", strerror(-r));
76                 goto fail;
77         }
78
79         r = sd_bus_get_peer(bus, &id);
80         if (r < 0) {
81                 log_error("Failed to get peer ID: %s", strerror(-r));
82                 goto fail;
83         }
84
85         r = sd_bus_get_unique_name(bus, &unique);
86         if (r < 0) {
87                 log_error("Failed to get unique name: %s", strerror(-r));
88                 goto fail;
89         }
90
91         log_info("Peer ID is " SD_ID128_FORMAT_STR ".", SD_ID128_FORMAT_VAL(id));
92         log_info("Unique ID: %s", unique);
93         log_info("Can send file handles: %i", sd_bus_can_send(bus, 'h'));
94
95         r = sd_bus_request_name(bus, "org.freedesktop.systemd.test", 0);
96         if (r < 0) {
97                 log_error("Failed to acquire name: %s", strerror(-r));
98                 goto fail;
99         }
100
101         r = sd_bus_add_fallback(bus, "/foo/bar", object_callback, NULL);
102         if (r < 0) {
103                 log_error("Failed to add object: %s", strerror(-r));
104                 goto fail;
105         }
106
107         *_bus = bus;
108         return 0;
109
110 fail:
111         if (bus)
112                 sd_bus_unref(bus);
113
114         return r;
115 }
116
117 static int server(sd_bus *bus) {
118         int r;
119         bool client1_gone = false, client2_gone = false;
120
121         while (!client1_gone || !client2_gone) {
122                 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
123                 pid_t pid = 0;
124
125                 r = sd_bus_process(bus, &m);
126                 if (r < 0) {
127                         log_error("Failed to process requests: %s", strerror(-r));
128                         goto fail;
129                 }
130
131                 if (r == 0) {
132                         r = sd_bus_wait(bus, (uint64_t) -1);
133                         if (r < 0) {
134                                 log_error("Failed to wait: %s", strerror(-r));
135                                 goto fail;
136                         }
137
138                         continue;
139                 }
140
141                 if (!m)
142                         continue;
143
144                 sd_bus_message_get_pid(m, &pid);
145                 log_info("Got message! member=%s pid=%lu label=%s", strna(sd_bus_message_get_member(m)), (unsigned long) pid, strna(sd_bus_message_get_label(m)));
146                 /* bus_message_dump(m); */
147                 /* sd_bus_message_rewind(m, true); */
148
149                 if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "LowerCase")) {
150                         const char *hello;
151                         _cleanup_free_ char *lowercase = NULL;
152
153                         r = sd_bus_message_read(m, "s", &hello);
154                         if (r < 0) {
155                                 log_error("Failed to get parameter: %s", strerror(-r));
156                                 goto fail;
157                         }
158
159                         r = sd_bus_message_new_method_return(bus, m, &reply);
160                         if (r < 0) {
161                                 log_error("Failed to allocate return: %s", strerror(-r));
162                                 goto fail;
163                         }
164
165                         lowercase = strdup(hello);
166                         if (!lowercase) {
167                                 r = log_oom();
168                                 goto fail;
169                         }
170
171                         ascii_strlower(lowercase);
172
173                         r = sd_bus_message_append(reply, "s", lowercase);
174                         if (r < 0) {
175                                 log_error("Failed to append message: %s", strerror(-r));
176                                 goto fail;
177                         }
178                 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient1")) {
179
180                         r = sd_bus_message_new_method_return(bus, m, &reply);
181                         if (r < 0) {
182                                 log_error("Failed to allocate return: %s", strerror(-r));
183                                 goto fail;
184                         }
185
186                         client1_gone = true;
187                 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient2")) {
188
189                         r = sd_bus_message_new_method_return(bus, m, &reply);
190                         if (r < 0) {
191                                 log_error("Failed to allocate return: %s", strerror(-r));
192                                 goto fail;
193                         }
194
195                         client2_gone = true;
196                 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "Slow")) {
197
198                         r = sd_bus_message_new_method_return(bus, m, &reply);
199                         if (r < 0) {
200                                 log_error("Failed to allocate return: %s", strerror(-r));
201                                 goto fail;
202                         }
203
204                         sleep(1);
205                 } else if (sd_bus_message_is_method_call(m, NULL, NULL)) {
206                         const sd_bus_error e = SD_BUS_ERROR_INIT_CONST("org.freedesktop.DBus.Error.UnknownMethod", "Unknown method.");
207
208                         r = sd_bus_message_new_method_error(bus, m, &e, &reply);
209                         if (r < 0) {
210                                 log_error("Failed to allocate return: %s", strerror(-r));
211                                 goto fail;
212                         }
213                 }
214
215                 if (reply) {
216                         r = sd_bus_send(bus, reply, NULL);
217                         if (r < 0) {
218                                 log_error("Failed to send reply: %s", strerror(-r));
219                                 goto fail;
220                         }
221
222                         /* log_info("Sent"); */
223                         /* bus_message_dump(reply); */
224                         /* sd_bus_message_rewind(reply, true); */
225                 }
226         }
227
228         r = 0;
229
230 fail:
231         if (bus) {
232                 sd_bus_flush(bus);
233                 sd_bus_unref(bus);
234         }
235
236         return r;
237 }
238
239 static void* client1(void*p) {
240         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
241         sd_bus *bus = NULL;
242         sd_bus_error error = SD_BUS_ERROR_INIT;
243         const char *hello;
244         int r;
245
246         r = sd_bus_open_user(&bus);
247         if (r < 0) {
248                 log_error("Failed to connect to user bus: %s", strerror(-r));
249                 goto finish;
250         }
251
252         r = sd_bus_message_new_method_call(
253                         bus,
254                         "org.freedesktop.systemd.test",
255                         "/",
256                         "org.freedesktop.systemd.test",
257                         "LowerCase",
258                         &m);
259         if (r < 0) {
260                 log_error("Failed to allocate method call: %s", strerror(-r));
261                 goto finish;
262         }
263
264         r = sd_bus_message_append(m, "s", "HELLO");
265         if (r < 0) {
266                 log_error("Failed to append string: %s", strerror(-r));
267                 goto finish;
268         }
269
270         r = sd_bus_send_with_reply_and_block(bus, m, 0, &error, &reply);
271         if (r < 0) {
272                 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
273                 goto finish;
274         }
275
276         r = sd_bus_message_read(reply, "s", &hello);
277         if (r < 0) {
278                 log_error("Failed to get string: %s", strerror(-r));
279                 goto finish;
280         }
281
282         assert(streq(hello, "hello"));
283
284         r = 0;
285
286 finish:
287         if (bus) {
288                 _cleanup_bus_message_unref_ sd_bus_message *q;
289
290                 r = sd_bus_message_new_method_call(
291                                 bus,
292                                 "org.freedesktop.systemd.test",
293                                 "/",
294                                 "org.freedesktop.systemd.test",
295                                 "ExitClient1",
296                                 &q);
297                 if (r < 0) {
298                         log_error("Failed to allocate method call: %s", strerror(-r));
299                         goto finish;
300                 }
301
302                 sd_bus_send(bus, q, NULL);
303                 sd_bus_flush(bus);
304                 sd_bus_unref(bus);
305         }
306
307         sd_bus_error_free(&error);
308         return INT_TO_PTR(r);
309 }
310
311 static int quit_callback(sd_bus *b, int ret, sd_bus_message *m, void *userdata) {
312         bool *x = userdata;
313
314         log_error("Quit callback: %s", strerror(ret));
315
316         *x = 1;
317         return 1;
318 }
319
320 static void* client2(void*p) {
321         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
322         sd_bus *bus = NULL;
323         sd_bus_error error = SD_BUS_ERROR_INIT;
324         bool quit = false;
325         const char *mid;
326         int r;
327
328         r = sd_bus_open_user(&bus);
329         if (r < 0) {
330                 log_error("Failed to connect to user bus: %s", strerror(-r));
331                 goto finish;
332         }
333
334         r = sd_bus_message_new_method_call(
335                         bus,
336                         "org.freedesktop.systemd.test",
337                         "/foo/bar/waldo/piep",
338                         "org.object.test",
339                         "Foobar",
340                         &m);
341         if (r < 0) {
342                 log_error("Failed to allocate method call: %s", strerror(-r));
343                 goto finish;
344         }
345
346         r = sd_bus_send(bus, m, NULL);
347         if (r < 0) {
348                 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
349                 goto finish;
350         }
351
352         sd_bus_message_unref(m);
353         m = NULL;
354
355         r = sd_bus_message_new_method_call(
356                         bus,
357                         "org.freedesktop.systemd.test",
358                         "/",
359                         "org.freedesktop.DBus.Peer",
360                         "GetMachineId",
361                         &m);
362         if (r < 0) {
363                 log_error("Failed to allocate method call: %s", strerror(-r));
364                 goto finish;
365         }
366
367         r = sd_bus_send_with_reply_and_block(bus, m, 0, &error, &reply);
368         if (r < 0) {
369                 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
370                 goto finish;
371         }
372
373         r = sd_bus_message_read(reply, "s", &mid);
374         if (r < 0) {
375                 log_error("Failed to parse machine ID: %s", strerror(-r));
376                 goto finish;
377         }
378
379         log_info("Machine ID is %s.", mid);
380
381         sd_bus_message_unref(m);
382         m = NULL;
383
384         r = sd_bus_message_new_method_call(
385                         bus,
386                         "org.freedesktop.systemd.test",
387                         "/",
388                         "org.freedesktop.systemd.test",
389                         "Slow",
390                         &m);
391         if (r < 0) {
392                 log_error("Failed to allocate method call: %s", strerror(-r));
393                 goto finish;
394         }
395
396         sd_bus_message_unref(reply);
397         reply = NULL;
398
399         r = sd_bus_send_with_reply_and_block(bus, m, 200 * USEC_PER_MSEC, &error, &reply);
400         if (r < 0)
401                 log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
402         else
403                 log_info("Slow call succeed.");
404
405         sd_bus_message_unref(m);
406         m = NULL;
407
408         r = sd_bus_message_new_method_call(
409                         bus,
410                         "org.freedesktop.systemd.test",
411                         "/",
412                         "org.freedesktop.systemd.test",
413                         "Slow",
414                         &m);
415         if (r < 0) {
416                 log_error("Failed to allocate method call: %s", strerror(-r));
417                 goto finish;
418         }
419
420         r = sd_bus_send_with_reply(bus, m, quit_callback, &quit, 200 * USEC_PER_MSEC, NULL);
421         if (r < 0) {
422                 log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
423                 goto finish;
424         }
425
426         while (!quit) {
427                 r = sd_bus_process(bus, NULL);
428                 if (r < 0) {
429                         log_error("Failed to process requests: %s", strerror(-r));
430                         goto finish;
431                 }
432                 if (r == 0) {
433                         r = sd_bus_wait(bus, (uint64_t) -1);
434                         if (r < 0) {
435                                 log_error("Failed to wait: %s", strerror(-r));
436                                 goto finish;
437                         }
438                 }
439         }
440
441         r = 0;
442
443 finish:
444         if (bus) {
445                 _cleanup_bus_message_unref_ sd_bus_message *q;
446
447                 r = sd_bus_message_new_method_call(
448                                 bus,
449                                 "org.freedesktop.systemd.test",
450                                 "/",
451                                 "org.freedesktop.systemd.test",
452                                 "ExitClient2",
453                                 &q);
454                 if (r < 0) {
455                         log_error("Failed to allocate method call: %s", strerror(-r));
456                         goto finish;
457                 }
458
459                 sd_bus_send(bus, q, NULL);
460                 sd_bus_flush(bus);
461                 sd_bus_unref(bus);
462         }
463
464         sd_bus_error_free(&error);
465         return INT_TO_PTR(r);
466 }
467
468 int main(int argc, char *argv[]) {
469         pthread_t c1, c2;
470         sd_bus *bus;
471         void *p;
472         int q, r;
473
474         r = server_init(&bus);
475         if (r < 0)
476                 return EXIT_FAILURE;
477
478         log_info("Initialized...");
479
480         r = pthread_create(&c1, NULL, client1, bus);
481         if (r != 0)
482                 return EXIT_FAILURE;
483
484         r = pthread_create(&c2, NULL, client2, bus);
485         if (r != 0)
486                 return EXIT_FAILURE;
487
488         r = server(bus);
489
490         q = pthread_join(c1, &p);
491         if (q != 0)
492                 return EXIT_FAILURE;
493         q = pthread_join(c2, &p);
494         if (q != 0)
495                 return EXIT_FAILURE;
496         if (r < 0)
497                 return EXIT_FAILURE;
498
499         return EXIT_SUCCESS;
500 }