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