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