chiark / gitweb /
868a5b14089a87ebcdc922544450d89af74ae937
[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                         r = sd_bus_reply_method_return(bus, m, NULL);
210                         if (r < 0) {
211                                 log_error("Failed to send reply: %s", strerror(-r));
212                                 goto fail;
213                         }
214
215                         sleep(1);
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                         close_nointr_nofail(fd);
234
235                         r = sd_bus_reply_method_return(bus, m, NULL);
236                         if (r < 0) {
237                                 log_error("Failed to send reply: %s", strerror(-r));
238                                 goto fail;
239                         }
240
241                 } else if (sd_bus_message_is_method_call(m, NULL, NULL)) {
242
243                         r = sd_bus_reply_method_error(
244                                         bus, m,
245                                         &SD_BUS_ERROR_MAKE("org.freedesktop.DBus.Error.UnknownMethod", "Unknown method."));
246                         if (r < 0) {
247                                 log_error("Failed to send reply: %s", strerror(-r));
248                                 goto fail;
249                         }
250                 }
251         }
252
253         r = 0;
254
255 fail:
256         if (bus) {
257                 sd_bus_flush(bus);
258                 sd_bus_unref(bus);
259         }
260
261         return r;
262 }
263
264 static void* client1(void*p) {
265         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
266         sd_bus *bus = NULL;
267         sd_bus_error error = SD_BUS_ERROR_NULL;
268         const char *hello;
269         int r;
270         int pp[2] = { -1, -1 };
271         char x;
272
273         r = sd_bus_open_user(&bus);
274         if (r < 0) {
275                 log_error("Failed to connect to user bus: %s", strerror(-r));
276                 goto finish;
277         }
278
279         r = sd_bus_call_method(
280                         bus,
281                         "org.freedesktop.systemd.test",
282                         "/",
283                         "org.freedesktop.systemd.test",
284                         "LowerCase",
285                         &error,
286                         &reply,
287                         "s",
288                         "HELLO");
289         if (r < 0) {
290                 log_error("Failed to issue method call: %s", strerror(-r));
291                 goto finish;
292         }
293
294         r = sd_bus_message_read(reply, "s", &hello);
295         if (r < 0) {
296                 log_error("Failed to get string: %s", strerror(-r));
297                 goto finish;
298         }
299
300         assert(streq(hello, "hello"));
301
302         if (pipe2(pp, O_CLOEXEC|O_NONBLOCK) < 0) {
303                 log_error("Failed to allocate pipe: %m");
304                 r = -errno;
305                 goto finish;
306         }
307
308         sd_bus_message_unref(m);
309         m = NULL;
310         r = sd_bus_message_new_method_call(
311                         bus,
312                         "org.freedesktop.systemd.test",
313                         "/",
314                         "org.freedesktop.systemd.test",
315                         "FileDescriptor",
316                         &m);
317         if (r < 0) {
318                 log_error("Failed to allocate method call: %s", strerror(-r));
319                 goto finish;
320         }
321
322         r = sd_bus_message_append(m, "h", pp[1]);
323         if (r < 0) {
324                 log_error("Failed to append string: %s", strerror(-r));
325                 goto finish;
326         }
327
328         sd_bus_message_unref(reply);
329         reply = NULL;
330         r = sd_bus_send_with_reply_and_block(bus, m, 0, &error, &reply);
331         if (r < 0) {
332                 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
333                 goto finish;
334         }
335
336         errno = 0;
337         if (read(pp[0], &x, 1) <= 0) {
338                 log_error("Failed to read from pipe: %s", errno ? strerror(errno) : "early read");
339                 goto finish;
340         }
341
342         r = 0;
343
344 finish:
345         if (bus) {
346                 _cleanup_bus_message_unref_ sd_bus_message *q;
347
348                 r = sd_bus_message_new_method_call(
349                                 bus,
350                                 "org.freedesktop.systemd.test",
351                                 "/",
352                                 "org.freedesktop.systemd.test",
353                                 "ExitClient1",
354                                 &q);
355                 if (r < 0) {
356                         log_error("Failed to allocate method call: %s", strerror(-r));
357                         goto finish;
358                 }
359
360                 sd_bus_send(bus, q, NULL);
361                 sd_bus_flush(bus);
362                 sd_bus_unref(bus);
363         }
364
365         sd_bus_error_free(&error);
366
367         close_pipe(pp);
368
369         return INT_TO_PTR(r);
370 }
371
372 static int quit_callback(sd_bus *b, int ret, sd_bus_message *m, void *userdata) {
373         bool *x = userdata;
374
375         log_error("Quit callback: %s", strerror(ret));
376
377         *x = 1;
378         return 1;
379 }
380
381 static void* client2(void*p) {
382         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
383         sd_bus *bus = NULL;
384         sd_bus_error error = SD_BUS_ERROR_NULL;
385         bool quit = false;
386         const char *mid;
387         int r;
388
389         r = sd_bus_open_user(&bus);
390         if (r < 0) {
391                 log_error("Failed to connect to user bus: %s", strerror(-r));
392                 goto finish;
393         }
394
395         r = sd_bus_message_new_method_call(
396                         bus,
397                         "org.freedesktop.systemd.test",
398                         "/foo/bar/waldo/piep",
399                         "org.object.test",
400                         "Foobar",
401                         &m);
402         if (r < 0) {
403                 log_error("Failed to allocate method call: %s", strerror(-r));
404                 goto finish;
405         }
406
407         r = sd_bus_send(bus, m, NULL);
408         if (r < 0) {
409                 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
410                 goto finish;
411         }
412
413         sd_bus_message_unref(m);
414         m = NULL;
415
416         r = sd_bus_message_new_signal(
417                         bus,
418                         "/foobar",
419                         "foo.bar",
420                         "Notify",
421                         &m);
422         if (r < 0) {
423                 log_error("Failed to allocate signal: %s", strerror(-r));
424                 goto finish;
425         }
426
427         r = sd_bus_send(bus, m, NULL);
428         if (r < 0) {
429                 log_error("Failed to issue signal: %s", bus_error_message(&error, -r));
430                 goto finish;
431         }
432
433         sd_bus_message_unref(m);
434         m = NULL;
435
436         r = sd_bus_message_new_method_call(
437                         bus,
438                         "org.freedesktop.systemd.test",
439                         "/",
440                         "org.freedesktop.DBus.Peer",
441                         "GetMachineId",
442                         &m);
443         if (r < 0) {
444                 log_error("Failed to allocate method call: %s", strerror(-r));
445                 goto finish;
446         }
447
448         r = sd_bus_send_with_reply_and_block(bus, m, 0, &error, &reply);
449         if (r < 0) {
450                 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
451                 goto finish;
452         }
453
454         r = sd_bus_message_read(reply, "s", &mid);
455         if (r < 0) {
456                 log_error("Failed to parse machine ID: %s", strerror(-r));
457                 goto finish;
458         }
459
460         log_info("Machine ID is %s.", mid);
461
462         sd_bus_message_unref(m);
463         m = NULL;
464
465         r = sd_bus_message_new_method_call(
466                         bus,
467                         "org.freedesktop.systemd.test",
468                         "/",
469                         "org.freedesktop.systemd.test",
470                         "Slow",
471                         &m);
472         if (r < 0) {
473                 log_error("Failed to allocate method call: %s", strerror(-r));
474                 goto finish;
475         }
476
477         sd_bus_message_unref(reply);
478         reply = NULL;
479
480         r = sd_bus_send_with_reply_and_block(bus, m, 200 * USEC_PER_MSEC, &error, &reply);
481         if (r < 0)
482                 log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
483         else
484                 log_info("Slow call succeed.");
485
486         sd_bus_message_unref(m);
487         m = NULL;
488
489         r = sd_bus_message_new_method_call(
490                         bus,
491                         "org.freedesktop.systemd.test",
492                         "/",
493                         "org.freedesktop.systemd.test",
494                         "Slow",
495                         &m);
496         if (r < 0) {
497                 log_error("Failed to allocate method call: %s", strerror(-r));
498                 goto finish;
499         }
500
501         r = sd_bus_send_with_reply(bus, m, quit_callback, &quit, 200 * USEC_PER_MSEC, NULL);
502         if (r < 0) {
503                 log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
504                 goto finish;
505         }
506
507         while (!quit) {
508                 r = sd_bus_process(bus, NULL);
509                 if (r < 0) {
510                         log_error("Failed to process requests: %s", strerror(-r));
511                         goto finish;
512                 }
513                 if (r == 0) {
514                         r = sd_bus_wait(bus, (uint64_t) -1);
515                         if (r < 0) {
516                                 log_error("Failed to wait: %s", strerror(-r));
517                                 goto finish;
518                         }
519                 }
520         }
521
522         r = 0;
523
524 finish:
525         if (bus) {
526                 _cleanup_bus_message_unref_ sd_bus_message *q;
527
528                 r = sd_bus_message_new_method_call(
529                                 bus,
530                                 "org.freedesktop.systemd.test",
531                                 "/",
532                                 "org.freedesktop.systemd.test",
533                                 "ExitClient2",
534                                 &q);
535                 if (r < 0) {
536                         log_error("Failed to allocate method call: %s", strerror(-r));
537                         goto finish;
538                 }
539
540                 sd_bus_send(bus, q, NULL);
541                 sd_bus_flush(bus);
542                 sd_bus_unref(bus);
543         }
544
545         sd_bus_error_free(&error);
546         return INT_TO_PTR(r);
547 }
548
549 int main(int argc, char *argv[]) {
550         pthread_t c1, c2;
551         sd_bus *bus;
552         void *p;
553         int q, r;
554
555         r = server_init(&bus);
556         if (r < 0) {
557                 log_info("Failed to connect to bus, skipping tests.");
558                 return EXIT_TEST_SKIP;
559         }
560
561         log_info("Initialized...");
562
563         r = pthread_create(&c1, NULL, client1, bus);
564         if (r != 0)
565                 return EXIT_FAILURE;
566
567         r = pthread_create(&c2, NULL, client2, bus);
568         if (r != 0)
569                 return EXIT_FAILURE;
570
571         r = server(bus);
572
573         q = pthread_join(c1, &p);
574         if (q != 0)
575                 return EXIT_FAILURE;
576         if (PTR_TO_INT(p) < 0)
577                 return EXIT_FAILURE;
578
579         q = pthread_join(c2, &p);
580         if (q != 0)
581                 return EXIT_FAILURE;
582         if (PTR_TO_INT(p) < 0)
583                 return EXIT_FAILURE;
584
585         if (r < 0)
586                 return EXIT_FAILURE;
587
588         return EXIT_SUCCESS;
589 }