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