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