chiark / gitweb /
tests: skip bus test if bus cannot be opened
[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
36 static int object_callback(sd_bus *bus, int error, sd_bus_message *m, void *userdata) {
37         int r;
38
39         assert(bus);
40
41         if (error != 0)
42                 return 0;
43
44         if (sd_bus_message_is_method_call(m, "org.object.test", "Foobar")) {
45                 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
46
47                 log_info("Invoked Foobar() on %s", sd_bus_message_get_path(m));
48
49                 r = sd_bus_message_new_method_return(bus, m, &reply);
50                 if (r < 0) {
51                         log_error("Failed to allocate return: %s", strerror(-r));
52                         return r;
53                 }
54
55                 r = sd_bus_send(bus, reply, 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_peer(bus, &id);
82         if (r < 0) {
83                 log_error("Failed to get peer 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         *_bus = bus;
110         return 0;
111
112 fail:
113         if (bus)
114                 sd_bus_unref(bus);
115
116         return r;
117 }
118
119 static int server(sd_bus *bus) {
120         int r;
121         bool client1_gone = false, client2_gone = false;
122
123         while (!client1_gone || !client2_gone) {
124                 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
125                 pid_t pid = 0;
126
127                 r = sd_bus_process(bus, &m);
128                 if (r < 0) {
129                         log_error("Failed to process requests: %s", strerror(-r));
130                         goto fail;
131                 }
132
133                 if (r == 0) {
134                         r = sd_bus_wait(bus, (uint64_t) -1);
135                         if (r < 0) {
136                                 log_error("Failed to wait: %s", strerror(-r));
137                                 goto fail;
138                         }
139
140                         continue;
141                 }
142
143                 if (!m)
144                         continue;
145
146                 sd_bus_message_get_pid(m, &pid);
147                 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)));
148                 /* bus_message_dump(m); */
149                 /* sd_bus_message_rewind(m, true); */
150
151                 if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "LowerCase")) {
152                         const char *hello;
153                         _cleanup_free_ char *lowercase = NULL;
154
155                         r = sd_bus_message_read(m, "s", &hello);
156                         if (r < 0) {
157                                 log_error("Failed to get parameter: %s", strerror(-r));
158                                 goto fail;
159                         }
160
161                         r = sd_bus_message_new_method_return(bus, m, &reply);
162                         if (r < 0) {
163                                 log_error("Failed to allocate return: %s", strerror(-r));
164                                 goto fail;
165                         }
166
167                         lowercase = strdup(hello);
168                         if (!lowercase) {
169                                 r = log_oom();
170                                 goto fail;
171                         }
172
173                         ascii_strlower(lowercase);
174
175                         r = sd_bus_message_append(reply, "s", lowercase);
176                         if (r < 0) {
177                                 log_error("Failed to append message: %s", strerror(-r));
178                                 goto fail;
179                         }
180                 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient1")) {
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                         client1_gone = true;
189                 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient2")) {
190
191                         r = sd_bus_message_new_method_return(bus, m, &reply);
192                         if (r < 0) {
193                                 log_error("Failed to allocate return: %s", strerror(-r));
194                                 goto fail;
195                         }
196
197                         client2_gone = true;
198                 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "Slow")) {
199
200                         r = sd_bus_message_new_method_return(bus, m, &reply);
201                         if (r < 0) {
202                                 log_error("Failed to allocate return: %s", strerror(-r));
203                                 goto fail;
204                         }
205
206                         sleep(1);
207
208                 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "FileDescriptor")) {
209                         int fd;
210                         static const char x = 'X';
211
212                         r = sd_bus_message_read(m, "h", &fd);
213                         if (r < 0) {
214                                 log_error("Failed to get parameter: %s", strerror(-r));
215                                 goto fail;
216                         }
217
218                         if (write(fd, &x, 1) < 0) {
219                                 log_error("Failed to write to fd: %m");
220                                 close_nointr_nofail(fd);
221                                 goto fail;
222                         }
223
224                         close_nointr_nofail(fd);
225
226                         r = sd_bus_message_new_method_return(bus, m, &reply);
227                         if (r < 0) {
228                                 log_error("Failed to allocate return: %s", strerror(-r));
229                                 goto fail;
230                         }
231
232                 } else if (sd_bus_message_is_method_call(m, NULL, NULL)) {
233                         const sd_bus_error e = SD_BUS_ERROR_INIT_CONST("org.freedesktop.DBus.Error.UnknownMethod", "Unknown method.");
234
235                         r = sd_bus_message_new_method_error(bus, m, &e, &reply);
236                         if (r < 0) {
237                                 log_error("Failed to allocate return: %s", strerror(-r));
238                                 goto fail;
239                         }
240                 }
241
242                 if (reply) {
243                         r = sd_bus_send(bus, reply, NULL);
244                         if (r < 0) {
245                                 log_error("Failed to send reply: %s", strerror(-r));
246                                 goto fail;
247                         }
248
249                         /* log_info("Sent"); */
250                         /* bus_message_dump(reply); */
251                         /* sd_bus_message_rewind(reply, true); */
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 *m = NULL, *reply = NULL;
268         sd_bus *bus = NULL;
269         sd_bus_error error = SD_BUS_ERROR_INIT;
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_message_new_method_call(
282                         bus,
283                         "org.freedesktop.systemd.test",
284                         "/",
285                         "org.freedesktop.systemd.test",
286                         "LowerCase",
287                         &m);
288         if (r < 0) {
289                 log_error("Failed to allocate method call: %s", strerror(-r));
290                 goto finish;
291         }
292
293         r = sd_bus_message_append(m, "s", "HELLO");
294         if (r < 0) {
295                 log_error("Failed to append string: %s", strerror(-r));
296                 goto finish;
297         }
298
299         r = sd_bus_send_with_reply_and_block(bus, m, 0, &error, &reply);
300         if (r < 0) {
301                 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
302                 goto finish;
303         }
304
305         r = sd_bus_message_read(reply, "s", &hello);
306         if (r < 0) {
307                 log_error("Failed to get string: %s", strerror(-r));
308                 goto finish;
309         }
310
311         assert(streq(hello, "hello"));
312
313         if (pipe2(pp, O_CLOEXEC|O_NONBLOCK) < 0) {
314                 log_error("Failed to allocate pipe: %m");
315                 r = -errno;
316                 goto finish;
317         }
318
319         sd_bus_message_unref(m);
320         m = NULL;
321         r = sd_bus_message_new_method_call(
322                         bus,
323                         "org.freedesktop.systemd.test",
324                         "/",
325                         "org.freedesktop.systemd.test",
326                         "FileDescriptor",
327                         &m);
328         if (r < 0) {
329                 log_error("Failed to allocate method call: %s", strerror(-r));
330                 goto finish;
331         }
332
333         r = sd_bus_message_append(m, "h", pp[1]);
334         if (r < 0) {
335                 log_error("Failed to append string: %s", strerror(-r));
336                 goto finish;
337         }
338
339         sd_bus_message_unref(reply);
340         reply = NULL;
341         r = sd_bus_send_with_reply_and_block(bus, m, 0, &error, &reply);
342         if (r < 0) {
343                 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
344                 goto finish;
345         }
346
347         errno = 0;
348         if (read(pp[0], &x, 1) <= 0) {
349                 log_error("Failed to read from pipe: %s", errno ? strerror(errno) : "early read");
350                 goto finish;
351         }
352
353         r = 0;
354
355 finish:
356         if (bus) {
357                 _cleanup_bus_message_unref_ sd_bus_message *q;
358
359                 r = sd_bus_message_new_method_call(
360                                 bus,
361                                 "org.freedesktop.systemd.test",
362                                 "/",
363                                 "org.freedesktop.systemd.test",
364                                 "ExitClient1",
365                                 &q);
366                 if (r < 0) {
367                         log_error("Failed to allocate method call: %s", strerror(-r));
368                         goto finish;
369                 }
370
371                 sd_bus_send(bus, q, NULL);
372                 sd_bus_flush(bus);
373                 sd_bus_unref(bus);
374         }
375
376         sd_bus_error_free(&error);
377
378         close_pipe(pp);
379
380         return INT_TO_PTR(r);
381 }
382
383 static int quit_callback(sd_bus *b, int ret, sd_bus_message *m, void *userdata) {
384         bool *x = userdata;
385
386         log_error("Quit callback: %s", strerror(ret));
387
388         *x = 1;
389         return 1;
390 }
391
392 static void* client2(void*p) {
393         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
394         sd_bus *bus = NULL;
395         sd_bus_error error = SD_BUS_ERROR_INIT;
396         bool quit = false;
397         const char *mid;
398         int r;
399
400         r = sd_bus_open_user(&bus);
401         if (r < 0) {
402                 log_error("Failed to connect to user bus: %s", strerror(-r));
403                 goto finish;
404         }
405
406         r = sd_bus_message_new_method_call(
407                         bus,
408                         "org.freedesktop.systemd.test",
409                         "/foo/bar/waldo/piep",
410                         "org.object.test",
411                         "Foobar",
412                         &m);
413         if (r < 0) {
414                 log_error("Failed to allocate method call: %s", strerror(-r));
415                 goto finish;
416         }
417
418         r = sd_bus_send(bus, m, NULL);
419         if (r < 0) {
420                 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
421                 goto finish;
422         }
423
424         sd_bus_message_unref(m);
425         m = NULL;
426
427         r = sd_bus_message_new_method_call(
428                         bus,
429                         "org.freedesktop.systemd.test",
430                         "/",
431                         "org.freedesktop.DBus.Peer",
432                         "GetMachineId",
433                         &m);
434         if (r < 0) {
435                 log_error("Failed to allocate method call: %s", strerror(-r));
436                 goto finish;
437         }
438
439         r = sd_bus_send_with_reply_and_block(bus, m, 0, &error, &reply);
440         if (r < 0) {
441                 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
442                 goto finish;
443         }
444
445         r = sd_bus_message_read(reply, "s", &mid);
446         if (r < 0) {
447                 log_error("Failed to parse machine ID: %s", strerror(-r));
448                 goto finish;
449         }
450
451         log_info("Machine ID is %s.", mid);
452
453         sd_bus_message_unref(m);
454         m = NULL;
455
456         r = sd_bus_message_new_method_call(
457                         bus,
458                         "org.freedesktop.systemd.test",
459                         "/",
460                         "org.freedesktop.systemd.test",
461                         "Slow",
462                         &m);
463         if (r < 0) {
464                 log_error("Failed to allocate method call: %s", strerror(-r));
465                 goto finish;
466         }
467
468         sd_bus_message_unref(reply);
469         reply = NULL;
470
471         r = sd_bus_send_with_reply_and_block(bus, m, 200 * USEC_PER_MSEC, &error, &reply);
472         if (r < 0)
473                 log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
474         else
475                 log_info("Slow call succeed.");
476
477         sd_bus_message_unref(m);
478         m = NULL;
479
480         r = sd_bus_message_new_method_call(
481                         bus,
482                         "org.freedesktop.systemd.test",
483                         "/",
484                         "org.freedesktop.systemd.test",
485                         "Slow",
486                         &m);
487         if (r < 0) {
488                 log_error("Failed to allocate method call: %s", strerror(-r));
489                 goto finish;
490         }
491
492         r = sd_bus_send_with_reply(bus, m, quit_callback, &quit, 200 * USEC_PER_MSEC, NULL);
493         if (r < 0) {
494                 log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
495                 goto finish;
496         }
497
498         while (!quit) {
499                 r = sd_bus_process(bus, NULL);
500                 if (r < 0) {
501                         log_error("Failed to process requests: %s", strerror(-r));
502                         goto finish;
503                 }
504                 if (r == 0) {
505                         r = sd_bus_wait(bus, (uint64_t) -1);
506                         if (r < 0) {
507                                 log_error("Failed to wait: %s", strerror(-r));
508                                 goto finish;
509                         }
510                 }
511         }
512
513         r = 0;
514
515 finish:
516         if (bus) {
517                 _cleanup_bus_message_unref_ sd_bus_message *q;
518
519                 r = sd_bus_message_new_method_call(
520                                 bus,
521                                 "org.freedesktop.systemd.test",
522                                 "/",
523                                 "org.freedesktop.systemd.test",
524                                 "ExitClient2",
525                                 &q);
526                 if (r < 0) {
527                         log_error("Failed to allocate method call: %s", strerror(-r));
528                         goto finish;
529                 }
530
531                 sd_bus_send(bus, q, NULL);
532                 sd_bus_flush(bus);
533                 sd_bus_unref(bus);
534         }
535
536         sd_bus_error_free(&error);
537         return INT_TO_PTR(r);
538 }
539
540 int main(int argc, char *argv[]) {
541         pthread_t c1, c2;
542         sd_bus *bus;
543         void *p;
544         int q, r;
545
546         r = server_init(&bus);
547         if (r < 0) {
548                 log_info("Failed to connect to bus, skipping tests.");
549                 return EXIT_TEST_SKIP;
550         }
551
552         log_info("Initialized...");
553
554         r = pthread_create(&c1, NULL, client1, bus);
555         if (r != 0)
556                 return EXIT_FAILURE;
557
558         r = pthread_create(&c2, NULL, client2, bus);
559         if (r != 0)
560                 return EXIT_FAILURE;
561
562         r = server(bus);
563
564         q = pthread_join(c1, &p);
565         if (q != 0)
566                 return EXIT_FAILURE;
567         q = pthread_join(c2, &p);
568         if (q != 0)
569                 return EXIT_FAILURE;
570         if (r < 0)
571                 return EXIT_FAILURE;
572
573         return EXIT_SUCCESS;
574 }