1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 #include "data-fd-util.h"
15 #include "tmpfile-util.h"
16 #include "user-util.h"
19 /* Let's pick some high value, that is higher than the largest listen() backlog, but leaves enough room below
20 the typical RLIMIT_NOFILE value of 1024 so that we can process both sides of each socket in our
21 process. Or in other words: "OVERLOAD_CONNECTIONS * 2 + x < 1024" should hold, for some small x that
22 should cover any auxiliary fds, the listener server fds, stdin/stdout/stderr and whatever else. */
23 #define OVERLOAD_CONNECTIONS 333
25 static int n_done
= 0;
26 static int block_write_fd
= -EBADF
;
28 static int method_something(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
29 _cleanup_(json_variant_unrefp
) JsonVariant
*ret
= NULL
;
34 a
= json_variant_by_key(parameters
, "a");
36 return varlink_error(link
, "io.test.BadParameters", NULL
);
38 x
= json_variant_integer(a
);
40 b
= json_variant_by_key(parameters
, "b");
42 return varlink_error(link
, "io.test.BadParameters", NULL
);
44 y
= json_variant_integer(b
);
46 r
= json_build(&ret
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("sum", JSON_BUILD_INTEGER(x
+ y
))));
50 return varlink_reply(link
, ret
);
53 static int method_something_more(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
54 _cleanup_(json_variant_unrefp
) JsonVariant
*ret
= NULL
;
62 static const JsonDispatch dispatch_table
[] = {
63 { "a", JSON_VARIANT_INTEGER
, json_dispatch_int
, offsetof(struct Something
, x
), JSON_MANDATORY
},
64 { "b", JSON_VARIANT_INTEGER
, json_dispatch_int
, offsetof(struct Something
, y
), JSON_MANDATORY
},
67 struct Something s
= {};
69 r
= varlink_dispatch(link
, parameters
, dispatch_table
, &s
);
73 for (int i
= 0; i
< 5; i
++) {
74 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
76 r
= json_build(&w
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("sum", JSON_BUILD_INTEGER(s
.x
+ (s
.y
* i
)))));
80 r
= varlink_notify(link
, w
);
85 r
= json_build(&ret
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("sum", JSON_BUILD_INTEGER(s
.x
+ (s
.y
* 5)))));
89 return varlink_reply(link
, ret
);
92 static void test_fd(int fd
, const void *buf
, size_t n
) {
96 m
= read(fd
, rbuf
, n
+ 1);
98 assert_se(memcmp_nn(buf
, n
, rbuf
, m
) == 0);
101 static int method_passfd(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
102 _cleanup_(json_variant_unrefp
) JsonVariant
*ret
= NULL
;
106 a
= json_variant_by_key(parameters
, "fd");
108 return varlink_error(link
, "io.test.BadParameters", NULL
);
110 assert_se(streq_ptr(json_variant_string(a
), "whoop"));
112 int xx
= varlink_peek_fd(link
, 0),
113 yy
= varlink_peek_fd(link
, 1),
114 zz
= varlink_peek_fd(link
, 2);
116 log_info("%i %i %i", xx
, yy
, zz
);
122 test_fd(xx
, "foo", 3);
123 test_fd(yy
, "bar", 3);
124 test_fd(zz
, "quux", 4);
126 _cleanup_close_
int vv
= acquire_data_fd("miau", 4, 0);
127 _cleanup_close_
int ww
= acquire_data_fd("wuff", 4, 0);
132 r
= json_build(&ret
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("yo", JSON_BUILD_INTEGER(88))));
136 assert_se(varlink_push_fd(link
, vv
) == 0);
137 assert_se(varlink_push_fd(link
, ww
) == 1);
142 return varlink_reply(link
, ret
);
145 static int method_done(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
148 sd_event_exit(varlink_get_event(link
), EXIT_FAILURE
);
153 static int reply(Varlink
*link
, JsonVariant
*parameters
, const char *error_id
, VarlinkReplyFlags flags
, void *userdata
) {
156 sum
= json_variant_by_key(parameters
, "sum");
158 assert_se(json_variant_integer(sum
) == 7+22);
161 sd_event_exit(varlink_get_event(link
), EXIT_FAILURE
);
166 static int on_connect(VarlinkServer
*s
, Varlink
*link
, void *userdata
) {
167 uid_t uid
= UID_INVALID
;
172 assert_se(varlink_get_peer_uid(link
, &uid
) >= 0);
173 assert_se(getuid() == uid
);
174 assert_se(varlink_set_allow_fd_passing_input(link
, true) >= 0);
175 assert_se(varlink_set_allow_fd_passing_output(link
, true) >= 0);
180 static int overload_reply(Varlink
*link
, JsonVariant
*parameters
, const char *error_id
, VarlinkReplyFlags flags
, void *userdata
) {
182 /* This method call reply should always be called with a disconnection, since the method call should
183 * be talking to an overloaded server */
185 log_debug("Over reply triggered with error: %s", strna(error_id
));
186 assert_se(streq(error_id
, VARLINK_ERROR_DISCONNECTED
));
187 sd_event_exit(varlink_get_event(link
), 0);
192 static void flood_test(const char *address
) {
193 _cleanup_(varlink_flush_close_unrefp
) Varlink
*c
= NULL
;
194 _cleanup_(sd_event_unrefp
) sd_event
*e
= NULL
;
195 _cleanup_free_ Varlink
**connections
= NULL
;
199 log_debug("Flooding server...");
201 /* Block the main event loop while we flood */
202 assert_se(write(block_write_fd
, &x
, sizeof(x
)) == sizeof(x
));
204 assert_se(sd_event_default(&e
) >= 0);
206 /* Flood the server with connections */
207 assert_se(connections
= new0(Varlink
*, OVERLOAD_CONNECTIONS
));
208 for (k
= 0; k
< OVERLOAD_CONNECTIONS
; k
++) {
209 _cleanup_free_
char *t
= NULL
;
210 log_debug("connection %zu", k
);
211 assert_se(varlink_connect_address(connections
+ k
, address
) >= 0);
213 assert_se(asprintf(&t
, "flood-%zu", k
) >= 0);
214 assert_se(varlink_set_description(connections
[k
], t
) >= 0);
215 assert_se(varlink_attach_event(connections
[k
], e
, k
) >= 0);
216 assert_se(varlink_sendb(connections
[k
], "io.test.Rubbish", JSON_BUILD_OBJECT(JSON_BUILD_PAIR("id", JSON_BUILD_INTEGER(k
)))) >= 0);
219 /* Then, create one more, which should fail */
220 log_debug("Creating overload connection...");
221 assert_se(varlink_connect_address(&c
, address
) >= 0);
222 assert_se(varlink_set_description(c
, "overload-client") >= 0);
223 assert_se(varlink_attach_event(c
, e
, k
) >= 0);
224 assert_se(varlink_bind_reply(c
, overload_reply
) >= 0);
225 assert_se(varlink_invokeb(c
, "io.test.Overload", JSON_BUILD_OBJECT(JSON_BUILD_PAIR("foo", JSON_BUILD_CONST_STRING("bar")))) >= 0);
228 log_debug("Unblocking server...");
229 block_write_fd
= safe_close(block_write_fd
);
231 /* This loop will terminate as soon as the overload reply callback is called */
232 assert_se(sd_event_loop(e
) >= 0);
234 /* And close all connections again */
235 for (k
= 0; k
< OVERLOAD_CONNECTIONS
; k
++)
236 connections
[k
] = varlink_unref(connections
[k
]);
239 static void *thread(void *arg
) {
240 _cleanup_(varlink_flush_close_unrefp
) Varlink
*c
= NULL
;
241 _cleanup_(json_variant_unrefp
) JsonVariant
*i
= NULL
;
242 JsonVariant
*o
= NULL
, *k
= NULL
, *j
= NULL
;
243 const char *error_id
;
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);
250 assert_se(varlink_connect_address(&c
, arg
) >= 0);
251 assert_se(varlink_set_description(c
, "thread-client") >= 0);
252 assert_se(varlink_set_allow_fd_passing_input(c
, true) >= 0);
253 assert_se(varlink_set_allow_fd_passing_output(c
, true) >= 0);
255 assert_se(varlink_collect(c
, "io.test.DoSomethingMore", i
, &j
, &error_id
) >= 0);
257 assert_se(!error_id
);
258 assert_se(json_variant_is_array(j
) && !json_variant_is_blank_array(j
));
260 JSON_VARIANT_ARRAY_FOREACH(k
, j
) {
261 assert_se(json_variant_integer(json_variant_by_key(k
, "sum")) == 88 + (99 * x
));
266 assert_se(varlink_call(c
, "io.test.DoSomething", i
, &o
, &e
) >= 0);
267 assert_se(json_variant_integer(json_variant_by_key(o
, "sum")) == 88 + 99);
270 int fd1
= acquire_data_fd("foo", 3, 0);
271 int fd2
= acquire_data_fd("bar", 3, 0);
272 int fd3
= acquire_data_fd("quux", 4, 0);
278 assert_se(varlink_push_fd(c
, fd1
) == 0);
279 assert_se(varlink_push_fd(c
, fd2
) == 1);
280 assert_se(varlink_push_fd(c
, fd3
) == 2);
282 assert_se(varlink_callb(c
, "io.test.PassFD", &o
, &e
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("fd", JSON_BUILD_STRING("whoop")))) >= 0);
284 int fd4
= varlink_peek_fd(c
, 0);
285 int fd5
= varlink_peek_fd(c
, 1);
290 test_fd(fd4
, "miau", 4);
291 test_fd(fd5
, "wuff", 4);
293 assert_se(varlink_callb(c
, "io.test.IDontExist", &o
, &e
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("x", JSON_BUILD_REAL(5.5)))) >= 0);
294 assert_se(streq_ptr(json_variant_string(json_variant_by_key(o
, "method")), "io.test.IDontExist"));
295 assert_se(streq(e
, VARLINK_ERROR_METHOD_NOT_FOUND
));
299 assert_se(varlink_send(c
, "io.test.Done", NULL
) >= 0);
304 static int block_fd_handler(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
307 assert_se(fd_nonblock(fd
, false) >= 0);
309 assert_se(read(fd
, &c
, sizeof(c
)) == sizeof(c
));
310 /* When a character is written to this pipe we'll block until the pipe is closed. */
312 assert_se(read(fd
, &c
, sizeof(c
)) == 0);
314 assert_se(fd_nonblock(fd
, true) >= 0);
316 assert_se(sd_event_source_set_enabled(s
, SD_EVENT_OFF
) >= 0);
321 int main(int argc
, char *argv
[]) {
322 _cleanup_(sd_event_source_unrefp
) sd_event_source
*block_event
= NULL
;
323 _cleanup_(varlink_server_unrefp
) VarlinkServer
*s
= NULL
;
324 _cleanup_(varlink_flush_close_unrefp
) Varlink
*c
= NULL
;
325 _cleanup_(rm_rf_physical_and_freep
) char *tmpdir
= NULL
;
326 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
327 _cleanup_(sd_event_unrefp
) sd_event
*e
= NULL
;
328 _cleanup_close_pair_
int block_fds
[2] = EBADF_PAIR
;
332 test_setup_logging(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);