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