1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
10 #include "alloc-util.h"
11 #include "bus-internal.h"
15 #include "path-util.h"
16 #include "random-util.h"
18 #include "socket-util.h"
19 #include "string-util.h"
21 #include "time-util.h"
22 #include "tmpfile-util.h"
24 static int method_foobar(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
25 log_info("Got Foobar() call.");
27 assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), 0) >= 0);
28 return sd_bus_reply_method_return(m
, NULL
);
31 static int method_exit(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
32 log_info("Got Exit() call");
34 assert_se(sd_bus_reply_method_return(m
, NULL
) >= 0);
35 /* Simulate D-Bus going away to test the bus_exit_now() path with exit_on_disconnect set */
36 bus_enter_closing(sd_bus_message_get_bus(m
));
40 static const sd_bus_vtable vtable
[] = {
41 SD_BUS_VTABLE_START(0),
42 SD_BUS_METHOD("Foobar", NULL
, NULL
, method_foobar
, SD_BUS_VTABLE_UNPRIVILEGED
),
43 SD_BUS_METHOD("Exit", NULL
, NULL
, method_exit
, SD_BUS_VTABLE_UNPRIVILEGED
),
47 static void* thread_server(void *p
) {
48 _cleanup_free_
char *suffixed
= NULL
, *suffixed_basename
= NULL
, *suffixed2
= NULL
, *d
= NULL
;
49 _cleanup_close_
int fd
= -EBADF
;
50 union sockaddr_union u
;
54 log_debug("Initializing server");
56 /* Let's play some games, by slowly creating the socket directory, and renaming it in the middle */
57 usleep_safe(100 * USEC_PER_MSEC
);
59 assert_se(mkdir_parents(path
, 0755) >= 0);
60 usleep_safe(100 * USEC_PER_MSEC
);
62 assert_se(path_extract_directory(path
, &d
) >= 0);
63 assert_se(asprintf(&suffixed
, "%s.%" PRIx64
, d
, random_u64()) >= 0);
64 assert_se(rename(d
, suffixed
) >= 0);
65 usleep_safe(100 * USEC_PER_MSEC
);
67 assert_se(asprintf(&suffixed2
, "%s.%" PRIx64
, d
, random_u64()) >= 0);
68 assert_se(symlink(suffixed2
, d
) >= 0);
69 usleep_safe(100 * USEC_PER_MSEC
);
71 assert_se(path_extract_filename(suffixed
, &suffixed_basename
) >= 0);
72 assert_se(symlink(suffixed_basename
, suffixed2
) >= 0);
73 usleep_safe(100 * USEC_PER_MSEC
);
76 r
= sockaddr_un_set_path(&u
.un
, path
);
80 fd
= socket(AF_UNIX
, SOCK_STREAM
|SOCK_CLOEXEC
, 0);
83 assert_se(bind(fd
, &u
.sa
, sa_len
) >= 0);
84 usleep_safe(100 * USEC_PER_MSEC
);
86 assert_se(listen(fd
, SOMAXCONN_DELUXE
) >= 0);
87 usleep_safe(100 * USEC_PER_MSEC
);
89 assert_se(touch(path
) >= 0);
90 usleep_safe(100 * USEC_PER_MSEC
);
92 log_debug("Initialized server");
95 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
96 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
100 assert_se(sd_id128_randomize(&id
) >= 0);
102 assert_se(sd_event_new(&event
) >= 0);
104 bus_fd
= accept4(fd
, NULL
, NULL
, SOCK_NONBLOCK
|SOCK_CLOEXEC
);
105 assert_se(bus_fd
>= 0);
107 log_debug("Accepted server connection");
109 assert_se(sd_bus_new(&bus
) >= 0);
110 assert_se(sd_bus_set_exit_on_disconnect(bus
, true) >= 0);
111 assert_se(sd_bus_set_description(bus
, "server") >= 0);
112 assert_se(sd_bus_set_fd(bus
, bus_fd
, bus_fd
) >= 0);
113 assert_se(sd_bus_set_server(bus
, true, id
) >= 0);
114 /* assert_se(sd_bus_set_anonymous(bus, true) >= 0); */
116 assert_se(sd_bus_attach_event(bus
, event
, 0) >= 0);
118 assert_se(sd_bus_add_object_vtable(bus
, NULL
, "/foo", "foo.TestInterface", vtable
, NULL
) >= 0);
120 assert_se(sd_bus_start(bus
) >= 0);
122 assert_se(sd_event_loop(event
) >= 0);
124 assert_se(sd_event_get_exit_code(event
, &code
) >= 0);
130 log_debug("Server done");
135 static void* thread_client1(void *p
) {
136 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
137 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
138 const char *path
= p
, *t
;
141 log_debug("Initializing client1");
143 assert_se(sd_bus_new(&bus
) >= 0);
144 assert_se(sd_bus_set_description(bus
, "client1") >= 0);
146 t
= strjoina("unix:path=", path
);
147 assert_se(sd_bus_set_address(bus
, t
) >= 0);
148 assert_se(sd_bus_set_watch_bind(bus
, true) >= 0);
149 assert_se(sd_bus_start(bus
) >= 0);
151 r
= sd_bus_call_method(bus
, "foo.bar", "/foo", "foo.TestInterface", "Foobar", &error
, NULL
, NULL
);
154 log_debug("Client1 done");
159 static int client2_callback(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
160 assert_se(sd_bus_message_is_method_error(m
, NULL
) == 0);
161 assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), 0) >= 0);
165 static void* thread_client2(void *p
) {
166 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
167 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
168 const char *path
= p
, *t
;
170 log_debug("Initializing client2");
172 assert_se(sd_event_new(&event
) >= 0);
173 assert_se(sd_bus_new(&bus
) >= 0);
174 assert_se(sd_bus_set_description(bus
, "client2") >= 0);
176 t
= strjoina("unix:path=", path
);
177 assert_se(sd_bus_set_address(bus
, t
) >= 0);
178 assert_se(sd_bus_set_watch_bind(bus
, true) >= 0);
179 assert_se(sd_bus_attach_event(bus
, event
, 0) >= 0);
180 assert_se(sd_bus_start(bus
) >= 0);
182 assert_se(sd_bus_call_method_async(bus
, NULL
, "foo.bar", "/foo", "foo.TestInterface", "Foobar", client2_callback
, NULL
, NULL
) >= 0);
184 assert_se(sd_event_loop(event
) >= 0);
186 log_debug("Client2 done");
191 static void request_exit(const char *path
) {
192 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
195 assert_se(sd_bus_new(&bus
) >= 0);
197 t
= strjoina("unix:path=", path
);
198 assert_se(sd_bus_set_address(bus
, t
) >= 0);
199 assert_se(sd_bus_set_watch_bind(bus
, true) >= 0);
200 assert_se(sd_bus_set_description(bus
, "request-exit") >= 0);
201 assert_se(sd_bus_start(bus
) >= 0);
203 assert_se(sd_bus_call_method(bus
, "foo.bar", "/foo", "foo.TestInterface", "Exit", NULL
, NULL
, NULL
) >= 0);
206 int main(int argc
, char *argv
[]) {
207 _cleanup_(rm_rf_physical_and_freep
) char *d
= NULL
;
208 pthread_t server
, client1
, client2
;
211 test_setup_logging(LOG_DEBUG
);
213 /* We use /dev/shm here rather than /tmp, since some weird distros might set up /tmp as some weird fs that
214 * doesn't support inotify properly. */
215 assert_se(mkdtemp_malloc("/dev/shm/systemd-watch-bind-XXXXXX", &d
) >= 0);
217 path
= strjoina(d
, "/this/is/a/socket");
219 assert_se(pthread_create(&server
, NULL
, thread_server
, path
) == 0);
220 assert_se(pthread_create(&client1
, NULL
, thread_client1
, path
) == 0);
221 assert_se(pthread_create(&client2
, NULL
, thread_client2
, path
) == 0);
223 assert_se(pthread_join(client1
, NULL
) == 0);
224 assert_se(pthread_join(client2
, NULL
) == 0);
228 assert_se(pthread_join(server
, NULL
) == 0);