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