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