1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
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 
method_foobar(sd_bus_message * m,void * userdata,sd_bus_error * ret_error)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 
method_exit(sd_bus_message * m,void * userdata,sd_bus_error * ret_error)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 
thread_server(void * p)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 r;
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         socklen_t sa_len;
70         r = sockaddr_un_set_path(&u.un, path);
71         assert_se(r >= 0);
72         sa_len = r;
73 
74         fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
75         assert_se(fd >= 0);
76 
77         assert_se(bind(fd, &u.sa, sa_len) >= 0);
78         usleep(100 * USEC_PER_MSEC);
79 
80         assert_se(listen(fd, SOMAXCONN) >= 0);
81         usleep(100 * USEC_PER_MSEC);
82 
83         assert_se(touch(path) >= 0);
84         usleep(100 * USEC_PER_MSEC);
85 
86         log_debug("Initialized server");
87 
88         for (;;) {
89                 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
90                 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
91                 sd_id128_t id;
92                 int bus_fd, code;
93 
94                 assert_se(sd_id128_randomize(&id) >= 0);
95 
96                 assert_se(sd_event_new(&event) >= 0);
97 
98                 bus_fd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
99                 assert_se(bus_fd >= 0);
100 
101                 log_debug("Accepted server connection");
102 
103                 assert_se(sd_bus_new(&bus) >= 0);
104                 assert_se(sd_bus_set_description(bus, "server") >= 0);
105                 assert_se(sd_bus_set_fd(bus, bus_fd, bus_fd) >= 0);
106                 assert_se(sd_bus_set_server(bus, true, id) >= 0);
107                 /* assert_se(sd_bus_set_anonymous(bus, true) >= 0); */
108 
109                 assert_se(sd_bus_attach_event(bus, event, 0) >= 0);
110 
111                 assert_se(sd_bus_add_object_vtable(bus, NULL, "/foo", "foo.TestInterface", vtable, NULL) >= 0);
112 
113                 assert_se(sd_bus_start(bus) >= 0);
114 
115                 assert_se(sd_event_loop(event) >= 0);
116 
117                 assert_se(sd_event_get_exit_code(event, &code) >= 0);
118 
119                 if (code > 0)
120                         break;
121         }
122 
123         log_debug("Server done");
124 
125         return NULL;
126 }
127 
thread_client1(void * p)128 static void* thread_client1(void *p) {
129         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
130         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
131         const char *path = p, *t;
132         int r;
133 
134         log_debug("Initializing client1");
135 
136         assert_se(sd_bus_new(&bus) >= 0);
137         assert_se(sd_bus_set_description(bus, "client1") >= 0);
138 
139         t = strjoina("unix:path=", path);
140         assert_se(sd_bus_set_address(bus, t) >= 0);
141         assert_se(sd_bus_set_watch_bind(bus, true) >= 0);
142         assert_se(sd_bus_start(bus) >= 0);
143 
144         r = sd_bus_call_method(bus, "foo.bar", "/foo", "foo.TestInterface", "Foobar", &error, NULL, NULL);
145         assert_se(r >= 0);
146 
147         log_debug("Client1 done");
148 
149         return NULL;
150 }
151 
client2_callback(sd_bus_message * m,void * userdata,sd_bus_error * ret_error)152 static int client2_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
153         assert_se(sd_bus_message_is_method_error(m, NULL) == 0);
154         assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 0) >= 0);
155         return 0;
156 }
157 
thread_client2(void * p)158 static void* thread_client2(void *p) {
159         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
160         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
161         const char *path = p, *t;
162 
163         log_debug("Initializing client2");
164 
165         assert_se(sd_event_new(&event) >= 0);
166         assert_se(sd_bus_new(&bus) >= 0);
167         assert_se(sd_bus_set_description(bus, "client2") >= 0);
168 
169         t = strjoina("unix:path=", path);
170         assert_se(sd_bus_set_address(bus, t) >= 0);
171         assert_se(sd_bus_set_watch_bind(bus, true) >= 0);
172         assert_se(sd_bus_attach_event(bus, event, 0) >= 0);
173         assert_se(sd_bus_start(bus) >= 0);
174 
175         assert_se(sd_bus_call_method_async(bus, NULL, "foo.bar", "/foo", "foo.TestInterface", "Foobar", client2_callback, NULL, NULL) >= 0);
176 
177         assert_se(sd_event_loop(event) >= 0);
178 
179         log_debug("Client2 done");
180 
181         return NULL;
182 }
183 
request_exit(const char * path)184 static void request_exit(const char *path) {
185         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
186         const char *t;
187 
188         assert_se(sd_bus_new(&bus) >= 0);
189 
190         t = strjoina("unix:path=", path);
191         assert_se(sd_bus_set_address(bus, t) >= 0);
192         assert_se(sd_bus_set_watch_bind(bus, true) >= 0);
193         assert_se(sd_bus_set_description(bus, "request-exit") >= 0);
194         assert_se(sd_bus_start(bus) >= 0);
195 
196         assert_se(sd_bus_call_method(bus, "foo.bar", "/foo", "foo.TestInterface", "Exit", NULL, NULL, NULL) >= 0);
197 }
198 
main(int argc,char * argv[])199 int main(int argc, char *argv[]) {
200         _cleanup_(rm_rf_physical_and_freep) char *d = NULL;
201         pthread_t server, client1, client2;
202         char *path;
203 
204         test_setup_logging(LOG_DEBUG);
205 
206         /* We use /dev/shm here rather than /tmp, since some weird distros might set up /tmp as some weird fs that
207          * doesn't support inotify properly. */
208         assert_se(mkdtemp_malloc("/dev/shm/systemd-watch-bind-XXXXXX", &d) >= 0);
209 
210         path = strjoina(d, "/this/is/a/socket");
211 
212         assert_se(pthread_create(&server, NULL, thread_server, path) == 0);
213         assert_se(pthread_create(&client1, NULL, thread_client1, path) == 0);
214         assert_se(pthread_create(&client2, NULL, thread_client2, path) == 0);
215 
216         assert_se(pthread_join(client1, NULL) == 0);
217         assert_se(pthread_join(client2, NULL) == 0);
218 
219         request_exit(path);
220 
221         assert_se(pthread_join(server, NULL) == 0);
222 
223         return 0;
224 }
225