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