]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/libsystemd-bus/test-bus-chat.c
bus: add convenience calls for method replies, too
[thirdparty/systemd.git] / src / libsystemd-bus / test-bus-chat.c
... / ...
CommitLineData
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
32#include "sd-bus.h"
33#include "bus-message.h"
34#include "bus-error.h"
35#include "bus-match.h"
36#include "bus-internal.h"
37
38static int match_callback(sd_bus *bus, int error, sd_bus_message *m, void *userdata) {
39 log_info("Match triggered! interface=%s member=%s", strna(sd_bus_message_get_interface(m)), strna(sd_bus_message_get_member(m)));
40 return 0;
41}
42
43static int object_callback(sd_bus *bus, int error, sd_bus_message *m, void *userdata) {
44 int r;
45
46 assert(bus);
47
48 if (error != 0)
49 return 0;
50
51 if (sd_bus_message_is_method_call(m, "org.object.test", "Foobar")) {
52 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
53
54 log_info("Invoked Foobar() on %s", sd_bus_message_get_path(m));
55
56 r = sd_bus_message_new_method_return(bus, m, &reply);
57 if (r < 0) {
58 log_error("Failed to allocate return: %s", strerror(-r));
59 return r;
60 }
61
62 r = sd_bus_send(bus, reply, NULL);
63 if (r < 0) {
64 log_error("Failed to send reply: %s", strerror(-r));
65 return r;
66 }
67
68 return 1;
69 }
70
71 return 0;
72}
73
74static int server_init(sd_bus **_bus) {
75 sd_bus *bus = NULL;
76 sd_id128_t id;
77 int r;
78 const char *unique;
79
80 assert(_bus);
81
82 r = sd_bus_open_user(&bus);
83 if (r < 0) {
84 log_error("Failed to connect to user bus: %s", strerror(-r));
85 goto fail;
86 }
87
88 r = sd_bus_get_server_id(bus, &id);
89 if (r < 0) {
90 log_error("Failed to get server ID: %s", strerror(-r));
91 goto fail;
92 }
93
94 r = sd_bus_get_unique_name(bus, &unique);
95 if (r < 0) {
96 log_error("Failed to get unique name: %s", strerror(-r));
97 goto fail;
98 }
99
100 log_info("Peer ID is " SD_ID128_FORMAT_STR ".", SD_ID128_FORMAT_VAL(id));
101 log_info("Unique ID: %s", unique);
102 log_info("Can send file handles: %i", sd_bus_can_send(bus, 'h'));
103
104 r = sd_bus_request_name(bus, "org.freedesktop.systemd.test", 0);
105 if (r < 0) {
106 log_error("Failed to acquire name: %s", strerror(-r));
107 goto fail;
108 }
109
110 r = sd_bus_add_fallback(bus, "/foo/bar", object_callback, NULL);
111 if (r < 0) {
112 log_error("Failed to add object: %s", strerror(-r));
113 goto fail;
114 }
115
116 r = sd_bus_add_match(bus, "type='signal',interface='foo.bar',member='Notify'", match_callback, NULL);
117 if (r < 0) {
118 log_error("Failed to add match: %s", strerror(-r));
119 goto fail;
120 }
121
122 r = sd_bus_add_match(bus, "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'", match_callback, NULL);
123 if (r < 0) {
124 log_error("Failed to add match: %s", strerror(-r));
125 goto fail;
126 }
127
128 bus_match_dump(&bus->match_callbacks, 0);
129
130 *_bus = bus;
131 return 0;
132
133fail:
134 if (bus)
135 sd_bus_unref(bus);
136
137 return r;
138}
139
140static int server(sd_bus *bus) {
141 int r;
142 bool client1_gone = false, client2_gone = false;
143
144 while (!client1_gone || !client2_gone) {
145 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
146 pid_t pid = 0;
147
148 r = sd_bus_process(bus, &m);
149 if (r < 0) {
150 log_error("Failed to process requests: %s", strerror(-r));
151 goto fail;
152 }
153
154 if (r == 0) {
155 r = sd_bus_wait(bus, (uint64_t) -1);
156 if (r < 0) {
157 log_error("Failed to wait: %s", strerror(-r));
158 goto fail;
159 }
160
161 continue;
162 }
163
164 if (!m)
165 continue;
166
167 sd_bus_message_get_pid(m, &pid);
168 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)));
169 /* bus_message_dump(m); */
170 /* sd_bus_message_rewind(m, true); */
171
172 if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "LowerCase")) {
173 const char *hello;
174 _cleanup_free_ char *lowercase = NULL;
175
176 r = sd_bus_message_read(m, "s", &hello);
177 if (r < 0) {
178 log_error("Failed to get parameter: %s", strerror(-r));
179 goto fail;
180 }
181
182 r = sd_bus_message_new_method_return(bus, m, &reply);
183 if (r < 0) {
184 log_error("Failed to allocate return: %s", strerror(-r));
185 goto fail;
186 }
187
188 lowercase = strdup(hello);
189 if (!lowercase) {
190 r = log_oom();
191 goto fail;
192 }
193
194 ascii_strlower(lowercase);
195
196 r = sd_bus_message_append(reply, "s", lowercase);
197 if (r < 0) {
198 log_error("Failed to append message: %s", strerror(-r));
199 goto fail;
200 }
201 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient1")) {
202
203 r = sd_bus_message_new_method_return(bus, m, &reply);
204 if (r < 0) {
205 log_error("Failed to allocate return: %s", strerror(-r));
206 goto fail;
207 }
208
209 client1_gone = true;
210 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient2")) {
211
212 r = sd_bus_message_new_method_return(bus, m, &reply);
213 if (r < 0) {
214 log_error("Failed to allocate return: %s", strerror(-r));
215 goto fail;
216 }
217
218 client2_gone = true;
219 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "Slow")) {
220
221 r = sd_bus_message_new_method_return(bus, m, &reply);
222 if (r < 0) {
223 log_error("Failed to allocate return: %s", strerror(-r));
224 goto fail;
225 }
226
227 sleep(1);
228
229 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "FileDescriptor")) {
230 int fd;
231 static const char x = 'X';
232
233 r = sd_bus_message_read(m, "h", &fd);
234 if (r < 0) {
235 log_error("Failed to get parameter: %s", strerror(-r));
236 goto fail;
237 }
238
239 if (write(fd, &x, 1) < 0) {
240 log_error("Failed to write to fd: %m");
241 close_nointr_nofail(fd);
242 goto fail;
243 }
244
245 close_nointr_nofail(fd);
246
247 r = sd_bus_message_new_method_return(bus, m, &reply);
248 if (r < 0) {
249 log_error("Failed to allocate return: %s", strerror(-r));
250 goto fail;
251 }
252
253 } else if (sd_bus_message_is_method_call(m, NULL, NULL)) {
254
255 r = sd_bus_message_new_method_error(
256 bus, m,
257 &SD_BUS_ERROR_MAKE("org.freedesktop.DBus.Error.UnknownMethod", "Unknown method."),
258 &reply);
259 if (r < 0) {
260 log_error("Failed to allocate return: %s", strerror(-r));
261 goto fail;
262 }
263 }
264
265 if (reply) {
266 r = sd_bus_send(bus, reply, NULL);
267 if (r < 0) {
268 log_error("Failed to send reply: %s", strerror(-r));
269 goto fail;
270 }
271
272 /* log_info("Sent"); */
273 /* bus_message_dump(reply); */
274 /* sd_bus_message_rewind(reply, true); */
275 }
276 }
277
278 r = 0;
279
280fail:
281 if (bus) {
282 sd_bus_flush(bus);
283 sd_bus_unref(bus);
284 }
285
286 return r;
287}
288
289static void* client1(void*p) {
290 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
291 sd_bus *bus = NULL;
292 sd_bus_error error = SD_BUS_ERROR_NULL;
293 const char *hello;
294 int r;
295 int pp[2] = { -1, -1 };
296 char x;
297
298 r = sd_bus_open_user(&bus);
299 if (r < 0) {
300 log_error("Failed to connect to user bus: %s", strerror(-r));
301 goto finish;
302 }
303
304 r = sd_bus_message_new_method_call(
305 bus,
306 "org.freedesktop.systemd.test",
307 "/",
308 "org.freedesktop.systemd.test",
309 "LowerCase",
310 &m);
311 if (r < 0) {
312 log_error("Failed to allocate method call: %s", strerror(-r));
313 goto finish;
314 }
315
316 r = sd_bus_message_append(m, "s", "HELLO");
317 if (r < 0) {
318 log_error("Failed to append string: %s", strerror(-r));
319 goto finish;
320 }
321
322 r = sd_bus_send_with_reply_and_block(bus, m, 0, &error, &reply);
323 if (r < 0) {
324 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
325 goto finish;
326 }
327
328 r = sd_bus_message_read(reply, "s", &hello);
329 if (r < 0) {
330 log_error("Failed to get string: %s", strerror(-r));
331 goto finish;
332 }
333
334 assert(streq(hello, "hello"));
335
336 if (pipe2(pp, O_CLOEXEC|O_NONBLOCK) < 0) {
337 log_error("Failed to allocate pipe: %m");
338 r = -errno;
339 goto finish;
340 }
341
342 sd_bus_message_unref(m);
343 m = NULL;
344 r = sd_bus_message_new_method_call(
345 bus,
346 "org.freedesktop.systemd.test",
347 "/",
348 "org.freedesktop.systemd.test",
349 "FileDescriptor",
350 &m);
351 if (r < 0) {
352 log_error("Failed to allocate method call: %s", strerror(-r));
353 goto finish;
354 }
355
356 r = sd_bus_message_append(m, "h", pp[1]);
357 if (r < 0) {
358 log_error("Failed to append string: %s", strerror(-r));
359 goto finish;
360 }
361
362 sd_bus_message_unref(reply);
363 reply = NULL;
364 r = sd_bus_send_with_reply_and_block(bus, m, 0, &error, &reply);
365 if (r < 0) {
366 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
367 goto finish;
368 }
369
370 errno = 0;
371 if (read(pp[0], &x, 1) <= 0) {
372 log_error("Failed to read from pipe: %s", errno ? strerror(errno) : "early read");
373 goto finish;
374 }
375
376 r = 0;
377
378finish:
379 if (bus) {
380 _cleanup_bus_message_unref_ sd_bus_message *q;
381
382 r = sd_bus_message_new_method_call(
383 bus,
384 "org.freedesktop.systemd.test",
385 "/",
386 "org.freedesktop.systemd.test",
387 "ExitClient1",
388 &q);
389 if (r < 0) {
390 log_error("Failed to allocate method call: %s", strerror(-r));
391 goto finish;
392 }
393
394 sd_bus_send(bus, q, NULL);
395 sd_bus_flush(bus);
396 sd_bus_unref(bus);
397 }
398
399 sd_bus_error_free(&error);
400
401 close_pipe(pp);
402
403 return INT_TO_PTR(r);
404}
405
406static int quit_callback(sd_bus *b, int ret, sd_bus_message *m, void *userdata) {
407 bool *x = userdata;
408
409 log_error("Quit callback: %s", strerror(ret));
410
411 *x = 1;
412 return 1;
413}
414
415static void* client2(void*p) {
416 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
417 sd_bus *bus = NULL;
418 sd_bus_error error = SD_BUS_ERROR_NULL;
419 bool quit = false;
420 const char *mid;
421 int r;
422
423 r = sd_bus_open_user(&bus);
424 if (r < 0) {
425 log_error("Failed to connect to user bus: %s", strerror(-r));
426 goto finish;
427 }
428
429 r = sd_bus_message_new_method_call(
430 bus,
431 "org.freedesktop.systemd.test",
432 "/foo/bar/waldo/piep",
433 "org.object.test",
434 "Foobar",
435 &m);
436 if (r < 0) {
437 log_error("Failed to allocate method call: %s", strerror(-r));
438 goto finish;
439 }
440
441 r = sd_bus_send(bus, m, NULL);
442 if (r < 0) {
443 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
444 goto finish;
445 }
446
447 sd_bus_message_unref(m);
448 m = NULL;
449
450 r = sd_bus_message_new_signal(
451 bus,
452 "/foobar",
453 "foo.bar",
454 "Notify",
455 &m);
456 if (r < 0) {
457 log_error("Failed to allocate signal: %s", strerror(-r));
458 goto finish;
459 }
460
461 r = sd_bus_send(bus, m, NULL);
462 if (r < 0) {
463 log_error("Failed to issue signal: %s", bus_error_message(&error, -r));
464 goto finish;
465 }
466
467 sd_bus_message_unref(m);
468 m = NULL;
469
470 r = sd_bus_message_new_method_call(
471 bus,
472 "org.freedesktop.systemd.test",
473 "/",
474 "org.freedesktop.DBus.Peer",
475 "GetMachineId",
476 &m);
477 if (r < 0) {
478 log_error("Failed to allocate method call: %s", strerror(-r));
479 goto finish;
480 }
481
482 r = sd_bus_send_with_reply_and_block(bus, m, 0, &error, &reply);
483 if (r < 0) {
484 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
485 goto finish;
486 }
487
488 r = sd_bus_message_read(reply, "s", &mid);
489 if (r < 0) {
490 log_error("Failed to parse machine ID: %s", strerror(-r));
491 goto finish;
492 }
493
494 log_info("Machine ID is %s.", mid);
495
496 sd_bus_message_unref(m);
497 m = NULL;
498
499 r = sd_bus_message_new_method_call(
500 bus,
501 "org.freedesktop.systemd.test",
502 "/",
503 "org.freedesktop.systemd.test",
504 "Slow",
505 &m);
506 if (r < 0) {
507 log_error("Failed to allocate method call: %s", strerror(-r));
508 goto finish;
509 }
510
511 sd_bus_message_unref(reply);
512 reply = NULL;
513
514 r = sd_bus_send_with_reply_and_block(bus, m, 200 * USEC_PER_MSEC, &error, &reply);
515 if (r < 0)
516 log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
517 else
518 log_info("Slow call succeed.");
519
520 sd_bus_message_unref(m);
521 m = NULL;
522
523 r = sd_bus_message_new_method_call(
524 bus,
525 "org.freedesktop.systemd.test",
526 "/",
527 "org.freedesktop.systemd.test",
528 "Slow",
529 &m);
530 if (r < 0) {
531 log_error("Failed to allocate method call: %s", strerror(-r));
532 goto finish;
533 }
534
535 r = sd_bus_send_with_reply(bus, m, quit_callback, &quit, 200 * USEC_PER_MSEC, NULL);
536 if (r < 0) {
537 log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
538 goto finish;
539 }
540
541 while (!quit) {
542 r = sd_bus_process(bus, NULL);
543 if (r < 0) {
544 log_error("Failed to process requests: %s", strerror(-r));
545 goto finish;
546 }
547 if (r == 0) {
548 r = sd_bus_wait(bus, (uint64_t) -1);
549 if (r < 0) {
550 log_error("Failed to wait: %s", strerror(-r));
551 goto finish;
552 }
553 }
554 }
555
556 r = 0;
557
558finish:
559 if (bus) {
560 _cleanup_bus_message_unref_ sd_bus_message *q;
561
562 r = sd_bus_message_new_method_call(
563 bus,
564 "org.freedesktop.systemd.test",
565 "/",
566 "org.freedesktop.systemd.test",
567 "ExitClient2",
568 &q);
569 if (r < 0) {
570 log_error("Failed to allocate method call: %s", strerror(-r));
571 goto finish;
572 }
573
574 sd_bus_send(bus, q, NULL);
575 sd_bus_flush(bus);
576 sd_bus_unref(bus);
577 }
578
579 sd_bus_error_free(&error);
580 return INT_TO_PTR(r);
581}
582
583int main(int argc, char *argv[]) {
584 pthread_t c1, c2;
585 sd_bus *bus;
586 void *p;
587 int q, r;
588
589 r = server_init(&bus);
590 if (r < 0) {
591 log_info("Failed to connect to bus, skipping tests.");
592 return EXIT_TEST_SKIP;
593 }
594
595 log_info("Initialized...");
596
597 r = pthread_create(&c1, NULL, client1, bus);
598 if (r != 0)
599 return EXIT_FAILURE;
600
601 r = pthread_create(&c2, NULL, client2, bus);
602 if (r != 0)
603 return EXIT_FAILURE;
604
605 r = server(bus);
606
607 q = pthread_join(c1, &p);
608 if (q != 0)
609 return EXIT_FAILURE;
610 if (PTR_TO_INT(p) < 0)
611 return EXIT_FAILURE;
612
613 q = pthread_join(c2, &p);
614 if (q != 0)
615 return EXIT_FAILURE;
616 if (PTR_TO_INT(p) < 0)
617 return EXIT_FAILURE;
618
619 if (r < 0)
620 return EXIT_FAILURE;
621
622 return EXIT_SUCCESS;
623}