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