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