chiark / gitweb /
journal: Fix typo
[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                         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         r = sd_bus_call_method(
309                         bus,
310                         "org.freedesktop.systemd.test",
311                         "/",
312                         "org.freedesktop.systemd.test",
313                         "FileDescriptor",
314                         &error,
315                         NULL,
316                         "h",
317                         pp[1]);
318         if (r < 0) {
319                 log_error("Failed to issue method call: %s", strerror(-r));
320                 goto finish;
321         }
322
323         errno = 0;
324         if (read(pp[0], &x, 1) <= 0) {
325                 log_error("Failed to read from pipe: %s", errno ? strerror(errno) : "early read");
326                 goto finish;
327         }
328
329         r = 0;
330
331 finish:
332         if (bus) {
333                 _cleanup_bus_message_unref_ sd_bus_message *q;
334
335                 r = sd_bus_message_new_method_call(
336                                 bus,
337                                 "org.freedesktop.systemd.test",
338                                 "/",
339                                 "org.freedesktop.systemd.test",
340                                 "ExitClient1",
341                                 &q);
342                 if (r < 0)
343                         log_error("Failed to allocate method call: %s", strerror(-r));
344                 else
345                         sd_bus_send(bus, q, NULL);
346
347                 sd_bus_flush(bus);
348                 sd_bus_unref(bus);
349         }
350
351         sd_bus_error_free(&error);
352
353         close_pipe(pp);
354
355         return INT_TO_PTR(r);
356 }
357
358 static int quit_callback(sd_bus *b, int ret, sd_bus_message *m, void *userdata) {
359         bool *x = userdata;
360
361         log_error("Quit callback: %s", strerror(ret));
362
363         *x = 1;
364         return 1;
365 }
366
367 static void* client2(void*p) {
368         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
369         sd_bus *bus = NULL;
370         sd_bus_error error = SD_BUS_ERROR_NULL;
371         bool quit = false;
372         const char *mid;
373         int r;
374
375         r = sd_bus_open_user(&bus);
376         if (r < 0) {
377                 log_error("Failed to connect to user bus: %s", strerror(-r));
378                 goto finish;
379         }
380
381         r = sd_bus_message_new_method_call(
382                         bus,
383                         "org.freedesktop.systemd.test",
384                         "/foo/bar/waldo/piep",
385                         "org.object.test",
386                         "Foobar",
387                         &m);
388         if (r < 0) {
389                 log_error("Failed to allocate method call: %s", strerror(-r));
390                 goto finish;
391         }
392
393         r = sd_bus_send(bus, m, NULL);
394         if (r < 0) {
395                 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
396                 goto finish;
397         }
398
399         sd_bus_message_unref(m);
400         m = NULL;
401
402         r = sd_bus_message_new_signal(
403                         bus,
404                         "/foobar",
405                         "foo.bar",
406                         "Notify",
407                         &m);
408         if (r < 0) {
409                 log_error("Failed to allocate signal: %s", strerror(-r));
410                 goto finish;
411         }
412
413         r = sd_bus_send(bus, m, NULL);
414         if (r < 0) {
415                 log_error("Failed to issue signal: %s", bus_error_message(&error, -r));
416                 goto finish;
417         }
418
419         sd_bus_message_unref(m);
420         m = NULL;
421
422         r = sd_bus_message_new_method_call(
423                         bus,
424                         "org.freedesktop.systemd.test",
425                         "/",
426                         "org.freedesktop.DBus.Peer",
427                         "GetMachineId",
428                         &m);
429         if (r < 0) {
430                 log_error("Failed to allocate method call: %s", strerror(-r));
431                 goto finish;
432         }
433
434         r = sd_bus_send_with_reply_and_block(bus, m, 0, &error, &reply);
435         if (r < 0) {
436                 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
437                 goto finish;
438         }
439
440         r = sd_bus_message_read(reply, "s", &mid);
441         if (r < 0) {
442                 log_error("Failed to parse machine ID: %s", strerror(-r));
443                 goto finish;
444         }
445
446         log_info("Machine ID is %s.", mid);
447
448         sd_bus_message_unref(m);
449         m = NULL;
450
451         r = sd_bus_message_new_method_call(
452                         bus,
453                         "org.freedesktop.systemd.test",
454                         "/",
455                         "org.freedesktop.systemd.test",
456                         "Slow",
457                         &m);
458         if (r < 0) {
459                 log_error("Failed to allocate method call: %s", strerror(-r));
460                 goto finish;
461         }
462
463         sd_bus_message_unref(reply);
464         reply = NULL;
465
466         r = sd_bus_send_with_reply_and_block(bus, m, 200 * USEC_PER_MSEC, &error, &reply);
467         if (r < 0)
468                 log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
469         else
470                 log_info("Slow call succeed.");
471
472         sd_bus_message_unref(m);
473         m = NULL;
474
475         r = sd_bus_message_new_method_call(
476                         bus,
477                         "org.freedesktop.systemd.test",
478                         "/",
479                         "org.freedesktop.systemd.test",
480                         "Slow",
481                         &m);
482         if (r < 0) {
483                 log_error("Failed to allocate method call: %s", strerror(-r));
484                 goto finish;
485         }
486
487         r = sd_bus_send_with_reply(bus, m, quit_callback, &quit, 200 * USEC_PER_MSEC, NULL);
488         if (r < 0) {
489                 log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
490                 goto finish;
491         }
492
493         while (!quit) {
494                 r = sd_bus_process(bus, NULL);
495                 if (r < 0) {
496                         log_error("Failed to process requests: %s", strerror(-r));
497                         goto finish;
498                 }
499                 if (r == 0) {
500                         r = sd_bus_wait(bus, (uint64_t) -1);
501                         if (r < 0) {
502                                 log_error("Failed to wait: %s", strerror(-r));
503                                 goto finish;
504                         }
505                 }
506         }
507
508         r = 0;
509
510 finish:
511         if (bus) {
512                 _cleanup_bus_message_unref_ sd_bus_message *q;
513
514                 r = sd_bus_message_new_method_call(
515                                 bus,
516                                 "org.freedesktop.systemd.test",
517                                 "/",
518                                 "org.freedesktop.systemd.test",
519                                 "ExitClient2",
520                                 &q);
521                 if (r < 0) {
522                         log_error("Failed to allocate method call: %s", strerror(-r));
523                         goto finish;
524                 }
525
526                 sd_bus_send(bus, q, NULL);
527                 sd_bus_flush(bus);
528                 sd_bus_unref(bus);
529         }
530
531         sd_bus_error_free(&error);
532         return INT_TO_PTR(r);
533 }
534
535 int main(int argc, char *argv[]) {
536         pthread_t c1, c2;
537         sd_bus *bus;
538         void *p;
539         int q, r;
540
541         r = server_init(&bus);
542         if (r < 0) {
543                 log_info("Failed to connect to bus, skipping tests.");
544                 return EXIT_TEST_SKIP;
545         }
546
547         log_info("Initialized...");
548
549         r = pthread_create(&c1, NULL, client1, bus);
550         if (r != 0)
551                 return EXIT_FAILURE;
552
553         r = pthread_create(&c2, NULL, client2, bus);
554         if (r != 0)
555                 return EXIT_FAILURE;
556
557         r = server(bus);
558
559         q = pthread_join(c1, &p);
560         if (q != 0)
561                 return EXIT_FAILURE;
562         if (PTR_TO_INT(p) < 0)
563                 return EXIT_FAILURE;
564
565         q = pthread_join(c2, &p);
566         if (q != 0)
567                 return EXIT_FAILURE;
568         if (PTR_TO_INT(p) < 0)
569                 return EXIT_FAILURE;
570
571         if (r < 0)
572                 return EXIT_FAILURE;
573
574         return EXIT_SUCCESS;
575 }