chiark / gitweb /
7f49fe90fab2a255be6fb86d6246857df77eb874
[elogind.git] / src / libelogind / sd-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 <stdlib.h>
23 #include <pthread.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26
27 #include "log.h"
28 #include "util.h"
29 #include "macro.h"
30
31 #include "sd-bus.h"
32 #include "bus-error.h"
33 #include "bus-match.h"
34 #include "bus-internal.h"
35 #include "bus-util.h"
36
37 static int match_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
38         log_info("Match triggered! interface=%s member=%s", strna(sd_bus_message_get_interface(m)), strna(sd_bus_message_get_member(m)));
39         return 0;
40 }
41
42 static int object_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
43         int r;
44
45         assert_se(bus);
46
47         if (sd_bus_message_is_method_error(m, NULL))
48                 return 0;
49
50         if (sd_bus_message_is_method_call(m, "org.object.test", "Foobar")) {
51                 log_info("Invoked Foobar() on %s", sd_bus_message_get_path(m));
52
53                 r = sd_bus_reply_method_return(m, NULL);
54                 if (r < 0)
55                         return log_error_errno(r, "Failed to send reply: %m");
56
57                 return 1;
58         }
59
60         return 0;
61 }
62
63 static int server_init(sd_bus **_bus) {
64         sd_bus *bus = NULL;
65         sd_id128_t id;
66         int r;
67         const char *unique;
68
69         assert_se(_bus);
70
71         r = sd_bus_open_user(&bus);
72         if (r < 0) {
73                 log_error_errno(r, "Failed to connect to user bus: %m");
74                 goto fail;
75         }
76
77         r = sd_bus_get_bus_id(bus, &id);
78         if (r < 0) {
79                 log_error_errno(r, "Failed to get server ID: %m");
80                 goto fail;
81         }
82
83         r = sd_bus_get_unique_name(bus, &unique);
84         if (r < 0) {
85                 log_error_errno(r, "Failed to get unique name: %m");
86                 goto fail;
87         }
88
89         log_info("Peer ID is " SD_ID128_FORMAT_STR ".", SD_ID128_FORMAT_VAL(id));
90         log_info("Unique ID: %s", unique);
91         log_info("Can send file handles: %i", sd_bus_can_send(bus, 'h'));
92
93         r = sd_bus_request_name(bus, "org.freedesktop.systemd.test", 0);
94         if (r < 0) {
95                 log_error_errno(r, "Failed to acquire name: %m");
96                 goto fail;
97         }
98
99         r = sd_bus_add_fallback(bus, NULL, "/foo/bar", object_callback, NULL);
100         if (r < 0) {
101                 log_error_errno(r, "Failed to add object: %m");
102                 goto fail;
103         }
104
105         r = sd_bus_add_match(bus, NULL, "type='signal',interface='foo.bar',member='Notify'", match_callback, NULL);
106         if (r < 0) {
107                 log_error_errno(r, "Failed to add match: %m");
108                 goto fail;
109         }
110
111         r = sd_bus_add_match(bus, NULL, "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'", match_callback, NULL);
112         if (r < 0) {
113                 log_error_errno(r, "Failed to add match: %m");
114                 goto fail;
115         }
116
117         bus_match_dump(&bus->match_callbacks, 0);
118
119         *_bus = bus;
120         return 0;
121
122 fail:
123         if (bus)
124                 sd_bus_unref(bus);
125
126         return r;
127 }
128
129 static int server(sd_bus *bus) {
130         int r;
131         bool client1_gone = false, client2_gone = false;
132
133         while (!client1_gone || !client2_gone) {
134                 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
135                 pid_t pid = 0;
136                 const char *label = NULL;
137
138                 r = sd_bus_process(bus, &m);
139                 if (r < 0) {
140                         log_error_errno(r, "Failed to process requests: %m");
141                         goto fail;
142                 }
143
144                 if (r == 0) {
145                         r = sd_bus_wait(bus, (uint64_t) -1);
146                         if (r < 0) {
147                                 log_error_errno(r, "Failed to wait: %m");
148                                 goto fail;
149                         }
150
151                         continue;
152                 }
153
154                 if (!m)
155                         continue;
156
157                 sd_bus_creds_get_pid(sd_bus_message_get_creds(m), &pid);
158                 sd_bus_creds_get_selinux_context(sd_bus_message_get_creds(m), &label);
159                 log_info("Got message! member=%s pid="PID_FMT" label=%s",
160                          strna(sd_bus_message_get_member(m)),
161                          pid,
162                          strna(label));
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_errno(r, "Failed to get parameter: %m");
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(m, "s", lowercase);
185                         if (r < 0) {
186                                 log_error_errno(r, "Failed to send reply: %m");
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(m, NULL);
192                         if (r < 0) {
193                                 log_error_errno(r, "Failed to send reply: %m");
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(m, NULL);
201                         if (r < 0) {
202                                 log_error_errno(r, "Failed to send reply: %m");
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(m, NULL);
212                         if (r < 0) {
213                                 log_error_errno(r, "Failed to send reply: %m");
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_errno(r, "Failed to get parameter: %m");
224                                 goto fail;
225                         }
226
227                         log_info("Received fd=%d", fd);
228
229                         if (write(fd, &x, 1) < 0) {
230                                 log_error_errno(errno, "Failed to write to fd: %m");
231                                 safe_close(fd);
232                                 goto fail;
233                         }
234
235                         r = sd_bus_reply_method_return(m, NULL);
236                         if (r < 0) {
237                                 log_error_errno(r, "Failed to send reply: %m");
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                                         m,
245                                         &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method."));
246                         if (r < 0) {
247                                 log_error_errno(r, "Failed to send reply: %m");
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 *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_errno(r, "Failed to connect to user bus: %m");
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_errno(r, "Failed to issue method call: %m");
291                 goto finish;
292         }
293
294         r = sd_bus_message_read(reply, "s", &hello);
295         if (r < 0) {
296                 log_error_errno(r, "Failed to get string: %m");
297                 goto finish;
298         }
299
300         assert_se(streq(hello, "hello"));
301
302         if (pipe2(pp, O_CLOEXEC|O_NONBLOCK) < 0) {
303                 log_error_errno(errno, "Failed to allocate pipe: %m");
304                 r = -errno;
305                 goto finish;
306         }
307
308         log_info("Sending fd=%d", pp[1]);
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_errno(r, "Failed to issue method call: %m");
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                                 &q,
340                                 "org.freedesktop.systemd.test",
341                                 "/",
342                                 "org.freedesktop.systemd.test",
343                                 "ExitClient1");
344                 if (r < 0)
345                         log_error_errno(r, "Failed to allocate method call: %m");
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         safe_close_pair(pp);
356
357         return INT_TO_PTR(r);
358 }
359
360 static int quit_callback(sd_bus *b, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
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_errno(r, "Failed to connect to user bus: %m");
380                 goto finish;
381         }
382
383         r = sd_bus_message_new_method_call(
384                         bus,
385                         &m,
386                         "org.freedesktop.systemd.test",
387                         "/foo/bar/waldo/piep",
388                         "org.object.test",
389                         "Foobar");
390         if (r < 0) {
391                 log_error_errno(r, "Failed to allocate method call: %m");
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                         &m,
407                         "/foobar",
408                         "foo.bar",
409                         "Notify");
410         if (r < 0) {
411                 log_error_errno(r, "Failed to allocate signal: %m");
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                         &m,
427                         "org.freedesktop.systemd.test",
428                         "/",
429                         "org.freedesktop.DBus.Peer",
430                         "GetMachineId");
431         if (r < 0) {
432                 log_error_errno(r, "Failed to allocate method call: %m");
433                 goto finish;
434         }
435
436         r = sd_bus_call(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_errno(r, "Failed to parse machine ID: %m");
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                         &m,
456                         "org.freedesktop.systemd.test",
457                         "/",
458                         "org.freedesktop.systemd.test",
459                         "Slow");
460         if (r < 0) {
461                 log_error_errno(r, "Failed to allocate method call: %m");
462                 goto finish;
463         }
464
465         sd_bus_message_unref(reply);
466         reply = NULL;
467
468         r = sd_bus_call(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                         &m,
480                         "org.freedesktop.systemd.test",
481                         "/",
482                         "org.freedesktop.systemd.test",
483                         "Slow");
484         if (r < 0) {
485                 log_error_errno(r, "Failed to allocate method call: %m");
486                 goto finish;
487         }
488
489         r = sd_bus_call_async(bus, NULL, m, quit_callback, &quit, 200 * USEC_PER_MSEC);
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_errno(r, "Failed to process requests: %m");
499                         goto finish;
500                 }
501                 if (r == 0) {
502                         r = sd_bus_wait(bus, (uint64_t) -1);
503                         if (r < 0) {
504                                 log_error_errno(r, "Failed to wait: %m");
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                                 &q,
519                                 "org.freedesktop.systemd.test",
520                                 "/",
521                                 "org.freedesktop.systemd.test",
522                                 "ExitClient2");
523                 if (r < 0) {
524                         log_error_errno(r, "Failed to allocate method call: %m");
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 }