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