]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/test/test-varlink.c
varlink,json: introduce new varlink_dispatch() helper
[thirdparty/systemd.git] / src / test / test-varlink.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
635d059f
LP
2
3#include <fcntl.h>
4#include <poll.h>
5#include <pthread.h>
6
7#include "sd-event.h"
8
7947dbe3 9#include "data-fd-util.h"
635d059f
LP
10#include "fd-util.h"
11#include "json.h"
12#include "rm-rf.h"
13#include "strv.h"
14#include "tmpfile-util.h"
15#include "user-util.h"
16#include "varlink.h"
17
18/* Let's pick some high value, that is higher than the largest listen() backlog, but leaves enough room below
19 the typical RLIMIT_NOFILE value of 1024 so that we can process both sides of each socket in our
20 process. Or in other words: "OVERLOAD_CONNECTIONS * 2 + x < 1024" should hold, for some small x that
21 should cover any auxiliary fds, the listener server fds, stdin/stdout/stderr and whatever else. */
22#define OVERLOAD_CONNECTIONS 333
23
24static int n_done = 0;
254d1313 25static int block_write_fd = -EBADF;
635d059f
LP
26
27static int method_something(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
28 _cleanup_(json_variant_unrefp) JsonVariant *ret = NULL;
29 JsonVariant *a, *b;
718ca772 30 int64_t x, y;
635d059f
LP
31 int r;
32
33 a = json_variant_by_key(parameters, "a");
34 if (!a)
35 return varlink_error(link, "io.test.BadParameters", NULL);
36
37 x = json_variant_integer(a);
38
39 b = json_variant_by_key(parameters, "b");
40 if (!b)
41 return varlink_error(link, "io.test.BadParameters", NULL);
42
43 y = json_variant_integer(b);
44
45 r = json_build(&ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("sum", JSON_BUILD_INTEGER(x + y))));
46 if (r < 0)
47 return r;
48
49 return varlink_reply(link, ret);
50}
51
1bd0b9c0
AS
52static int method_something_more(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
53 _cleanup_(json_variant_unrefp) JsonVariant *ret = NULL;
54 int r;
55
56 struct Something {
57 int x;
58 int y;
59 };
60
61 static const JsonDispatch dispatch_table[] = {
f1b622a0 62 { "a", JSON_VARIANT_INTEGER, json_dispatch_int, offsetof(struct Something, x), JSON_MANDATORY },
1bd0b9c0
AS
63 { "b", JSON_VARIANT_INTEGER, json_dispatch_int, offsetof(struct Something, y), JSON_MANDATORY},
64 {}
65 };
66 struct Something s = {};
67
f1b622a0
LP
68 r = varlink_dispatch(link, parameters, dispatch_table, &s);
69 if (r != 0)
70 return r;
1bd0b9c0
AS
71
72 for (int i = 0; i < 5; i++) {
73 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
74
75 r = json_build(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("sum", JSON_BUILD_INTEGER(s.x + (s.y * i)))));
76 if (r < 0)
77 return r;
78
79 r = varlink_notify(link, w);
80 if (r < 0)
81 return r;
82 }
83
84 r = json_build(&ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("sum", JSON_BUILD_INTEGER(s.x + (s.y * 5)))));
85 if (r < 0)
86 return r;
87
88 return varlink_reply(link, ret);
89}
90
7947dbe3
LP
91static void test_fd(int fd, const void *buf, size_t n) {
92 char rbuf[n + 1];
93 ssize_t m;
94
95 m = read(fd, rbuf, n + 1);
85ba4ca8 96 assert_se(m >= 0);
7947dbe3
LP
97 assert_se(memcmp_nn(buf, n, rbuf, m) == 0);
98}
99
100static int method_passfd(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
101 _cleanup_(json_variant_unrefp) JsonVariant *ret = NULL;
102 JsonVariant *a;
103 int r;
104
105 a = json_variant_by_key(parameters, "fd");
106 if (!a)
107 return varlink_error(link, "io.test.BadParameters", NULL);
108
109 assert_se(streq_ptr(json_variant_string(a), "whoop"));
110
111 int xx = varlink_peek_fd(link, 0),
112 yy = varlink_peek_fd(link, 1),
113 zz = varlink_peek_fd(link, 2);
114
115 log_info("%i %i %i", xx, yy, zz);
116
85ba4ca8
YW
117 assert_se(xx >= 0);
118 assert_se(yy >= 0);
119 assert_se(zz >= 0);
120
7947dbe3
LP
121 test_fd(xx, "foo", 3);
122 test_fd(yy, "bar", 3);
123 test_fd(zz, "quux", 4);
124
125 _cleanup_close_ int vv = acquire_data_fd("miau", 4, 0);
126 _cleanup_close_ int ww = acquire_data_fd("wuff", 4, 0);
127
128 assert_se(vv >= 0);
129 assert_se(ww >= 0);
130
131 r = json_build(&ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("yo", JSON_BUILD_INTEGER(88))));
132 if (r < 0)
133 return r;
134
135 assert_se(varlink_push_fd(link, vv) == 0);
136 assert_se(varlink_push_fd(link, ww) == 1);
137
138 TAKE_FD(vv);
139 TAKE_FD(ww);
140
141 return varlink_reply(link, ret);
142}
143
635d059f
LP
144static int method_done(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
145
146 if (++n_done == 2)
147 sd_event_exit(varlink_get_event(link), EXIT_FAILURE);
148
149 return 0;
150}
151
152static int reply(Varlink *link, JsonVariant *parameters, const char *error_id, VarlinkReplyFlags flags, void *userdata) {
153 JsonVariant *sum;
154
155 sum = json_variant_by_key(parameters, "sum");
156
157 assert_se(json_variant_integer(sum) == 7+22);
158
159 if (++n_done == 2)
160 sd_event_exit(varlink_get_event(link), EXIT_FAILURE);
161
162 return 0;
163}
164
165static int on_connect(VarlinkServer *s, Varlink *link, void *userdata) {
166 uid_t uid = UID_INVALID;
167
f21b863e
YW
168 assert_se(s);
169 assert_se(link);
635d059f
LP
170
171 assert_se(varlink_get_peer_uid(link, &uid) >= 0);
172 assert_se(getuid() == uid);
7947dbe3
LP
173 assert_se(varlink_set_allow_fd_passing_input(link, true) >= 0);
174 assert_se(varlink_set_allow_fd_passing_output(link, true) >= 0);
635d059f
LP
175
176 return 0;
177}
178
179static int overload_reply(Varlink *link, JsonVariant *parameters, const char *error_id, VarlinkReplyFlags flags, void *userdata) {
180
181 /* This method call reply should always be called with a disconnection, since the method call should
182 * be talking to an overloaded server */
183
184 log_debug("Over reply triggered with error: %s", strna(error_id));
185 assert_se(streq(error_id, VARLINK_ERROR_DISCONNECTED));
186 sd_event_exit(varlink_get_event(link), 0);
187
188 return 0;
189}
190
191static void flood_test(const char *address) {
192 _cleanup_(varlink_flush_close_unrefp) Varlink *c = NULL;
193 _cleanup_(sd_event_unrefp) sd_event *e = NULL;
194 _cleanup_free_ Varlink **connections = NULL;
195 size_t k;
196 char x = 'x';
197
198 log_debug("Flooding server...");
199
200 /* Block the main event loop while we flood */
201 assert_se(write(block_write_fd, &x, sizeof(x)) == sizeof(x));
202
203 assert_se(sd_event_default(&e) >= 0);
204
205 /* Flood the server with connections */
206 assert_se(connections = new0(Varlink*, OVERLOAD_CONNECTIONS));
207 for (k = 0; k < OVERLOAD_CONNECTIONS; k++) {
208 _cleanup_free_ char *t = NULL;
209 log_debug("connection %zu", k);
210 assert_se(varlink_connect_address(connections + k, address) >= 0);
211
212 assert_se(asprintf(&t, "flood-%zu", k) >= 0);
213 assert_se(varlink_set_description(connections[k], t) >= 0);
214 assert_se(varlink_attach_event(connections[k], e, k) >= 0);
215 assert_se(varlink_sendb(connections[k], "io.test.Rubbish", JSON_BUILD_OBJECT(JSON_BUILD_PAIR("id", JSON_BUILD_INTEGER(k)))) >= 0);
216 }
217
218 /* Then, create one more, which should fail */
219 log_debug("Creating overload connection...");
220 assert_se(varlink_connect_address(&c, address) >= 0);
221 assert_se(varlink_set_description(c, "overload-client") >= 0);
222 assert_se(varlink_attach_event(c, e, k) >= 0);
223 assert_se(varlink_bind_reply(c, overload_reply) >= 0);
0cdf6b14 224 assert_se(varlink_invokeb(c, "io.test.Overload", JSON_BUILD_OBJECT(JSON_BUILD_PAIR("foo", JSON_BUILD_CONST_STRING("bar")))) >= 0);
635d059f
LP
225
226 /* Unblock it */
227 log_debug("Unblocking server...");
228 block_write_fd = safe_close(block_write_fd);
229
230 /* This loop will terminate as soon as the overload reply callback is called */
231 assert_se(sd_event_loop(e) >= 0);
232
233 /* And close all connections again */
234 for (k = 0; k < OVERLOAD_CONNECTIONS; k++)
235 connections[k] = varlink_unref(connections[k]);
236}
237
238static void *thread(void *arg) {
239 _cleanup_(varlink_flush_close_unrefp) Varlink *c = NULL;
1bd0b9c0
AS
240 _cleanup_(json_variant_unrefp) JsonVariant *i = NULL, *j = NULL;
241 JsonVariant *o = NULL, *k = NULL;
242 const char *error_id;
243 VarlinkReplyFlags flags = 0;
635d059f 244 const char *e;
1bd0b9c0 245 int x = 0;
635d059f
LP
246
247 assert_se(json_build(&i, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("a", JSON_BUILD_INTEGER(88)),
248 JSON_BUILD_PAIR("b", JSON_BUILD_INTEGER(99)))) >= 0);
249
250 assert_se(varlink_connect_address(&c, arg) >= 0);
251 assert_se(varlink_set_description(c, "thread-client") >= 0);
7947dbe3
LP
252 assert_se(varlink_set_allow_fd_passing_input(c, true) >= 0);
253 assert_se(varlink_set_allow_fd_passing_output(c, true) >= 0);
635d059f 254
1bd0b9c0
AS
255 assert_se(varlink_collect(c, "io.test.DoSomethingMore", i, &j, &error_id, &flags) >= 0);
256
257 assert_se(!error_id);
258 assert_se(!flags);
259 assert_se(json_variant_is_array(j) && !json_variant_is_blank_array(j));
260
261 JSON_VARIANT_ARRAY_FOREACH(k, j) {
262 assert_se(json_variant_integer(json_variant_by_key(k, "sum")) == 88 + (99 * x));
263 x++;
264 }
265 assert_se(x == 6);
266
635d059f
LP
267 assert_se(varlink_call(c, "io.test.DoSomething", i, &o, &e, NULL) >= 0);
268 assert_se(json_variant_integer(json_variant_by_key(o, "sum")) == 88 + 99);
269 assert_se(!e);
270
7947dbe3
LP
271 int fd1 = acquire_data_fd("foo", 3, 0);
272 int fd2 = acquire_data_fd("bar", 3, 0);
273 int fd3 = acquire_data_fd("quux", 4, 0);
274
275 assert_se(fd1 >= 0);
276 assert_se(fd2 >= 0);
277 assert_se(fd3 >= 0);
278
279 assert_se(varlink_push_fd(c, fd1) == 0);
280 assert_se(varlink_push_fd(c, fd2) == 1);
281 assert_se(varlink_push_fd(c, fd3) == 2);
282
283 assert_se(varlink_callb(c, "io.test.PassFD", &o, &e, NULL, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("fd", JSON_BUILD_STRING("whoop")))) >= 0);
284
285 int fd4 = varlink_peek_fd(c, 0);
286 int fd5 = varlink_peek_fd(c, 1);
287
288 assert_se(fd4 >= 0);
289 assert_se(fd5 >= 0);
290
291 test_fd(fd4, "miau", 4);
292 test_fd(fd5, "wuff", 4);
293
635d059f
LP
294 assert_se(varlink_callb(c, "io.test.IDontExist", &o, &e, NULL, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("x", JSON_BUILD_REAL(5.5)))) >= 0);
295 assert_se(streq_ptr(json_variant_string(json_variant_by_key(o, "method")), "io.test.IDontExist"));
296 assert_se(streq(e, VARLINK_ERROR_METHOD_NOT_FOUND));
297
298 flood_test(arg);
299
300 assert_se(varlink_send(c, "io.test.Done", NULL) >= 0);
301
302 return NULL;
303}
304
305static int block_fd_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
306 char c;
307
308 assert_se(fd_nonblock(fd, false) >= 0);
309
310 assert_se(read(fd, &c, sizeof(c)) == sizeof(c));
311 /* When a character is written to this pipe we'll block until the pipe is closed. */
312
313 assert_se(read(fd, &c, sizeof(c)) == 0);
314
315 assert_se(fd_nonblock(fd, true) >= 0);
316
317 assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
318
319 return 0;
320}
321
322int main(int argc, char *argv[]) {
323 _cleanup_(sd_event_source_unrefp) sd_event_source *block_event = NULL;
324 _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
325 _cleanup_(varlink_flush_close_unrefp) Varlink *c = NULL;
326 _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL;
327 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
328 _cleanup_(sd_event_unrefp) sd_event *e = NULL;
71136404 329 _cleanup_close_pair_ int block_fds[2] = EBADF_PAIR;
635d059f
LP
330 pthread_t t;
331 const char *sp;
332
333 log_set_max_level(LOG_DEBUG);
334 log_open();
335
336 assert_se(mkdtemp_malloc("/tmp/varlink-test-XXXXXX", &tmpdir) >= 0);
337 sp = strjoina(tmpdir, "/socket");
338
339 assert_se(sd_event_default(&e) >= 0);
340
341 assert_se(pipe2(block_fds, O_NONBLOCK|O_CLOEXEC) >= 0);
342 assert_se(sd_event_add_io(e, &block_event, block_fds[0], EPOLLIN, block_fd_handler, NULL) >= 0);
343 assert_se(sd_event_source_set_priority(block_event, SD_EVENT_PRIORITY_IMPORTANT) >= 0);
344 block_write_fd = TAKE_FD(block_fds[1]);
345
346 assert_se(varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID) >= 0);
347 assert_se(varlink_server_set_description(s, "our-server") >= 0);
348
7947dbe3 349 assert_se(varlink_server_bind_method(s, "io.test.PassFD", method_passfd) >= 0);
635d059f 350 assert_se(varlink_server_bind_method(s, "io.test.DoSomething", method_something) >= 0);
1bd0b9c0 351 assert_se(varlink_server_bind_method(s, "io.test.DoSomethingMore", method_something_more) >= 0);
635d059f
LP
352 assert_se(varlink_server_bind_method(s, "io.test.Done", method_done) >= 0);
353 assert_se(varlink_server_bind_connect(s, on_connect) >= 0);
354 assert_se(varlink_server_listen_address(s, sp, 0600) >= 0);
355 assert_se(varlink_server_attach_event(s, e, 0) >= 0);
356 assert_se(varlink_server_set_connections_max(s, OVERLOAD_CONNECTIONS) >= 0);
357
1bd0b9c0
AS
358 assert_se(json_build(&v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("a", JSON_BUILD_INTEGER(7)),
359 JSON_BUILD_PAIR("b", JSON_BUILD_INTEGER(22)))) >= 0);
360
635d059f
LP
361 assert_se(varlink_connect_address(&c, sp) >= 0);
362 assert_se(varlink_set_description(c, "main-client") >= 0);
363 assert_se(varlink_bind_reply(c, reply) >= 0);
364
635d059f
LP
365 assert_se(varlink_invoke(c, "io.test.DoSomething", v) >= 0);
366
367 assert_se(varlink_attach_event(c, e, 0) >= 0);
368
369 assert_se(pthread_create(&t, NULL, thread, (void*) sp) == 0);
370
371 assert_se(sd_event_loop(e) >= 0);
372
373 assert_se(pthread_join(t, NULL) == 0);
374
375 return 0;
376}