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