]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/test-bus-watch-bind.c
Merge pull request #34499 from YHNdnzj/sd-path-trivial-cleanup
[thirdparty/systemd.git] / src / libsystemd / sd-bus / test-bus-watch-bind.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
8a5cd31e
LP
2
3#include <pthread.h>
4
5#include "sd-bus.h"
6#include "sd-event.h"
7#include "sd-id128.h"
8
9#include "alloc-util.h"
b5d48627 10#include "bus-internal.h"
8a5cd31e 11#include "fd-util.h"
8a5cd31e
LP
12#include "fs-util.h"
13#include "mkdir.h"
14#include "path-util.h"
15#include "random-util.h"
16#include "rm-rf.h"
17#include "socket-util.h"
18#include "string-util.h"
e4de7287 19#include "tmpfile-util.h"
f68a2622 20#include "tests.h"
8a5cd31e
LP
21
22static int method_foobar(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
23 log_info("Got Foobar() call.");
24
25 assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 0) >= 0);
26 return sd_bus_reply_method_return(m, NULL);
27}
28
29static int method_exit(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
30 log_info("Got Exit() call");
b5d48627
LB
31
32 assert_se(sd_bus_reply_method_return(m, NULL) >= 0);
33 /* Simulate D-Bus going away to test the bus_exit_now() path with exit_on_disconnect set */
34 bus_enter_closing(sd_bus_message_get_bus(m));
35 return 0;
8a5cd31e
LP
36}
37
38static const sd_bus_vtable vtable[] = {
39 SD_BUS_VTABLE_START(0),
40 SD_BUS_METHOD("Foobar", NULL, NULL, method_foobar, SD_BUS_VTABLE_UNPRIVILEGED),
41 SD_BUS_METHOD("Exit", NULL, NULL, method_exit, SD_BUS_VTABLE_UNPRIVILEGED),
42 SD_BUS_VTABLE_END,
43};
44
45static void* thread_server(void *p) {
46 _cleanup_free_ char *suffixed = NULL, *suffixed2 = NULL, *d = NULL;
254d1313 47 _cleanup_close_ int fd = -EBADF;
f36a9d59 48 union sockaddr_union u;
8a5cd31e 49 const char *path = p;
f36a9d59 50 int r;
8a5cd31e
LP
51
52 log_debug("Initializing server");
53
54 /* Let's play some games, by slowly creating the socket directory, and renaming it in the middle */
4251512e 55 usleep_safe(100 * USEC_PER_MSEC);
8a5cd31e
LP
56
57 assert_se(mkdir_parents(path, 0755) >= 0);
4251512e 58 usleep_safe(100 * USEC_PER_MSEC);
8a5cd31e 59
45519d13 60 assert_se(path_extract_directory(path, &d) >= 0);
8a5cd31e
LP
61 assert_se(asprintf(&suffixed, "%s.%" PRIx64, d, random_u64()) >= 0);
62 assert_se(rename(d, suffixed) >= 0);
4251512e 63 usleep_safe(100 * USEC_PER_MSEC);
8a5cd31e
LP
64
65 assert_se(asprintf(&suffixed2, "%s.%" PRIx64, d, random_u64()) >= 0);
66 assert_se(symlink(suffixed2, d) >= 0);
4251512e 67 usleep_safe(100 * USEC_PER_MSEC);
8a5cd31e
LP
68
69 assert_se(symlink(basename(suffixed), suffixed2) >= 0);
4251512e 70 usleep_safe(100 * USEC_PER_MSEC);
8a5cd31e 71
f36a9d59
ZJS
72 socklen_t sa_len;
73 r = sockaddr_un_set_path(&u.un, path);
74 assert_se(r >= 0);
75 sa_len = r;
8a5cd31e
LP
76
77 fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
78 assert_se(fd >= 0);
79
f36a9d59 80 assert_se(bind(fd, &u.sa, sa_len) >= 0);
4251512e 81 usleep_safe(100 * USEC_PER_MSEC);
8a5cd31e 82
768fcd77 83 assert_se(listen(fd, SOMAXCONN_DELUXE) >= 0);
4251512e 84 usleep_safe(100 * USEC_PER_MSEC);
8a5cd31e
LP
85
86 assert_se(touch(path) >= 0);
4251512e 87 usleep_safe(100 * USEC_PER_MSEC);
8a5cd31e
LP
88
89 log_debug("Initialized server");
90
91 for (;;) {
92 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
93 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
94 sd_id128_t id;
95 int bus_fd, code;
96
97 assert_se(sd_id128_randomize(&id) >= 0);
98
99 assert_se(sd_event_new(&event) >= 0);
100
101 bus_fd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
102 assert_se(bus_fd >= 0);
103
104 log_debug("Accepted server connection");
105
106 assert_se(sd_bus_new(&bus) >= 0);
b5d48627 107 assert_se(sd_bus_set_exit_on_disconnect(bus, true) >= 0);
8a5cd31e
LP
108 assert_se(sd_bus_set_description(bus, "server") >= 0);
109 assert_se(sd_bus_set_fd(bus, bus_fd, bus_fd) >= 0);
110 assert_se(sd_bus_set_server(bus, true, id) >= 0);
111 /* assert_se(sd_bus_set_anonymous(bus, true) >= 0); */
112
113 assert_se(sd_bus_attach_event(bus, event, 0) >= 0);
114
115 assert_se(sd_bus_add_object_vtable(bus, NULL, "/foo", "foo.TestInterface", vtable, NULL) >= 0);
116
117 assert_se(sd_bus_start(bus) >= 0);
118
119 assert_se(sd_event_loop(event) >= 0);
120
121 assert_se(sd_event_get_exit_code(event, &code) >= 0);
122
123 if (code > 0)
124 break;
125 }
126
127 log_debug("Server done");
128
129 return NULL;
130}
131
132static void* thread_client1(void *p) {
133 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
134 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
135 const char *path = p, *t;
136 int r;
137
138 log_debug("Initializing client1");
139
140 assert_se(sd_bus_new(&bus) >= 0);
141 assert_se(sd_bus_set_description(bus, "client1") >= 0);
142
143 t = strjoina("unix:path=", path);
144 assert_se(sd_bus_set_address(bus, t) >= 0);
145 assert_se(sd_bus_set_watch_bind(bus, true) >= 0);
146 assert_se(sd_bus_start(bus) >= 0);
147
148 r = sd_bus_call_method(bus, "foo.bar", "/foo", "foo.TestInterface", "Foobar", &error, NULL, NULL);
149 assert_se(r >= 0);
150
151 log_debug("Client1 done");
152
153 return NULL;
154}
155
156static int client2_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
157 assert_se(sd_bus_message_is_method_error(m, NULL) == 0);
158 assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 0) >= 0);
159 return 0;
160}
161
162static void* thread_client2(void *p) {
163 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
164 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
165 const char *path = p, *t;
166
167 log_debug("Initializing client2");
168
169 assert_se(sd_event_new(&event) >= 0);
170 assert_se(sd_bus_new(&bus) >= 0);
171 assert_se(sd_bus_set_description(bus, "client2") >= 0);
172
173 t = strjoina("unix:path=", path);
174 assert_se(sd_bus_set_address(bus, t) >= 0);
175 assert_se(sd_bus_set_watch_bind(bus, true) >= 0);
176 assert_se(sd_bus_attach_event(bus, event, 0) >= 0);
177 assert_se(sd_bus_start(bus) >= 0);
178
179 assert_se(sd_bus_call_method_async(bus, NULL, "foo.bar", "/foo", "foo.TestInterface", "Foobar", client2_callback, NULL, NULL) >= 0);
180
181 assert_se(sd_event_loop(event) >= 0);
182
183 log_debug("Client2 done");
184
185 return NULL;
186}
187
188static void request_exit(const char *path) {
189 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
190 const char *t;
191
192 assert_se(sd_bus_new(&bus) >= 0);
193
194 t = strjoina("unix:path=", path);
195 assert_se(sd_bus_set_address(bus, t) >= 0);
196 assert_se(sd_bus_set_watch_bind(bus, true) >= 0);
197 assert_se(sd_bus_set_description(bus, "request-exit") >= 0);
198 assert_se(sd_bus_start(bus) >= 0);
199
200 assert_se(sd_bus_call_method(bus, "foo.bar", "/foo", "foo.TestInterface", "Exit", NULL, NULL, NULL) >= 0);
201}
202
203int main(int argc, char *argv[]) {
204 _cleanup_(rm_rf_physical_and_freep) char *d = NULL;
205 pthread_t server, client1, client2;
206 char *path;
207
f68a2622 208 test_setup_logging(LOG_DEBUG);
8a5cd31e
LP
209
210 /* We use /dev/shm here rather than /tmp, since some weird distros might set up /tmp as some weird fs that
211 * doesn't support inotify properly. */
212 assert_se(mkdtemp_malloc("/dev/shm/systemd-watch-bind-XXXXXX", &d) >= 0);
213
214 path = strjoina(d, "/this/is/a/socket");
215
216 assert_se(pthread_create(&server, NULL, thread_server, path) == 0);
217 assert_se(pthread_create(&client1, NULL, thread_client1, path) == 0);
218 assert_se(pthread_create(&client2, NULL, thread_client2, path) == 0);
219
220 assert_se(pthread_join(client1, NULL) == 0);
221 assert_se(pthread_join(client2, NULL) == 0);
222
223 request_exit(path);
224
225 assert_se(pthread_join(server, NULL) == 0);
226
227 return 0;
228}