]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
8a5cd31e LP |
2 | |
3 | #include <pthread.h> | |
4f18ff2e | 4 | #include <unistd.h> |
8a5cd31e LP |
5 | |
6 | #include "sd-bus.h" | |
7 | #include "sd-event.h" | |
8 | #include "sd-id128.h" | |
9 | ||
10 | #include "alloc-util.h" | |
b5d48627 | 11 | #include "bus-internal.h" |
8a5cd31e | 12 | #include "fd-util.h" |
8a5cd31e LP |
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" | |
f68a2622 | 20 | #include "tests.h" |
5cdf13c7 | 21 | #include "time-util.h" |
1cf40697 | 22 | #include "tmpfile-util.h" |
8a5cd31e LP |
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"); | |
b5d48627 LB |
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; | |
8a5cd31e LP |
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) { | |
6b783209 | 48 | _cleanup_free_ char *suffixed = NULL, *suffixed_basename = NULL, *suffixed2 = NULL, *d = NULL; |
254d1313 | 49 | _cleanup_close_ int fd = -EBADF; |
f36a9d59 | 50 | union sockaddr_union u; |
8a5cd31e | 51 | const char *path = p; |
f36a9d59 | 52 | int r; |
8a5cd31e LP |
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 */ | |
4251512e | 57 | usleep_safe(100 * USEC_PER_MSEC); |
8a5cd31e LP |
58 | |
59 | assert_se(mkdir_parents(path, 0755) >= 0); | |
4251512e | 60 | usleep_safe(100 * USEC_PER_MSEC); |
8a5cd31e | 61 | |
45519d13 | 62 | assert_se(path_extract_directory(path, &d) >= 0); |
8a5cd31e LP |
63 | assert_se(asprintf(&suffixed, "%s.%" PRIx64, d, random_u64()) >= 0); |
64 | assert_se(rename(d, suffixed) >= 0); | |
4251512e | 65 | usleep_safe(100 * USEC_PER_MSEC); |
8a5cd31e LP |
66 | |
67 | assert_se(asprintf(&suffixed2, "%s.%" PRIx64, d, random_u64()) >= 0); | |
68 | assert_se(symlink(suffixed2, d) >= 0); | |
4251512e | 69 | usleep_safe(100 * USEC_PER_MSEC); |
8a5cd31e | 70 | |
6b783209 W |
71 | assert_se(path_extract_filename(suffixed, &suffixed_basename) >= 0); |
72 | assert_se(symlink(suffixed_basename, suffixed2) >= 0); | |
4251512e | 73 | usleep_safe(100 * USEC_PER_MSEC); |
8a5cd31e | 74 | |
f36a9d59 ZJS |
75 | socklen_t sa_len; |
76 | r = sockaddr_un_set_path(&u.un, path); | |
77 | assert_se(r >= 0); | |
78 | sa_len = r; | |
8a5cd31e LP |
79 | |
80 | fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); | |
81 | assert_se(fd >= 0); | |
82 | ||
f36a9d59 | 83 | assert_se(bind(fd, &u.sa, sa_len) >= 0); |
4251512e | 84 | usleep_safe(100 * USEC_PER_MSEC); |
8a5cd31e | 85 | |
768fcd77 | 86 | assert_se(listen(fd, SOMAXCONN_DELUXE) >= 0); |
4251512e | 87 | usleep_safe(100 * USEC_PER_MSEC); |
8a5cd31e LP |
88 | |
89 | assert_se(touch(path) >= 0); | |
4251512e | 90 | usleep_safe(100 * USEC_PER_MSEC); |
8a5cd31e LP |
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); | |
b5d48627 | 110 | assert_se(sd_bus_set_exit_on_disconnect(bus, true) >= 0); |
8a5cd31e LP |
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 | ||
f68a2622 | 211 | test_setup_logging(LOG_DEBUG); |
8a5cd31e LP |
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 | } |