]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/test-bus-chat.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / libsystemd / sd-bus / test-bus-chat.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2013 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <fcntl.h>
22 #include <pthread.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25
26 #include "sd-bus.h"
27
28 #include "alloc-util.h"
29 #include "bus-error.h"
30 #include "bus-internal.h"
31 #include "bus-match.h"
32 #include "bus-util.h"
33 #include "fd-util.h"
34 #include "format-util.h"
35 #include "log.h"
36 #include "macro.h"
37 #include "util.h"
38
39 static int match_callback(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_message *m, void *userdata, sd_bus_error *ret_error) {
45 int r;
46
47 if (sd_bus_message_is_method_error(m, NULL))
48 return 0;
49
50 if (sd_bus_message_is_method_call(m, "org.object.test", "Foobar")) {
51 log_info("Invoked Foobar() on %s", sd_bus_message_get_path(m));
52
53 r = sd_bus_reply_method_return(m, NULL);
54 if (r < 0)
55 return log_error_errno(r, "Failed to send reply: %m");
56
57 return 1;
58 }
59
60 return 0;
61 }
62
63 static int server_init(sd_bus **_bus) {
64 sd_bus *bus = NULL;
65 sd_id128_t id;
66 int r;
67 const char *unique;
68
69 assert_se(_bus);
70
71 r = sd_bus_open_user(&bus);
72 if (r < 0) {
73 log_error_errno(r, "Failed to connect to user bus: %m");
74 goto fail;
75 }
76
77 r = sd_bus_get_bus_id(bus, &id);
78 if (r < 0) {
79 log_error_errno(r, "Failed to get server ID: %m");
80 goto fail;
81 }
82
83 r = sd_bus_get_unique_name(bus, &unique);
84 if (r < 0) {
85 log_error_errno(r, "Failed to get unique name: %m");
86 goto fail;
87 }
88
89 log_info("Peer ID is " SD_ID128_FORMAT_STR ".", SD_ID128_FORMAT_VAL(id));
90 log_info("Unique ID: %s", unique);
91 log_info("Can send file handles: %i", sd_bus_can_send(bus, 'h'));
92
93 r = sd_bus_request_name(bus, "org.freedesktop.systemd.test", 0);
94 if (r < 0) {
95 log_error_errno(r, "Failed to acquire name: %m");
96 goto fail;
97 }
98
99 r = sd_bus_add_fallback(bus, NULL, "/foo/bar", object_callback, NULL);
100 if (r < 0) {
101 log_error_errno(r, "Failed to add object: %m");
102 goto fail;
103 }
104
105 r = sd_bus_add_match(bus, NULL, "type='signal',interface='foo.bar',member='Notify'", match_callback, NULL);
106 if (r < 0) {
107 log_error_errno(r, "Failed to add match: %m");
108 goto fail;
109 }
110
111 r = sd_bus_add_match(bus, NULL, "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'", match_callback, NULL);
112 if (r < 0) {
113 log_error_errno(r, "Failed to add match: %m");
114 goto fail;
115 }
116
117 bus_match_dump(&bus->match_callbacks, 0);
118
119 *_bus = bus;
120 return 0;
121
122 fail:
123 sd_bus_unref(bus);
124 return r;
125 }
126
127 static int server(sd_bus *bus) {
128 int r;
129 bool client1_gone = false, client2_gone = false;
130
131 while (!client1_gone || !client2_gone) {
132 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
133 pid_t pid = 0;
134 const char *label = NULL;
135
136 r = sd_bus_process(bus, &m);
137 if (r < 0) {
138 log_error_errno(r, "Failed to process requests: %m");
139 goto fail;
140 }
141
142 if (r == 0) {
143 r = sd_bus_wait(bus, (uint64_t) -1);
144 if (r < 0) {
145 log_error_errno(r, "Failed to wait: %m");
146 goto fail;
147 }
148
149 continue;
150 }
151
152 if (!m)
153 continue;
154
155 sd_bus_creds_get_pid(sd_bus_message_get_creds(m), &pid);
156 sd_bus_creds_get_selinux_context(sd_bus_message_get_creds(m), &label);
157 log_info("Got message! member=%s pid="PID_FMT" label=%s",
158 strna(sd_bus_message_get_member(m)),
159 pid,
160 strna(label));
161 /* bus_message_dump(m); */
162 /* sd_bus_message_rewind(m, true); */
163
164 if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "LowerCase")) {
165 const char *hello;
166 _cleanup_free_ char *lowercase = NULL;
167
168 r = sd_bus_message_read(m, "s", &hello);
169 if (r < 0) {
170 log_error_errno(r, "Failed to get parameter: %m");
171 goto fail;
172 }
173
174 lowercase = strdup(hello);
175 if (!lowercase) {
176 r = log_oom();
177 goto fail;
178 }
179
180 ascii_strlower(lowercase);
181
182 r = sd_bus_reply_method_return(m, "s", lowercase);
183 if (r < 0) {
184 log_error_errno(r, "Failed to send reply: %m");
185 goto fail;
186 }
187 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient1")) {
188
189 r = sd_bus_reply_method_return(m, NULL);
190 if (r < 0) {
191 log_error_errno(r, "Failed to send reply: %m");
192 goto fail;
193 }
194
195 client1_gone = true;
196 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient2")) {
197
198 r = sd_bus_reply_method_return(m, NULL);
199 if (r < 0) {
200 log_error_errno(r, "Failed to send reply: %m");
201 goto fail;
202 }
203
204 client2_gone = true;
205 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "Slow")) {
206
207 sleep(1);
208
209 r = sd_bus_reply_method_return(m, NULL);
210 if (r < 0) {
211 log_error_errno(r, "Failed to send reply: %m");
212 goto fail;
213 }
214
215 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "FileDescriptor")) {
216 int fd;
217 static const char x = 'X';
218
219 r = sd_bus_message_read(m, "h", &fd);
220 if (r < 0) {
221 log_error_errno(r, "Failed to get parameter: %m");
222 goto fail;
223 }
224
225 log_info("Received fd=%d", fd);
226
227 if (write(fd, &x, 1) < 0) {
228 log_error_errno(errno, "Failed to write to fd: %m");
229 safe_close(fd);
230 goto fail;
231 }
232
233 r = sd_bus_reply_method_return(m, NULL);
234 if (r < 0) {
235 log_error_errno(r, "Failed to send reply: %m");
236 goto fail;
237 }
238
239 } else if (sd_bus_message_is_method_call(m, NULL, NULL)) {
240
241 r = sd_bus_reply_method_error(
242 m,
243 &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method."));
244 if (r < 0) {
245 log_error_errno(r, "Failed to send reply: %m");
246 goto fail;
247 }
248 }
249 }
250
251 r = 0;
252
253 fail:
254 if (bus) {
255 sd_bus_flush(bus);
256 sd_bus_unref(bus);
257 }
258
259 return r;
260 }
261
262 static void* client1(void*p) {
263 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
264 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
265 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
266 const char *hello;
267 int r;
268 _cleanup_close_pair_ int pp[2] = { -1, -1 };
269 char x;
270
271 r = sd_bus_open_user(&bus);
272 if (r < 0) {
273 log_error_errno(r, "Failed to connect to user bus: %m");
274 goto finish;
275 }
276
277 r = sd_bus_call_method(
278 bus,
279 "org.freedesktop.systemd.test",
280 "/",
281 "org.freedesktop.systemd.test",
282 "LowerCase",
283 &error,
284 &reply,
285 "s",
286 "HELLO");
287 if (r < 0) {
288 log_error_errno(r, "Failed to issue method call: %m");
289 goto finish;
290 }
291
292 r = sd_bus_message_read(reply, "s", &hello);
293 if (r < 0) {
294 log_error_errno(r, "Failed to get string: %m");
295 goto finish;
296 }
297
298 assert_se(streq(hello, "hello"));
299
300 if (pipe2(pp, O_CLOEXEC|O_NONBLOCK) < 0) {
301 log_error_errno(errno, "Failed to allocate pipe: %m");
302 r = -errno;
303 goto finish;
304 }
305
306 log_info("Sending fd=%d", pp[1]);
307
308 r = sd_bus_call_method(
309 bus,
310 "org.freedesktop.systemd.test",
311 "/",
312 "org.freedesktop.systemd.test",
313 "FileDescriptor",
314 &error,
315 NULL,
316 "h",
317 pp[1]);
318 if (r < 0) {
319 log_error_errno(r, "Failed to issue method call: %m");
320 goto finish;
321 }
322
323 errno = 0;
324 if (read(pp[0], &x, 1) <= 0) {
325 log_error("Failed to read from pipe: %s", errno ? strerror(errno) : "early read");
326 goto finish;
327 }
328
329 r = 0;
330
331 finish:
332 if (bus) {
333 _cleanup_(sd_bus_message_unrefp) sd_bus_message *q;
334
335 r = sd_bus_message_new_method_call(
336 bus,
337 &q,
338 "org.freedesktop.systemd.test",
339 "/",
340 "org.freedesktop.systemd.test",
341 "ExitClient1");
342 if (r < 0)
343 log_error_errno(r, "Failed to allocate method call: %m");
344 else
345 sd_bus_send(bus, q, NULL);
346
347 }
348
349 return INT_TO_PTR(r);
350 }
351
352 static int quit_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
353 bool *x = userdata;
354
355 log_error_errno(sd_bus_message_get_errno(m), "Quit callback: %m");
356
357 *x = 1;
358 return 1;
359 }
360
361 static void* client2(void*p) {
362 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
363 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
364 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
365 bool quit = false;
366 const char *mid;
367 int r;
368
369 r = sd_bus_open_user(&bus);
370 if (r < 0) {
371 log_error_errno(r, "Failed to connect to user bus: %m");
372 goto finish;
373 }
374
375 r = sd_bus_message_new_method_call(
376 bus,
377 &m,
378 "org.freedesktop.systemd.test",
379 "/foo/bar/waldo/piep",
380 "org.object.test",
381 "Foobar");
382 if (r < 0) {
383 log_error_errno(r, "Failed to allocate method call: %m");
384 goto finish;
385 }
386
387 r = sd_bus_send(bus, m, NULL);
388 if (r < 0) {
389 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
390 goto finish;
391 }
392
393 m = sd_bus_message_unref(m);
394
395 r = sd_bus_message_new_signal(
396 bus,
397 &m,
398 "/foobar",
399 "foo.bar",
400 "Notify");
401 if (r < 0) {
402 log_error_errno(r, "Failed to allocate signal: %m");
403 goto finish;
404 }
405
406 r = sd_bus_send(bus, m, NULL);
407 if (r < 0) {
408 log_error("Failed to issue signal: %s", bus_error_message(&error, -r));
409 goto finish;
410 }
411
412 m = sd_bus_message_unref(m);
413
414 r = sd_bus_message_new_method_call(
415 bus,
416 &m,
417 "org.freedesktop.systemd.test",
418 "/",
419 "org.freedesktop.DBus.Peer",
420 "GetMachineId");
421 if (r < 0) {
422 log_error_errno(r, "Failed to allocate method call: %m");
423 goto finish;
424 }
425
426 r = sd_bus_call(bus, m, 0, &error, &reply);
427 if (r < 0) {
428 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
429 goto finish;
430 }
431
432 r = sd_bus_message_read(reply, "s", &mid);
433 if (r < 0) {
434 log_error_errno(r, "Failed to parse machine ID: %m");
435 goto finish;
436 }
437
438 log_info("Machine ID is %s.", mid);
439
440 m = sd_bus_message_unref(m);
441
442 r = sd_bus_message_new_method_call(
443 bus,
444 &m,
445 "org.freedesktop.systemd.test",
446 "/",
447 "org.freedesktop.systemd.test",
448 "Slow");
449 if (r < 0) {
450 log_error_errno(r, "Failed to allocate method call: %m");
451 goto finish;
452 }
453
454 reply = sd_bus_message_unref(reply);
455
456 r = sd_bus_call(bus, m, 200 * USEC_PER_MSEC, &error, &reply);
457 if (r < 0)
458 log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
459 else
460 log_info("Slow call succeed.");
461
462 m = sd_bus_message_unref(m);
463
464 r = sd_bus_message_new_method_call(
465 bus,
466 &m,
467 "org.freedesktop.systemd.test",
468 "/",
469 "org.freedesktop.systemd.test",
470 "Slow");
471 if (r < 0) {
472 log_error_errno(r, "Failed to allocate method call: %m");
473 goto finish;
474 }
475
476 r = sd_bus_call_async(bus, NULL, m, quit_callback, &quit, 200 * USEC_PER_MSEC);
477 if (r < 0) {
478 log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
479 goto finish;
480 }
481
482 while (!quit) {
483 r = sd_bus_process(bus, NULL);
484 if (r < 0) {
485 log_error_errno(r, "Failed to process requests: %m");
486 goto finish;
487 }
488 if (r == 0) {
489 r = sd_bus_wait(bus, (uint64_t) -1);
490 if (r < 0) {
491 log_error_errno(r, "Failed to wait: %m");
492 goto finish;
493 }
494 }
495 }
496
497 r = 0;
498
499 finish:
500 if (bus) {
501 _cleanup_(sd_bus_message_unrefp) sd_bus_message *q;
502
503 r = sd_bus_message_new_method_call(
504 bus,
505 &q,
506 "org.freedesktop.systemd.test",
507 "/",
508 "org.freedesktop.systemd.test",
509 "ExitClient2");
510 if (r < 0) {
511 log_error_errno(r, "Failed to allocate method call: %m");
512 goto finish;
513 }
514
515 (void) sd_bus_send(bus, q, NULL);
516 }
517
518 return INT_TO_PTR(r);
519 }
520
521 int main(int argc, char *argv[]) {
522 pthread_t c1, c2;
523 sd_bus *bus;
524 void *p;
525 int q, r;
526
527 r = server_init(&bus);
528 if (r < 0) {
529 log_info("Failed to connect to bus, skipping tests.");
530 return EXIT_TEST_SKIP;
531 }
532
533 log_info("Initialized...");
534
535 r = pthread_create(&c1, NULL, client1, bus);
536 if (r != 0)
537 return EXIT_FAILURE;
538
539 r = pthread_create(&c2, NULL, client2, bus);
540 if (r != 0)
541 return EXIT_FAILURE;
542
543 r = server(bus);
544
545 q = pthread_join(c1, &p);
546 if (q != 0)
547 return EXIT_FAILURE;
548 if (PTR_TO_INT(p) < 0)
549 return EXIT_FAILURE;
550
551 q = pthread_join(c2, &p);
552 if (q != 0)
553 return EXIT_FAILURE;
554 if (PTR_TO_INT(p) < 0)
555 return EXIT_FAILURE;
556
557 if (r < 0)
558 return EXIT_FAILURE;
559
560 return EXIT_SUCCESS;
561 }