]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-bus/test-bus-chat.c
459d39fdee5070664fc6f1103b621c2d6a9debff
[thirdparty/systemd.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 #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
38 static 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
43 static 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
74 static 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
133 fail:
134 if (bus)
135 sd_bus_unref(bus);
136
137 return r;
138 }
139
140 static 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 const sd_bus_error e = SD_BUS_ERROR_INIT_CONST("org.freedesktop.DBus.Error.UnknownMethod", "Unknown method.");
255
256 r = sd_bus_message_new_method_error(bus, m, &e, &reply);
257 if (r < 0) {
258 log_error("Failed to allocate return: %s", strerror(-r));
259 goto fail;
260 }
261 }
262
263 if (reply) {
264 r = sd_bus_send(bus, reply, NULL);
265 if (r < 0) {
266 log_error("Failed to send reply: %s", strerror(-r));
267 goto fail;
268 }
269
270 /* log_info("Sent"); */
271 /* bus_message_dump(reply); */
272 /* sd_bus_message_rewind(reply, true); */
273 }
274 }
275
276 r = 0;
277
278 fail:
279 if (bus) {
280 sd_bus_flush(bus);
281 sd_bus_unref(bus);
282 }
283
284 return r;
285 }
286
287 static void* client1(void*p) {
288 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
289 sd_bus *bus = NULL;
290 sd_bus_error error = SD_BUS_ERROR_INIT;
291 const char *hello;
292 int r;
293 int pp[2] = { -1, -1 };
294 char x;
295
296 r = sd_bus_open_user(&bus);
297 if (r < 0) {
298 log_error("Failed to connect to user bus: %s", strerror(-r));
299 goto finish;
300 }
301
302 r = sd_bus_message_new_method_call(
303 bus,
304 "org.freedesktop.systemd.test",
305 "/",
306 "org.freedesktop.systemd.test",
307 "LowerCase",
308 &m);
309 if (r < 0) {
310 log_error("Failed to allocate method call: %s", strerror(-r));
311 goto finish;
312 }
313
314 r = sd_bus_message_append(m, "s", "HELLO");
315 if (r < 0) {
316 log_error("Failed to append string: %s", strerror(-r));
317 goto finish;
318 }
319
320 r = sd_bus_send_with_reply_and_block(bus, m, 0, &error, &reply);
321 if (r < 0) {
322 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
323 goto finish;
324 }
325
326 r = sd_bus_message_read(reply, "s", &hello);
327 if (r < 0) {
328 log_error("Failed to get string: %s", strerror(-r));
329 goto finish;
330 }
331
332 assert(streq(hello, "hello"));
333
334 if (pipe2(pp, O_CLOEXEC|O_NONBLOCK) < 0) {
335 log_error("Failed to allocate pipe: %m");
336 r = -errno;
337 goto finish;
338 }
339
340 sd_bus_message_unref(m);
341 m = NULL;
342 r = sd_bus_message_new_method_call(
343 bus,
344 "org.freedesktop.systemd.test",
345 "/",
346 "org.freedesktop.systemd.test",
347 "FileDescriptor",
348 &m);
349 if (r < 0) {
350 log_error("Failed to allocate method call: %s", strerror(-r));
351 goto finish;
352 }
353
354 r = sd_bus_message_append(m, "h", pp[1]);
355 if (r < 0) {
356 log_error("Failed to append string: %s", strerror(-r));
357 goto finish;
358 }
359
360 sd_bus_message_unref(reply);
361 reply = NULL;
362 r = sd_bus_send_with_reply_and_block(bus, m, 0, &error, &reply);
363 if (r < 0) {
364 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
365 goto finish;
366 }
367
368 errno = 0;
369 if (read(pp[0], &x, 1) <= 0) {
370 log_error("Failed to read from pipe: %s", errno ? strerror(errno) : "early read");
371 goto finish;
372 }
373
374 r = 0;
375
376 finish:
377 if (bus) {
378 _cleanup_bus_message_unref_ sd_bus_message *q;
379
380 r = sd_bus_message_new_method_call(
381 bus,
382 "org.freedesktop.systemd.test",
383 "/",
384 "org.freedesktop.systemd.test",
385 "ExitClient1",
386 &q);
387 if (r < 0) {
388 log_error("Failed to allocate method call: %s", strerror(-r));
389 goto finish;
390 }
391
392 sd_bus_send(bus, q, NULL);
393 sd_bus_flush(bus);
394 sd_bus_unref(bus);
395 }
396
397 sd_bus_error_free(&error);
398
399 close_pipe(pp);
400
401 return INT_TO_PTR(r);
402 }
403
404 static int quit_callback(sd_bus *b, int ret, sd_bus_message *m, void *userdata) {
405 bool *x = userdata;
406
407 log_error("Quit callback: %s", strerror(ret));
408
409 *x = 1;
410 return 1;
411 }
412
413 static void* client2(void*p) {
414 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
415 sd_bus *bus = NULL;
416 sd_bus_error error = SD_BUS_ERROR_INIT;
417 bool quit = false;
418 const char *mid;
419 int r;
420
421 r = sd_bus_open_user(&bus);
422 if (r < 0) {
423 log_error("Failed to connect to user bus: %s", strerror(-r));
424 goto finish;
425 }
426
427 r = sd_bus_message_new_method_call(
428 bus,
429 "org.freedesktop.systemd.test",
430 "/foo/bar/waldo/piep",
431 "org.object.test",
432 "Foobar",
433 &m);
434 if (r < 0) {
435 log_error("Failed to allocate method call: %s", strerror(-r));
436 goto finish;
437 }
438
439 r = sd_bus_send(bus, m, NULL);
440 if (r < 0) {
441 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
442 goto finish;
443 }
444
445 sd_bus_message_unref(m);
446 m = NULL;
447
448 r = sd_bus_message_new_signal(
449 bus,
450 "/foobar",
451 "foo.bar",
452 "Notify",
453 &m);
454 if (r < 0) {
455 log_error("Failed to allocate signal: %s", strerror(-r));
456 goto finish;
457 }
458
459 r = sd_bus_send(bus, m, NULL);
460 if (r < 0) {
461 log_error("Failed to issue signal: %s", bus_error_message(&error, -r));
462 goto finish;
463 }
464
465 sd_bus_message_unref(m);
466 m = NULL;
467
468 r = sd_bus_message_new_method_call(
469 bus,
470 "org.freedesktop.systemd.test",
471 "/",
472 "org.freedesktop.DBus.Peer",
473 "GetMachineId",
474 &m);
475 if (r < 0) {
476 log_error("Failed to allocate method call: %s", strerror(-r));
477 goto finish;
478 }
479
480 r = sd_bus_send_with_reply_and_block(bus, m, 0, &error, &reply);
481 if (r < 0) {
482 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
483 goto finish;
484 }
485
486 r = sd_bus_message_read(reply, "s", &mid);
487 if (r < 0) {
488 log_error("Failed to parse machine ID: %s", strerror(-r));
489 goto finish;
490 }
491
492 log_info("Machine ID is %s.", mid);
493
494 sd_bus_message_unref(m);
495 m = NULL;
496
497 r = sd_bus_message_new_method_call(
498 bus,
499 "org.freedesktop.systemd.test",
500 "/",
501 "org.freedesktop.systemd.test",
502 "Slow",
503 &m);
504 if (r < 0) {
505 log_error("Failed to allocate method call: %s", strerror(-r));
506 goto finish;
507 }
508
509 sd_bus_message_unref(reply);
510 reply = NULL;
511
512 r = sd_bus_send_with_reply_and_block(bus, m, 200 * USEC_PER_MSEC, &error, &reply);
513 if (r < 0)
514 log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
515 else
516 log_info("Slow call succeed.");
517
518 sd_bus_message_unref(m);
519 m = NULL;
520
521 r = sd_bus_message_new_method_call(
522 bus,
523 "org.freedesktop.systemd.test",
524 "/",
525 "org.freedesktop.systemd.test",
526 "Slow",
527 &m);
528 if (r < 0) {
529 log_error("Failed to allocate method call: %s", strerror(-r));
530 goto finish;
531 }
532
533 r = sd_bus_send_with_reply(bus, m, quit_callback, &quit, 200 * USEC_PER_MSEC, NULL);
534 if (r < 0) {
535 log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
536 goto finish;
537 }
538
539 while (!quit) {
540 r = sd_bus_process(bus, NULL);
541 if (r < 0) {
542 log_error("Failed to process requests: %s", strerror(-r));
543 goto finish;
544 }
545 if (r == 0) {
546 r = sd_bus_wait(bus, (uint64_t) -1);
547 if (r < 0) {
548 log_error("Failed to wait: %s", strerror(-r));
549 goto finish;
550 }
551 }
552 }
553
554 r = 0;
555
556 finish:
557 if (bus) {
558 _cleanup_bus_message_unref_ sd_bus_message *q;
559
560 r = sd_bus_message_new_method_call(
561 bus,
562 "org.freedesktop.systemd.test",
563 "/",
564 "org.freedesktop.systemd.test",
565 "ExitClient2",
566 &q);
567 if (r < 0) {
568 log_error("Failed to allocate method call: %s", strerror(-r));
569 goto finish;
570 }
571
572 sd_bus_send(bus, q, NULL);
573 sd_bus_flush(bus);
574 sd_bus_unref(bus);
575 }
576
577 sd_bus_error_free(&error);
578 return INT_TO_PTR(r);
579 }
580
581 int main(int argc, char *argv[]) {
582 pthread_t c1, c2;
583 sd_bus *bus;
584 void *p;
585 int q, r;
586
587 r = server_init(&bus);
588 if (r < 0) {
589 log_info("Failed to connect to bus, skipping tests.");
590 return EXIT_TEST_SKIP;
591 }
592
593 log_info("Initialized...");
594
595 r = pthread_create(&c1, NULL, client1, bus);
596 if (r != 0)
597 return EXIT_FAILURE;
598
599 r = pthread_create(&c2, NULL, client2, bus);
600 if (r != 0)
601 return EXIT_FAILURE;
602
603 r = server(bus);
604
605 q = pthread_join(c1, &p);
606 if (q != 0)
607 return EXIT_FAILURE;
608 if (PTR_TO_INT(p) < 0)
609 return EXIT_FAILURE;
610
611 q = pthread_join(c2, &p);
612 if (q != 0)
613 return EXIT_FAILURE;
614 if (PTR_TO_INT(p) < 0)
615 return EXIT_FAILURE;
616
617 if (r < 0)
618 return EXIT_FAILURE;
619
620 return EXIT_SUCCESS;
621 }