chiark / gitweb /
journalctl: use _COMM= match for scripts
[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, 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, sd_bus_message *m, void *userdata) {
44         int r;
45
46         assert(bus);
47
48         if (sd_bus_message_is_method_error(m, NULL))
49                 return 0;
50
51         if (sd_bus_message_is_method_call(m, "org.object.test", "Foobar")) {
52                 log_info("Invoked Foobar() on %s", sd_bus_message_get_path(m));
53
54                 r = sd_bus_reply_method_return(bus, m, 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_server_id(bus, &id);
81         if (r < 0) {
82                 log_error("Failed to get server 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         r = sd_bus_add_match(bus, "type='signal',interface='foo.bar',member='Notify'", match_callback, NULL);
109         if (r < 0) {
110                 log_error("Failed to add match: %s", strerror(-r));
111                 goto fail;
112         }
113
114         r = sd_bus_add_match(bus, "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'", match_callback, NULL);
115         if (r < 0) {
116                 log_error("Failed to add match: %s", strerror(-r));
117                 goto fail;
118         }
119
120         bus_match_dump(&bus->match_callbacks, 0);
121
122         *_bus = bus;
123         return 0;
124
125 fail:
126         if (bus)
127                 sd_bus_unref(bus);
128
129         return r;
130 }
131
132 static int server(sd_bus *bus) {
133         int r;
134         bool client1_gone = false, client2_gone = false;
135
136         while (!client1_gone || !client2_gone) {
137                 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
138                 pid_t pid = 0;
139                 const char *label = NULL;
140
141                 r = sd_bus_process(bus, &m);
142                 if (r < 0) {
143                         log_error("Failed to process requests: %s", strerror(-r));
144                         goto fail;
145                 }
146
147                 if (r == 0) {
148                         r = sd_bus_wait(bus, (uint64_t) -1);
149                         if (r < 0) {
150                                 log_error("Failed to wait: %s", strerror(-r));
151                                 goto fail;
152                         }
153
154                         continue;
155                 }
156
157                 if (!m)
158                         continue;
159
160                 sd_bus_message_get_pid(m, &pid);
161                 sd_bus_message_get_selinux_context(m, &label);
162                 log_info("Got message! member=%s pid=%lu label=%s",
163                          strna(sd_bus_message_get_member(m)),
164                          (unsigned long) pid,
165                          strna(label));
166                 /* bus_message_dump(m); */
167                 /* sd_bus_message_rewind(m, true); */
168
169                 if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "LowerCase")) {
170                         const char *hello;
171                         _cleanup_free_ char *lowercase = NULL;
172
173                         r = sd_bus_message_read(m, "s", &hello);
174                         if (r < 0) {
175                                 log_error("Failed to get parameter: %s", strerror(-r));
176                                 goto fail;
177                         }
178
179                         lowercase = strdup(hello);
180                         if (!lowercase) {
181                                 r = log_oom();
182                                 goto fail;
183                         }
184
185                         ascii_strlower(lowercase);
186
187                         r = sd_bus_reply_method_return(bus, m, "s", lowercase);
188                         if (r < 0) {
189                                 log_error("Failed to send reply: %s", strerror(-r));
190                                 goto fail;
191                         }
192                 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient1")) {
193
194                         r = sd_bus_reply_method_return(bus, m, NULL);
195                         if (r < 0) {
196                                 log_error("Failed to send reply: %s", strerror(-r));
197                                 goto fail;
198                         }
199
200                         client1_gone = true;
201                 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient2")) {
202
203                         r = sd_bus_reply_method_return(bus, m, NULL);
204                         if (r < 0) {
205                                 log_error("Failed to send reply: %s", strerror(-r));
206                                 goto fail;
207                         }
208
209                         client2_gone = true;
210                 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "Slow")) {
211
212                         sleep(1);
213
214                         r = sd_bus_reply_method_return(bus, m, NULL);
215                         if (r < 0) {
216                                 log_error("Failed to send reply: %s", strerror(-r));
217                                 goto fail;
218                         }
219
220                 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "FileDescriptor")) {
221                         int fd;
222                         static const char x = 'X';
223
224                         r = sd_bus_message_read(m, "h", &fd);
225                         if (r < 0) {
226                                 log_error("Failed to get parameter: %s", strerror(-r));
227                                 goto fail;
228                         }
229
230                         if (write(fd, &x, 1) < 0) {
231                                 log_error("Failed to write to fd: %m");
232                                 close_nointr_nofail(fd);
233                                 goto fail;
234                         }
235
236                         r = sd_bus_reply_method_return(bus, m, NULL);
237                         if (r < 0) {
238                                 log_error("Failed to send reply: %s", strerror(-r));
239                                 goto fail;
240                         }
241
242                 } else if (sd_bus_message_is_method_call(m, NULL, NULL)) {
243
244                         r = sd_bus_reply_method_error(
245                                         bus, m,
246                                         &SD_BUS_ERROR_MAKE("org.freedesktop.DBus.Error.UnknownMethod", "Unknown method."));
247                         if (r < 0) {
248                                 log_error("Failed to send reply: %s", strerror(-r));
249                                 goto fail;
250                         }
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 *reply = NULL;
267         sd_bus *bus = NULL;
268         sd_bus_error error = SD_BUS_ERROR_NULL;
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_call_method(
281                         bus,
282                         "org.freedesktop.systemd.test",
283                         "/",
284                         "org.freedesktop.systemd.test",
285                         "LowerCase",
286                         &error,
287                         &reply,
288                         "s",
289                         "HELLO");
290         if (r < 0) {
291                 log_error("Failed to issue method call: %s", strerror(-r));
292                 goto finish;
293         }
294
295         r = sd_bus_message_read(reply, "s", &hello);
296         if (r < 0) {
297                 log_error("Failed to get string: %s", strerror(-r));
298                 goto finish;
299         }
300
301         assert(streq(hello, "hello"));
302
303         if (pipe2(pp, O_CLOEXEC|O_NONBLOCK) < 0) {
304                 log_error("Failed to allocate pipe: %m");
305                 r = -errno;
306                 goto finish;
307         }
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("Failed to issue method call: %s", strerror(-r));
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                                 "org.freedesktop.systemd.test",
339                                 "/",
340                                 "org.freedesktop.systemd.test",
341                                 "ExitClient1",
342                                 &q);
343                 if (r < 0)
344                         log_error("Failed to allocate method call: %s", strerror(-r));
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         close_pipe(pp);
355
356         return INT_TO_PTR(r);
357 }
358
359 static int quit_callback(sd_bus *b, sd_bus_message *m, void *userdata) {
360         bool *x = userdata;
361
362         log_error("Quit callback: %s", strerror(bus_message_to_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("Failed to connect to user bus: %s", strerror(-r));
379                 goto finish;
380         }
381
382         r = sd_bus_message_new_method_call(
383                         bus,
384                         "org.freedesktop.systemd.test",
385                         "/foo/bar/waldo/piep",
386                         "org.object.test",
387                         "Foobar",
388                         &m);
389         if (r < 0) {
390                 log_error("Failed to allocate method call: %s", strerror(-r));
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                         "/foobar",
406                         "foo.bar",
407                         "Notify",
408                         &m);
409         if (r < 0) {
410                 log_error("Failed to allocate signal: %s", strerror(-r));
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                         "org.freedesktop.systemd.test",
426                         "/",
427                         "org.freedesktop.DBus.Peer",
428                         "GetMachineId",
429                         &m);
430         if (r < 0) {
431                 log_error("Failed to allocate method call: %s", strerror(-r));
432                 goto finish;
433         }
434
435         r = sd_bus_send_with_reply_and_block(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("Failed to parse machine ID: %s", strerror(-r));
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                         "org.freedesktop.systemd.test",
455                         "/",
456                         "org.freedesktop.systemd.test",
457                         "Slow",
458                         &m);
459         if (r < 0) {
460                 log_error("Failed to allocate method call: %s", strerror(-r));
461                 goto finish;
462         }
463
464         sd_bus_message_unref(reply);
465         reply = NULL;
466
467         r = sd_bus_send_with_reply_and_block(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                         "org.freedesktop.systemd.test",
479                         "/",
480                         "org.freedesktop.systemd.test",
481                         "Slow",
482                         &m);
483         if (r < 0) {
484                 log_error("Failed to allocate method call: %s", strerror(-r));
485                 goto finish;
486         }
487
488         r = sd_bus_send_with_reply(bus, m, quit_callback, &quit, 200 * USEC_PER_MSEC, NULL);
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("Failed to process requests: %s", strerror(-r));
498                         goto finish;
499                 }
500                 if (r == 0) {
501                         r = sd_bus_wait(bus, (uint64_t) -1);
502                         if (r < 0) {
503                                 log_error("Failed to wait: %s", strerror(-r));
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                                 "org.freedesktop.systemd.test",
518                                 "/",
519                                 "org.freedesktop.systemd.test",
520                                 "ExitClient2",
521                                 &q);
522                 if (r < 0) {
523                         log_error("Failed to allocate method call: %s", strerror(-r));
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 }