chiark / gitweb /
util: introduce memmem_safe() and make use of it
[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         _cleanup_bus_close_unref_ 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         }
349
350         sd_bus_error_free(&error);
351
352         safe_close_pair(pp);
353
354         return INT_TO_PTR(r);
355 }
356
357 static int quit_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
358         bool *x = userdata;
359
360         log_error("Quit callback: %s", strerror(sd_bus_message_get_errno(m)));
361
362         *x = 1;
363         return 1;
364 }
365
366 static void* client2(void*p) {
367         _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
368         sd_bus *bus = NULL;
369         sd_bus_error error = SD_BUS_ERROR_NULL;
370         bool quit = false;
371         const char *mid;
372         int r;
373
374         r = sd_bus_open_user(&bus);
375         if (r < 0) {
376                 log_error_errno(r, "Failed to connect to user bus: %m");
377                 goto finish;
378         }
379
380         r = sd_bus_message_new_method_call(
381                         bus,
382                         &m,
383                         "org.freedesktop.systemd.test",
384                         "/foo/bar/waldo/piep",
385                         "org.object.test",
386                         "Foobar");
387         if (r < 0) {
388                 log_error_errno(r, "Failed to allocate method call: %m");
389                 goto finish;
390         }
391
392         r = sd_bus_send(bus, m, NULL);
393         if (r < 0) {
394                 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
395                 goto finish;
396         }
397
398         sd_bus_message_unref(m);
399         m = NULL;
400
401         r = sd_bus_message_new_signal(
402                         bus,
403                         &m,
404                         "/foobar",
405                         "foo.bar",
406                         "Notify");
407         if (r < 0) {
408                 log_error_errno(r, "Failed to allocate signal: %m");
409                 goto finish;
410         }
411
412         r = sd_bus_send(bus, m, NULL);
413         if (r < 0) {
414                 log_error("Failed to issue signal: %s", bus_error_message(&error, -r));
415                 goto finish;
416         }
417
418         sd_bus_message_unref(m);
419         m = NULL;
420
421         r = sd_bus_message_new_method_call(
422                         bus,
423                         &m,
424                         "org.freedesktop.systemd.test",
425                         "/",
426                         "org.freedesktop.DBus.Peer",
427                         "GetMachineId");
428         if (r < 0) {
429                 log_error_errno(r, "Failed to allocate method call: %m");
430                 goto finish;
431         }
432
433         r = sd_bus_call(bus, m, 0, &error, &reply);
434         if (r < 0) {
435                 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
436                 goto finish;
437         }
438
439         r = sd_bus_message_read(reply, "s", &mid);
440         if (r < 0) {
441                 log_error_errno(r, "Failed to parse machine ID: %m");
442                 goto finish;
443         }
444
445         log_info("Machine ID is %s.", mid);
446
447         sd_bus_message_unref(m);
448         m = NULL;
449
450         r = sd_bus_message_new_method_call(
451                         bus,
452                         &m,
453                         "org.freedesktop.systemd.test",
454                         "/",
455                         "org.freedesktop.systemd.test",
456                         "Slow");
457         if (r < 0) {
458                 log_error_errno(r, "Failed to allocate method call: %m");
459                 goto finish;
460         }
461
462         sd_bus_message_unref(reply);
463         reply = NULL;
464
465         r = sd_bus_call(bus, m, 200 * USEC_PER_MSEC, &error, &reply);
466         if (r < 0)
467                 log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
468         else
469                 log_info("Slow call succeed.");
470
471         sd_bus_message_unref(m);
472         m = NULL;
473
474         r = sd_bus_message_new_method_call(
475                         bus,
476                         &m,
477                         "org.freedesktop.systemd.test",
478                         "/",
479                         "org.freedesktop.systemd.test",
480                         "Slow");
481         if (r < 0) {
482                 log_error_errno(r, "Failed to allocate method call: %m");
483                 goto finish;
484         }
485
486         r = sd_bus_call_async(bus, NULL, m, quit_callback, &quit, 200 * USEC_PER_MSEC);
487         if (r < 0) {
488                 log_info("Failed to issue method call: %s", bus_error_message(&error, -r));
489                 goto finish;
490         }
491
492         while (!quit) {
493                 r = sd_bus_process(bus, NULL);
494                 if (r < 0) {
495                         log_error_errno(r, "Failed to process requests: %m");
496                         goto finish;
497                 }
498                 if (r == 0) {
499                         r = sd_bus_wait(bus, (uint64_t) -1);
500                         if (r < 0) {
501                                 log_error_errno(r, "Failed to wait: %m");
502                                 goto finish;
503                         }
504                 }
505         }
506
507         r = 0;
508
509 finish:
510         if (bus) {
511                 _cleanup_bus_message_unref_ sd_bus_message *q;
512
513                 r = sd_bus_message_new_method_call(
514                                 bus,
515                                 &q,
516                                 "org.freedesktop.systemd.test",
517                                 "/",
518                                 "org.freedesktop.systemd.test",
519                                 "ExitClient2");
520                 if (r < 0) {
521                         log_error_errno(r, "Failed to allocate method call: %m");
522                         goto finish;
523                 }
524
525                 sd_bus_send(bus, q, NULL);
526                 sd_bus_flush(bus);
527                 sd_bus_unref(bus);
528         }
529
530         sd_bus_error_free(&error);
531         return INT_TO_PTR(r);
532 }
533
534 int main(int argc, char *argv[]) {
535         pthread_t c1, c2;
536         sd_bus *bus;
537         void *p;
538         int q, r;
539
540         r = server_init(&bus);
541         if (r < 0) {
542                 log_info("Failed to connect to bus, skipping tests.");
543                 return EXIT_TEST_SKIP;
544         }
545
546         log_info("Initialized...");
547
548         r = pthread_create(&c1, NULL, client1, bus);
549         if (r != 0)
550                 return EXIT_FAILURE;
551
552         r = pthread_create(&c2, NULL, client2, bus);
553         if (r != 0)
554                 return EXIT_FAILURE;
555
556         r = server(bus);
557
558         q = pthread_join(c1, &p);
559         if (q != 0)
560                 return EXIT_FAILURE;
561         if (PTR_TO_INT(p) < 0)
562                 return EXIT_FAILURE;
563
564         q = pthread_join(c2, &p);
565         if (q != 0)
566                 return EXIT_FAILURE;
567         if (PTR_TO_INT(p) < 0)
568                 return EXIT_FAILURE;
569
570         if (r < 0)
571                 return EXIT_FAILURE;
572
573         return EXIT_SUCCESS;
574 }