]>
Commit | Line | Data |
---|---|---|
8a5cd31e LP |
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
2 | /*** | |
3 | This file is part of systemd. | |
4 | ||
5 | Copyright 2017 Lennart Poettering | |
6 | ||
7 | systemd is free software; you can redistribute it and/or modify it | |
8 | under the terms of the GNU Lesser General Public License as published by | |
9 | the Free Software Foundation; either version 2.1 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | systemd is distributed in the hope that it will be useful, but | |
13 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | Lesser General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU Lesser General Public License | |
18 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
19 | ***/ | |
20 | ||
21 | #include <pthread.h> | |
22 | ||
23 | #include "sd-bus.h" | |
24 | #include "sd-event.h" | |
25 | #include "sd-id128.h" | |
26 | ||
27 | #include "alloc-util.h" | |
28 | #include "fd-util.h" | |
29 | #include "fileio.h" | |
30 | #include "fs-util.h" | |
31 | #include "mkdir.h" | |
32 | #include "path-util.h" | |
33 | #include "random-util.h" | |
34 | #include "rm-rf.h" | |
35 | #include "socket-util.h" | |
36 | #include "string-util.h" | |
37 | ||
38 | static int method_foobar(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { | |
39 | log_info("Got Foobar() call."); | |
40 | ||
41 | assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 0) >= 0); | |
42 | return sd_bus_reply_method_return(m, NULL); | |
43 | } | |
44 | ||
45 | static int method_exit(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { | |
46 | log_info("Got Exit() call"); | |
47 | assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 1) >= 0); | |
48 | return sd_bus_reply_method_return(m, NULL); | |
49 | } | |
50 | ||
51 | static const sd_bus_vtable vtable[] = { | |
52 | SD_BUS_VTABLE_START(0), | |
53 | SD_BUS_METHOD("Foobar", NULL, NULL, method_foobar, SD_BUS_VTABLE_UNPRIVILEGED), | |
54 | SD_BUS_METHOD("Exit", NULL, NULL, method_exit, SD_BUS_VTABLE_UNPRIVILEGED), | |
55 | SD_BUS_VTABLE_END, | |
56 | }; | |
57 | ||
58 | static void* thread_server(void *p) { | |
59 | _cleanup_free_ char *suffixed = NULL, *suffixed2 = NULL, *d = NULL; | |
60 | _cleanup_close_ int fd = -1; | |
61 | union sockaddr_union u = { | |
62 | .un.sun_family = AF_UNIX, | |
63 | }; | |
64 | const char *path = p; | |
65 | ||
66 | log_debug("Initializing server"); | |
67 | ||
68 | /* Let's play some games, by slowly creating the socket directory, and renaming it in the middle */ | |
69 | (void) usleep(100 * USEC_PER_MSEC); | |
70 | ||
71 | assert_se(mkdir_parents(path, 0755) >= 0); | |
72 | (void) usleep(100 * USEC_PER_MSEC); | |
73 | ||
74 | d = dirname_malloc(path); | |
75 | assert_se(d); | |
76 | assert_se(asprintf(&suffixed, "%s.%" PRIx64, d, random_u64()) >= 0); | |
77 | assert_se(rename(d, suffixed) >= 0); | |
78 | (void) usleep(100 * USEC_PER_MSEC); | |
79 | ||
80 | assert_se(asprintf(&suffixed2, "%s.%" PRIx64, d, random_u64()) >= 0); | |
81 | assert_se(symlink(suffixed2, d) >= 0); | |
82 | (void) usleep(100 * USEC_PER_MSEC); | |
83 | ||
84 | assert_se(symlink(basename(suffixed), suffixed2) >= 0); | |
85 | (void) usleep(100 * USEC_PER_MSEC); | |
86 | ||
87 | strncpy(u.un.sun_path, path, sizeof(u.un.sun_path)); | |
88 | ||
89 | fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); | |
90 | assert_se(fd >= 0); | |
91 | ||
92 | assert_se(bind(fd, &u.sa, SOCKADDR_UN_LEN(u.un)) >= 0); | |
93 | usleep(100 * USEC_PER_MSEC); | |
94 | ||
95 | assert_se(listen(fd, SOMAXCONN) >= 0); | |
96 | usleep(100 * USEC_PER_MSEC); | |
97 | ||
98 | assert_se(touch(path) >= 0); | |
99 | usleep(100 * USEC_PER_MSEC); | |
100 | ||
101 | log_debug("Initialized server"); | |
102 | ||
103 | for (;;) { | |
104 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; | |
105 | _cleanup_(sd_event_unrefp) sd_event *event = NULL; | |
106 | sd_id128_t id; | |
107 | int bus_fd, code; | |
108 | ||
109 | assert_se(sd_id128_randomize(&id) >= 0); | |
110 | ||
111 | assert_se(sd_event_new(&event) >= 0); | |
112 | ||
113 | bus_fd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); | |
114 | assert_se(bus_fd >= 0); | |
115 | ||
116 | log_debug("Accepted server connection"); | |
117 | ||
118 | assert_se(sd_bus_new(&bus) >= 0); | |
119 | assert_se(sd_bus_set_description(bus, "server") >= 0); | |
120 | assert_se(sd_bus_set_fd(bus, bus_fd, bus_fd) >= 0); | |
121 | assert_se(sd_bus_set_server(bus, true, id) >= 0); | |
122 | /* assert_se(sd_bus_set_anonymous(bus, true) >= 0); */ | |
123 | ||
124 | assert_se(sd_bus_attach_event(bus, event, 0) >= 0); | |
125 | ||
126 | assert_se(sd_bus_add_object_vtable(bus, NULL, "/foo", "foo.TestInterface", vtable, NULL) >= 0); | |
127 | ||
128 | assert_se(sd_bus_start(bus) >= 0); | |
129 | ||
130 | assert_se(sd_event_loop(event) >= 0); | |
131 | ||
132 | assert_se(sd_event_get_exit_code(event, &code) >= 0); | |
133 | ||
134 | if (code > 0) | |
135 | break; | |
136 | } | |
137 | ||
138 | log_debug("Server done"); | |
139 | ||
140 | return NULL; | |
141 | } | |
142 | ||
143 | static void* thread_client1(void *p) { | |
144 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
145 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; | |
146 | const char *path = p, *t; | |
147 | int r; | |
148 | ||
149 | log_debug("Initializing client1"); | |
150 | ||
151 | assert_se(sd_bus_new(&bus) >= 0); | |
152 | assert_se(sd_bus_set_description(bus, "client1") >= 0); | |
153 | ||
154 | t = strjoina("unix:path=", path); | |
155 | assert_se(sd_bus_set_address(bus, t) >= 0); | |
156 | assert_se(sd_bus_set_watch_bind(bus, true) >= 0); | |
157 | assert_se(sd_bus_start(bus) >= 0); | |
158 | ||
159 | r = sd_bus_call_method(bus, "foo.bar", "/foo", "foo.TestInterface", "Foobar", &error, NULL, NULL); | |
160 | assert_se(r >= 0); | |
161 | ||
162 | log_debug("Client1 done"); | |
163 | ||
164 | return NULL; | |
165 | } | |
166 | ||
167 | static int client2_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { | |
168 | assert_se(sd_bus_message_is_method_error(m, NULL) == 0); | |
169 | assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 0) >= 0); | |
170 | return 0; | |
171 | } | |
172 | ||
173 | static void* thread_client2(void *p) { | |
174 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; | |
175 | _cleanup_(sd_event_unrefp) sd_event *event = NULL; | |
176 | const char *path = p, *t; | |
177 | ||
178 | log_debug("Initializing client2"); | |
179 | ||
180 | assert_se(sd_event_new(&event) >= 0); | |
181 | assert_se(sd_bus_new(&bus) >= 0); | |
182 | assert_se(sd_bus_set_description(bus, "client2") >= 0); | |
183 | ||
184 | t = strjoina("unix:path=", path); | |
185 | assert_se(sd_bus_set_address(bus, t) >= 0); | |
186 | assert_se(sd_bus_set_watch_bind(bus, true) >= 0); | |
187 | assert_se(sd_bus_attach_event(bus, event, 0) >= 0); | |
188 | assert_se(sd_bus_start(bus) >= 0); | |
189 | ||
190 | assert_se(sd_bus_call_method_async(bus, NULL, "foo.bar", "/foo", "foo.TestInterface", "Foobar", client2_callback, NULL, NULL) >= 0); | |
191 | ||
192 | assert_se(sd_event_loop(event) >= 0); | |
193 | ||
194 | log_debug("Client2 done"); | |
195 | ||
196 | return NULL; | |
197 | } | |
198 | ||
199 | static void request_exit(const char *path) { | |
200 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; | |
201 | const char *t; | |
202 | ||
203 | assert_se(sd_bus_new(&bus) >= 0); | |
204 | ||
205 | t = strjoina("unix:path=", path); | |
206 | assert_se(sd_bus_set_address(bus, t) >= 0); | |
207 | assert_se(sd_bus_set_watch_bind(bus, true) >= 0); | |
208 | assert_se(sd_bus_set_description(bus, "request-exit") >= 0); | |
209 | assert_se(sd_bus_start(bus) >= 0); | |
210 | ||
211 | assert_se(sd_bus_call_method(bus, "foo.bar", "/foo", "foo.TestInterface", "Exit", NULL, NULL, NULL) >= 0); | |
212 | } | |
213 | ||
214 | int main(int argc, char *argv[]) { | |
215 | _cleanup_(rm_rf_physical_and_freep) char *d = NULL; | |
216 | pthread_t server, client1, client2; | |
217 | char *path; | |
218 | ||
219 | log_set_max_level(LOG_DEBUG); | |
220 | ||
221 | /* We use /dev/shm here rather than /tmp, since some weird distros might set up /tmp as some weird fs that | |
222 | * doesn't support inotify properly. */ | |
223 | assert_se(mkdtemp_malloc("/dev/shm/systemd-watch-bind-XXXXXX", &d) >= 0); | |
224 | ||
225 | path = strjoina(d, "/this/is/a/socket"); | |
226 | ||
227 | assert_se(pthread_create(&server, NULL, thread_server, path) == 0); | |
228 | assert_se(pthread_create(&client1, NULL, thread_client1, path) == 0); | |
229 | assert_se(pthread_create(&client2, NULL, thread_client2, path) == 0); | |
230 | ||
231 | assert_se(pthread_join(client1, NULL) == 0); | |
232 | assert_se(pthread_join(client2, NULL) == 0); | |
233 | ||
234 | request_exit(path); | |
235 | ||
236 | assert_se(pthread_join(server, NULL) == 0); | |
237 | ||
238 | return 0; | |
239 | } |