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