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