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