]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/test-bus-watch-bind.c
158def7f75c9de98fffb2d00f13037e6b4262051
[thirdparty/systemd.git] / src / libsystemd / sd-bus / test-bus-watch-bind.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <pthread.h>
4 #include <unistd.h>
5
6 #include "sd-bus.h"
7 #include "sd-event.h"
8 #include "sd-id128.h"
9
10 #include "alloc-util.h"
11 #include "bus-internal.h"
12 #include "fd-util.h"
13 #include "fs-util.h"
14 #include "mkdir.h"
15 #include "path-util.h"
16 #include "random-util.h"
17 #include "rm-rf.h"
18 #include "socket-util.h"
19 #include "string-util.h"
20 #include "tests.h"
21 #include "time-util.h"
22 #include "tmpfile-util.h"
23
24 static int method_foobar(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
25 log_info("Got Foobar() call.");
26
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);
29 }
30
31 static int method_exit(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
32 log_info("Got Exit() call");
33
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));
37 return 0;
38 }
39
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),
44 SD_BUS_VTABLE_END,
45 };
46
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;
51 const char *path = p;
52 int r;
53
54 log_debug("Initializing server");
55
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);
58
59 assert_se(mkdir_parents(path, 0755) >= 0);
60 usleep_safe(100 * USEC_PER_MSEC);
61
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);
66
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);
70
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);
74
75 socklen_t sa_len;
76 r = sockaddr_un_set_path(&u.un, path);
77 assert_se(r >= 0);
78 sa_len = r;
79
80 fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
81 assert_se(fd >= 0);
82
83 assert_se(bind(fd, &u.sa, sa_len) >= 0);
84 usleep_safe(100 * USEC_PER_MSEC);
85
86 assert_se(listen(fd, SOMAXCONN_DELUXE) >= 0);
87 usleep_safe(100 * USEC_PER_MSEC);
88
89 assert_se(touch(path) >= 0);
90 usleep_safe(100 * USEC_PER_MSEC);
91
92 log_debug("Initialized server");
93
94 for (;;) {
95 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
96 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
97 sd_id128_t id;
98 int bus_fd, code;
99
100 assert_se(sd_id128_randomize(&id) >= 0);
101
102 assert_se(sd_event_new(&event) >= 0);
103
104 bus_fd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
105 assert_se(bus_fd >= 0);
106
107 log_debug("Accepted server connection");
108
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); */
115
116 assert_se(sd_bus_attach_event(bus, event, 0) >= 0);
117
118 assert_se(sd_bus_add_object_vtable(bus, NULL, "/foo", "foo.TestInterface", vtable, NULL) >= 0);
119
120 assert_se(sd_bus_start(bus) >= 0);
121
122 assert_se(sd_event_loop(event) >= 0);
123
124 assert_se(sd_event_get_exit_code(event, &code) >= 0);
125
126 if (code > 0)
127 break;
128 }
129
130 log_debug("Server done");
131
132 return NULL;
133 }
134
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;
139 int r;
140
141 log_debug("Initializing client1");
142
143 assert_se(sd_bus_new(&bus) >= 0);
144 assert_se(sd_bus_set_description(bus, "client1") >= 0);
145
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);
150
151 r = sd_bus_call_method(bus, "foo.bar", "/foo", "foo.TestInterface", "Foobar", &error, NULL, NULL);
152 assert_se(r >= 0);
153
154 log_debug("Client1 done");
155
156 return NULL;
157 }
158
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);
162 return 0;
163 }
164
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;
169
170 log_debug("Initializing client2");
171
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);
175
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);
181
182 assert_se(sd_bus_call_method_async(bus, NULL, "foo.bar", "/foo", "foo.TestInterface", "Foobar", client2_callback, NULL, NULL) >= 0);
183
184 assert_se(sd_event_loop(event) >= 0);
185
186 log_debug("Client2 done");
187
188 return NULL;
189 }
190
191 static void request_exit(const char *path) {
192 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
193 const char *t;
194
195 assert_se(sd_bus_new(&bus) >= 0);
196
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);
202
203 assert_se(sd_bus_call_method(bus, "foo.bar", "/foo", "foo.TestInterface", "Exit", NULL, NULL, NULL) >= 0);
204 }
205
206 int main(int argc, char *argv[]) {
207 _cleanup_(rm_rf_physical_and_freep) char *d = NULL;
208 pthread_t server, client1, client2;
209 char *path;
210
211 test_setup_logging(LOG_DEBUG);
212
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);
216
217 path = strjoina(d, "/this/is/a/socket");
218
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);
222
223 assert_se(pthread_join(client1, NULL) == 0);
224 assert_se(pthread_join(client2, NULL) == 0);
225
226 request_exit(path);
227
228 assert_se(pthread_join(server, NULL) == 0);
229
230 return 0;
231 }