1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 #include "data-fd-util.h"
14 #include "tmpfile-util.h"
15 #include "user-util.h"
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
24 static int n_done
= 0;
25 static int block_write_fd
= -EBADF
;
27 static int method_something(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
28 _cleanup_(json_variant_unrefp
) JsonVariant
*ret
= NULL
;
33 a
= json_variant_by_key(parameters
, "a");
35 return varlink_error(link
, "io.test.BadParameters", NULL
);
37 x
= json_variant_integer(a
);
39 b
= json_variant_by_key(parameters
, "b");
41 return varlink_error(link
, "io.test.BadParameters", NULL
);
43 y
= json_variant_integer(b
);
45 r
= json_build(&ret
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("sum", JSON_BUILD_INTEGER(x
+ y
))));
49 return varlink_reply(link
, ret
);
52 static int method_something_more(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
53 _cleanup_(json_variant_unrefp
) JsonVariant
*ret
= NULL
;
61 static const JsonDispatch dispatch_table
[] = {
62 { "a", JSON_VARIANT_INTEGER
, json_dispatch_int
, offsetof(struct Something
, x
), JSON_MANDATORY
},
63 { "b", JSON_VARIANT_INTEGER
, json_dispatch_int
, offsetof(struct Something
, y
), JSON_MANDATORY
},
66 struct Something s
= {};
68 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &s
);
70 for (int i
= 0; i
< 5; i
++) {
71 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
73 r
= json_build(&w
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("sum", JSON_BUILD_INTEGER(s
.x
+ (s
.y
* i
)))));
77 r
= varlink_notify(link
, w
);
82 r
= json_build(&ret
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("sum", JSON_BUILD_INTEGER(s
.x
+ (s
.y
* 5)))));
86 return varlink_reply(link
, ret
);
89 static void test_fd(int fd
, const void *buf
, size_t n
) {
93 m
= read(fd
, rbuf
, n
+ 1);
95 assert_se(memcmp_nn(buf
, n
, rbuf
, m
) == 0);
98 static int method_passfd(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
99 _cleanup_(json_variant_unrefp
) JsonVariant
*ret
= NULL
;
103 a
= json_variant_by_key(parameters
, "fd");
105 return varlink_error(link
, "io.test.BadParameters", NULL
);
107 assert_se(streq_ptr(json_variant_string(a
), "whoop"));
109 int xx
= varlink_peek_fd(link
, 0),
110 yy
= varlink_peek_fd(link
, 1),
111 zz
= varlink_peek_fd(link
, 2);
113 log_info("%i %i %i", xx
, yy
, zz
);
119 test_fd(xx
, "foo", 3);
120 test_fd(yy
, "bar", 3);
121 test_fd(zz
, "quux", 4);
123 _cleanup_close_
int vv
= acquire_data_fd("miau", 4, 0);
124 _cleanup_close_
int ww
= acquire_data_fd("wuff", 4, 0);
129 r
= json_build(&ret
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("yo", JSON_BUILD_INTEGER(88))));
133 assert_se(varlink_push_fd(link
, vv
) == 0);
134 assert_se(varlink_push_fd(link
, ww
) == 1);
139 return varlink_reply(link
, ret
);
142 static int method_done(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
145 sd_event_exit(varlink_get_event(link
), EXIT_FAILURE
);
150 static int reply(Varlink
*link
, JsonVariant
*parameters
, const char *error_id
, VarlinkReplyFlags flags
, void *userdata
) {
153 sum
= json_variant_by_key(parameters
, "sum");
155 assert_se(json_variant_integer(sum
) == 7+22);
158 sd_event_exit(varlink_get_event(link
), EXIT_FAILURE
);
163 static int on_connect(VarlinkServer
*s
, Varlink
*link
, void *userdata
) {
164 uid_t uid
= UID_INVALID
;
169 assert_se(varlink_get_peer_uid(link
, &uid
) >= 0);
170 assert_se(getuid() == uid
);
171 assert_se(varlink_set_allow_fd_passing_input(link
, true) >= 0);
172 assert_se(varlink_set_allow_fd_passing_output(link
, true) >= 0);
177 static int overload_reply(Varlink
*link
, JsonVariant
*parameters
, const char *error_id
, VarlinkReplyFlags flags
, void *userdata
) {
179 /* This method call reply should always be called with a disconnection, since the method call should
180 * be talking to an overloaded server */
182 log_debug("Over reply triggered with error: %s", strna(error_id
));
183 assert_se(streq(error_id
, VARLINK_ERROR_DISCONNECTED
));
184 sd_event_exit(varlink_get_event(link
), 0);
189 static void flood_test(const char *address
) {
190 _cleanup_(varlink_flush_close_unrefp
) Varlink
*c
= NULL
;
191 _cleanup_(sd_event_unrefp
) sd_event
*e
= NULL
;
192 _cleanup_free_ Varlink
**connections
= NULL
;
196 log_debug("Flooding server...");
198 /* Block the main event loop while we flood */
199 assert_se(write(block_write_fd
, &x
, sizeof(x
)) == sizeof(x
));
201 assert_se(sd_event_default(&e
) >= 0);
203 /* Flood the server with connections */
204 assert_se(connections
= new0(Varlink
*, OVERLOAD_CONNECTIONS
));
205 for (k
= 0; k
< OVERLOAD_CONNECTIONS
; k
++) {
206 _cleanup_free_
char *t
= NULL
;
207 log_debug("connection %zu", k
);
208 assert_se(varlink_connect_address(connections
+ k
, address
) >= 0);
210 assert_se(asprintf(&t
, "flood-%zu", k
) >= 0);
211 assert_se(varlink_set_description(connections
[k
], t
) >= 0);
212 assert_se(varlink_attach_event(connections
[k
], e
, k
) >= 0);
213 assert_se(varlink_sendb(connections
[k
], "io.test.Rubbish", JSON_BUILD_OBJECT(JSON_BUILD_PAIR("id", JSON_BUILD_INTEGER(k
)))) >= 0);
216 /* Then, create one more, which should fail */
217 log_debug("Creating overload connection...");
218 assert_se(varlink_connect_address(&c
, address
) >= 0);
219 assert_se(varlink_set_description(c
, "overload-client") >= 0);
220 assert_se(varlink_attach_event(c
, e
, k
) >= 0);
221 assert_se(varlink_bind_reply(c
, overload_reply
) >= 0);
222 assert_se(varlink_invokeb(c
, "io.test.Overload", JSON_BUILD_OBJECT(JSON_BUILD_PAIR("foo", JSON_BUILD_CONST_STRING("bar")))) >= 0);
225 log_debug("Unblocking server...");
226 block_write_fd
= safe_close(block_write_fd
);
228 /* This loop will terminate as soon as the overload reply callback is called */
229 assert_se(sd_event_loop(e
) >= 0);
231 /* And close all connections again */
232 for (k
= 0; k
< OVERLOAD_CONNECTIONS
; k
++)
233 connections
[k
] = varlink_unref(connections
[k
]);
236 static void *thread(void *arg
) {
237 _cleanup_(varlink_flush_close_unrefp
) Varlink
*c
= NULL
;
238 _cleanup_(json_variant_unrefp
) JsonVariant
*i
= NULL
, *j
= NULL
;
239 JsonVariant
*o
= NULL
, *k
= NULL
;
240 const char *error_id
;
241 VarlinkReplyFlags flags
= 0;
245 assert_se(json_build(&i
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("a", JSON_BUILD_INTEGER(88)),
246 JSON_BUILD_PAIR("b", JSON_BUILD_INTEGER(99)))) >= 0);
248 assert_se(varlink_connect_address(&c
, arg
) >= 0);
249 assert_se(varlink_set_description(c
, "thread-client") >= 0);
250 assert_se(varlink_set_allow_fd_passing_input(c
, true) >= 0);
251 assert_se(varlink_set_allow_fd_passing_output(c
, true) >= 0);
253 assert_se(varlink_collect(c
, "io.test.DoSomethingMore", i
, &j
, &error_id
, &flags
) >= 0);
255 assert_se(!error_id
);
257 assert_se(json_variant_is_array(j
) && !json_variant_is_blank_array(j
));
259 JSON_VARIANT_ARRAY_FOREACH(k
, j
) {
260 assert_se(json_variant_integer(json_variant_by_key(k
, "sum")) == 88 + (99 * x
));
265 assert_se(varlink_call(c
, "io.test.DoSomething", i
, &o
, &e
, NULL
) >= 0);
266 assert_se(json_variant_integer(json_variant_by_key(o
, "sum")) == 88 + 99);
269 int fd1
= acquire_data_fd("foo", 3, 0);
270 int fd2
= acquire_data_fd("bar", 3, 0);
271 int fd3
= acquire_data_fd("quux", 4, 0);
277 assert_se(varlink_push_fd(c
, fd1
) == 0);
278 assert_se(varlink_push_fd(c
, fd2
) == 1);
279 assert_se(varlink_push_fd(c
, fd3
) == 2);
281 assert_se(varlink_callb(c
, "io.test.PassFD", &o
, &e
, NULL
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("fd", JSON_BUILD_STRING("whoop")))) >= 0);
283 int fd4
= varlink_peek_fd(c
, 0);
284 int fd5
= varlink_peek_fd(c
, 1);
289 test_fd(fd4
, "miau", 4);
290 test_fd(fd5
, "wuff", 4);
292 assert_se(varlink_callb(c
, "io.test.IDontExist", &o
, &e
, NULL
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("x", JSON_BUILD_REAL(5.5)))) >= 0);
293 assert_se(streq_ptr(json_variant_string(json_variant_by_key(o
, "method")), "io.test.IDontExist"));
294 assert_se(streq(e
, VARLINK_ERROR_METHOD_NOT_FOUND
));
298 assert_se(varlink_send(c
, "io.test.Done", NULL
) >= 0);
303 static int block_fd_handler(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
306 assert_se(fd_nonblock(fd
, false) >= 0);
308 assert_se(read(fd
, &c
, sizeof(c
)) == sizeof(c
));
309 /* When a character is written to this pipe we'll block until the pipe is closed. */
311 assert_se(read(fd
, &c
, sizeof(c
)) == 0);
313 assert_se(fd_nonblock(fd
, true) >= 0);
315 assert_se(sd_event_source_set_enabled(s
, SD_EVENT_OFF
) >= 0);
320 int main(int argc
, char *argv
[]) {
321 _cleanup_(sd_event_source_unrefp
) sd_event_source
*block_event
= NULL
;
322 _cleanup_(varlink_server_unrefp
) VarlinkServer
*s
= NULL
;
323 _cleanup_(varlink_flush_close_unrefp
) Varlink
*c
= NULL
;
324 _cleanup_(rm_rf_physical_and_freep
) char *tmpdir
= NULL
;
325 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
326 _cleanup_(sd_event_unrefp
) sd_event
*e
= NULL
;
327 _cleanup_close_pair_
int block_fds
[2] = PIPE_EBADF
;
331 log_set_max_level(LOG_DEBUG
);
334 assert_se(mkdtemp_malloc("/tmp/varlink-test-XXXXXX", &tmpdir
) >= 0);
335 sp
= strjoina(tmpdir
, "/socket");
337 assert_se(sd_event_default(&e
) >= 0);
339 assert_se(pipe2(block_fds
, O_NONBLOCK
|O_CLOEXEC
) >= 0);
340 assert_se(sd_event_add_io(e
, &block_event
, block_fds
[0], EPOLLIN
, block_fd_handler
, NULL
) >= 0);
341 assert_se(sd_event_source_set_priority(block_event
, SD_EVENT_PRIORITY_IMPORTANT
) >= 0);
342 block_write_fd
= TAKE_FD(block_fds
[1]);
344 assert_se(varlink_server_new(&s
, VARLINK_SERVER_ACCOUNT_UID
) >= 0);
345 assert_se(varlink_server_set_description(s
, "our-server") >= 0);
347 assert_se(varlink_server_bind_method(s
, "io.test.PassFD", method_passfd
) >= 0);
348 assert_se(varlink_server_bind_method(s
, "io.test.DoSomething", method_something
) >= 0);
349 assert_se(varlink_server_bind_method(s
, "io.test.DoSomethingMore", method_something_more
) >= 0);
350 assert_se(varlink_server_bind_method(s
, "io.test.Done", method_done
) >= 0);
351 assert_se(varlink_server_bind_connect(s
, on_connect
) >= 0);
352 assert_se(varlink_server_listen_address(s
, sp
, 0600) >= 0);
353 assert_se(varlink_server_attach_event(s
, e
, 0) >= 0);
354 assert_se(varlink_server_set_connections_max(s
, OVERLOAD_CONNECTIONS
) >= 0);
356 assert_se(json_build(&v
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("a", JSON_BUILD_INTEGER(7)),
357 JSON_BUILD_PAIR("b", JSON_BUILD_INTEGER(22)))) >= 0);
359 assert_se(varlink_connect_address(&c
, sp
) >= 0);
360 assert_se(varlink_set_description(c
, "main-client") >= 0);
361 assert_se(varlink_bind_reply(c
, reply
) >= 0);
363 assert_se(varlink_invoke(c
, "io.test.DoSomething", v
) >= 0);
365 assert_se(varlink_attach_event(c
, e
, 0) >= 0);
367 assert_se(pthread_create(&t
, NULL
, thread
, (void*) sp
) == 0);
369 assert_se(sd_event_loop(e
) >= 0);
371 assert_se(pthread_join(t
, NULL
) == 0);