]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/socket-proxy/socket-proxyd.c
man/systemd-sysext: list ephemeral/ephemeral-import in the list of options
[thirdparty/systemd.git] / src / socket-proxy / socket-proxyd.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
912b54ad 2
3f6fd1ba 3#include <fcntl.h>
912b54ad 4#include <getopt.h>
3f6fd1ba 5#include <netdb.h>
912b54ad 6#include <stdio.h>
912b54ad
DS
7#include <unistd.h>
8
912b54ad
DS
9#include "sd-daemon.h"
10#include "sd-event.h"
fb69d709 11#include "sd-resolve.h"
3f6fd1ba 12
b5efdb8a 13#include "alloc-util.h"
d6b4d1c7 14#include "build.h"
37fe75a2 15#include "daemon-util.h"
f60a028a 16#include "errno-util.h"
1cf40697 17#include "event-util.h"
3ffd4af2 18#include "fd-util.h"
96c374d0 19#include "log.h"
a160567e 20#include "main-func.h"
37ec0fdd 21#include "parse-util.h"
294bf0c3 22#include "pretty-print.h"
f2935c77 23#include "resolve-private.h"
3f6fd1ba 24#include "set.h"
912b54ad 25#include "socket-util.h"
07630cea 26#include "string-util.h"
8857aa74 27#include "time-util.h"
8569a776
LP
28
29#define BUFFER_SIZE (256 * 1024)
912b54ad 30
f60a028a 31static unsigned arg_connections_max = 256;
fb69d709 32static const char *arg_remote_host = NULL;
9e12d5bf 33static usec_t arg_exit_idle_time = USEC_INFINITY;
fb69d709 34
8569a776 35typedef struct Context {
fb69d709
LP
36 sd_event *event;
37 sd_resolve *resolve;
9e12d5bf 38 sd_event_source *idle_time;
fb69d709 39
8569a776
LP
40 Set *listen;
41 Set *connections;
42} Context;
912b54ad 43
8569a776 44typedef struct Connection {
e633ea1c
LP
45 Context *context;
46
8569a776
LP
47 int server_fd, client_fd;
48 int server_to_client_buffer[2]; /* a pipe */
49 int client_to_server_buffer[2]; /* a pipe */
912b54ad 50
8569a776
LP
51 size_t server_to_client_buffer_full, client_to_server_buffer_full;
52 size_t server_to_client_buffer_size, client_to_server_buffer_size;
53
54 sd_event_source *server_event_source, *client_event_source;
912b54ad 55
fb69d709
LP
56 sd_resolve_query *resolve_query;
57} Connection;
912b54ad 58
234b86a4
YW
59static Connection* connection_free(Connection *c) {
60 if (!c)
61 return NULL;
32d3c809 62
e633ea1c
LP
63 if (c->context)
64 set_remove(c->context->connections, c);
65
8569a776
LP
66 sd_event_source_unref(c->server_event_source);
67 sd_event_source_unref(c->client_event_source);
32d3c809 68
03e334a1
LP
69 safe_close(c->server_fd);
70 safe_close(c->client_fd);
32d3c809 71
3d94f76c
LP
72 safe_close_pair(c->server_to_client_buffer);
73 safe_close_pair(c->client_to_server_buffer);
32d3c809 74
fb69d709
LP
75 sd_resolve_query_unref(c->resolve_query);
76
234b86a4 77 return mfree(c);
8569a776 78}
32d3c809 79
f4a717aa
YW
80DEFINE_TRIVIAL_CLEANUP_FUNC(Connection*, connection_free);
81
82DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
83 connection_hash_ops,
84 void, trivial_hash_func, trivial_compare_func,
85 Connection, connection_free);
86
87static void context_done(Context *context) {
88 assert(context);
89
37edefb3 90 set_free(context->listen);
f4a717aa
YW
91 set_free(context->connections);
92
93 sd_event_unref(context->event);
94 sd_resolve_unref(context->resolve);
95 sd_event_source_unref(context->idle_time);
96}
97
9e12d5bf
EA
98static int idle_time_cb(sd_event_source *s, uint64_t usec, void *userdata) {
99 Context *c = userdata;
100 int r;
101
102 if (!set_isempty(c->connections)) {
103 log_warning("Idle timer fired even though there are connections, ignoring");
104 return 0;
105 }
106
107 r = sd_event_exit(c->event, 0);
108 if (r < 0) {
109 log_warning_errno(r, "Error while stopping event loop, ignoring: %m");
110 return 0;
111 }
112 return 0;
113}
114
10e41cdd 115static void context_reset_timer(Context *context) {
39cf0351 116 int r;
9e12d5bf 117
10e41cdd 118 assert(context);
9e12d5bf
EA
119
120 if (arg_exit_idle_time < USEC_INFINITY && set_isempty(context->connections)) {
10e41cdd
YW
121 r = event_reset_time_relative(
122 context->event, &context->idle_time, CLOCK_MONOTONIC,
123 arg_exit_idle_time, 0, idle_time_cb, context,
72c38925 124 SD_EVENT_PRIORITY_NORMAL, "idle-timer", /* force_reset = */ true);
10e41cdd
YW
125 if (r < 0)
126 log_warning_errno(r, "Failed to reset idle timer, ignoring: %m");
9e12d5bf 127 }
10e41cdd 128}
9e12d5bf 129
10e41cdd 130static void connection_release(Connection *c) {
be1f90d9 131 Context *context = ASSERT_PTR(ASSERT_PTR(c)->context);
10e41cdd
YW
132
133 connection_free(c);
be1f90d9 134 context_reset_timer(context);
9e12d5bf
EA
135}
136
3042bbeb 137static int connection_create_pipes(Connection *c, int buffer[static 2], size_t *sz) {
8569a776 138 int r;
912b54ad 139
8569a776
LP
140 assert(c);
141 assert(buffer);
142 assert(sz);
912b54ad 143
8569a776
LP
144 if (buffer[0] >= 0)
145 return 0;
912b54ad 146
8569a776 147 r = pipe2(buffer, O_CLOEXEC|O_NONBLOCK);
4a62c710
MS
148 if (r < 0)
149 return log_error_errno(errno, "Failed to allocate pipe buffer: %m");
912b54ad 150
25dbe4f5 151 (void) fcntl(buffer[0], F_SETPIPE_SZ, BUFFER_SIZE);
8569a776
LP
152
153 r = fcntl(buffer[0], F_GETPIPE_SZ);
4a62c710
MS
154 if (r < 0)
155 return log_error_errno(errno, "Failed to get pipe buffer size: %m");
912b54ad 156
8569a776
LP
157 assert(r > 0);
158 *sz = r;
159
912b54ad
DS
160 return 0;
161}
162
8569a776
LP
163static int connection_shovel(
164 Connection *c,
165 int *from, int buffer[2], int *to,
166 size_t *full, size_t *sz,
167 sd_event_source **from_source, sd_event_source **to_source) {
168
169 bool shoveled;
170
171 assert(c);
172 assert(from);
173 assert(buffer);
174 assert(buffer[0] >= 0);
175 assert(buffer[1] >= 0);
176 assert(to);
177 assert(full);
178 assert(sz);
179 assert(from_source);
180 assert(to_source);
181
182 do {
183 ssize_t z;
184
185 shoveled = false;
186
187 if (*full < *sz && *from >= 0 && *to >= 0) {
188 z = splice(*from, NULL, buffer[1], NULL, *sz - *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
189 if (z > 0) {
190 *full += z;
191 shoveled = true;
f60a028a 192 } else if (z == 0 || ERRNO_IS_DISCONNECT(errno)) {
8569a776 193 *from_source = sd_event_source_unref(*from_source);
03e334a1 194 *from = safe_close(*from);
8add30a0 195 } else if (!ERRNO_IS_TRANSIENT(errno))
4a62c710 196 return log_error_errno(errno, "Failed to splice: %m");
912b54ad
DS
197 }
198
8569a776
LP
199 if (*full > 0 && *to >= 0) {
200 z = splice(buffer[0], NULL, *to, NULL, *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
201 if (z > 0) {
202 *full -= z;
203 shoveled = true;
f60a028a 204 } else if (z == 0 || ERRNO_IS_DISCONNECT(errno)) {
8569a776 205 *to_source = sd_event_source_unref(*to_source);
03e334a1 206 *to = safe_close(*to);
8add30a0 207 } else if (!ERRNO_IS_TRANSIENT(errno))
4a62c710 208 return log_error_errno(errno, "Failed to splice: %m");
8569a776
LP
209 }
210 } while (shoveled);
912b54ad 211
8569a776 212 return 0;
912b54ad
DS
213}
214
fb69d709 215static int connection_enable_event_sources(Connection *c);
8569a776
LP
216
217static int traffic_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
99534007 218 Connection *c = ASSERT_PTR(userdata);
912b54ad
DS
219 int r;
220
8569a776
LP
221 assert(s);
222 assert(fd >= 0);
912b54ad 223
8569a776
LP
224 r = connection_shovel(c,
225 &c->server_fd, c->server_to_client_buffer, &c->client_fd,
226 &c->server_to_client_buffer_full, &c->server_to_client_buffer_size,
227 &c->server_event_source, &c->client_event_source);
228 if (r < 0)
229 goto quit;
912b54ad 230
8569a776
LP
231 r = connection_shovel(c,
232 &c->client_fd, c->client_to_server_buffer, &c->server_fd,
233 &c->client_to_server_buffer_full, &c->client_to_server_buffer_size,
234 &c->client_event_source, &c->server_event_source);
235 if (r < 0)
236 goto quit;
912b54ad 237
8569a776 238 /* EOF on both sides? */
da850694 239 if (c->server_fd < 0 && c->client_fd < 0)
8569a776 240 goto quit;
912b54ad 241
8569a776 242 /* Server closed, and all data written to client? */
da850694 243 if (c->server_fd < 0 && c->server_to_client_buffer_full <= 0)
8569a776 244 goto quit;
912b54ad 245
8569a776 246 /* Client closed, and all data written to server? */
da850694 247 if (c->client_fd < 0 && c->client_to_server_buffer_full <= 0)
8569a776 248 goto quit;
912b54ad 249
fb69d709 250 r = connection_enable_event_sources(c);
8569a776
LP
251 if (r < 0)
252 goto quit;
912b54ad 253
8569a776 254 return 1;
912b54ad 255
8569a776 256quit:
9e12d5bf 257 connection_release(c);
8569a776 258 return 0; /* ignore errors, continue serving */
912b54ad
DS
259}
260
fb69d709 261static int connection_enable_event_sources(Connection *c) {
8569a776
LP
262 uint32_t a = 0, b = 0;
263 int r;
912b54ad 264
8569a776 265 assert(c);
912b54ad 266
8569a776
LP
267 if (c->server_to_client_buffer_full > 0)
268 b |= EPOLLOUT;
269 if (c->server_to_client_buffer_full < c->server_to_client_buffer_size)
270 a |= EPOLLIN;
912b54ad 271
8569a776
LP
272 if (c->client_to_server_buffer_full > 0)
273 a |= EPOLLOUT;
274 if (c->client_to_server_buffer_full < c->client_to_server_buffer_size)
275 b |= EPOLLIN;
912b54ad 276
8569a776
LP
277 if (c->server_event_source)
278 r = sd_event_source_set_io_events(c->server_event_source, a);
279 else if (c->server_fd >= 0)
fb69d709 280 r = sd_event_add_io(c->context->event, &c->server_event_source, c->server_fd, a, traffic_cb, c);
8569a776
LP
281 else
282 r = 0;
283
f647962d
MS
284 if (r < 0)
285 return log_error_errno(r, "Failed to set up server event source: %m");
912b54ad 286
8569a776
LP
287 if (c->client_event_source)
288 r = sd_event_source_set_io_events(c->client_event_source, b);
289 else if (c->client_fd >= 0)
fb69d709 290 r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, b, traffic_cb, c);
8569a776
LP
291 else
292 r = 0;
912b54ad 293
f647962d
MS
294 if (r < 0)
295 return log_error_errno(r, "Failed to set up client event source: %m");
912b54ad 296
8569a776 297 return 0;
912b54ad
DS
298}
299
fb69d709
LP
300static int connection_complete(Connection *c) {
301 int r;
302
303 assert(c);
304
305 r = connection_create_pipes(c, c->server_to_client_buffer, &c->server_to_client_buffer_size);
306 if (r < 0)
41c817e7 307 return r;
fb69d709
LP
308
309 r = connection_create_pipes(c, c->client_to_server_buffer, &c->client_to_server_buffer_size);
310 if (r < 0)
41c817e7 311 return r;
fb69d709
LP
312
313 r = connection_enable_event_sources(c);
314 if (r < 0)
41c817e7 315 return r;
fb69d709
LP
316
317 return 0;
fb69d709
LP
318}
319
8569a776 320static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
99534007 321 Connection *c = ASSERT_PTR(userdata);
8569a776 322 socklen_t solen;
41c817e7 323 int error;
8569a776
LP
324
325 assert(s);
326 assert(fd >= 0);
8569a776
LP
327
328 solen = sizeof(error);
41c817e7 329 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &solen) < 0) {
56f64d95 330 log_error_errno(errno, "Failed to issue SO_ERROR: %m");
40976028
DS
331 goto fail;
332 }
912b54ad 333
8569a776 334 if (error != 0) {
279d3c9c 335 log_error_errno(error, "Failed to connect to remote host: %m");
912b54ad
DS
336 goto fail;
337 }
338
8569a776
LP
339 c->client_event_source = sd_event_source_unref(c->client_event_source);
340
41c817e7
YW
341 if (connection_complete(c) < 0)
342 goto fail;
343
344 return 0;
912b54ad 345
fb69d709 346fail:
9e12d5bf 347 connection_release(c);
fb69d709
LP
348 return 0; /* ignore errors, continue serving */
349}
912b54ad 350
fb69d709
LP
351static int connection_start(Connection *c, struct sockaddr *sa, socklen_t salen) {
352 int r;
353
354 assert(c);
355 assert(sa);
356 assert(salen);
357
358 c->client_fd = socket(sa->sa_family, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
41c817e7
YW
359 if (c->client_fd < 0)
360 return log_error_errno(errno, "Failed to get remote socket: %m");
fb69d709
LP
361
362 r = connect(c->client_fd, sa, salen);
363 if (r < 0) {
41c817e7
YW
364 if (errno != EINPROGRESS)
365 return log_error_errno(errno, "Failed to connect to remote host: %m");
366
367 r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, EPOLLOUT, connect_cb, c);
fb69d709 368 if (r < 0)
41c817e7 369 return log_error_errno(r, "Failed to add connection socket: %m");
912b54ad 370
41c817e7
YW
371 r = sd_event_source_set_enabled(c->client_event_source, SD_EVENT_ONESHOT);
372 if (r < 0)
373 return log_error_errno(r, "Failed to enable oneshot event source: %m");
912b54ad 374
41c817e7
YW
375 return 0;
376 }
377
378 return connection_complete(c);
8569a776
LP
379}
380
f2935c77 381static int resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, Connection *c) {
fb69d709
LP
382 assert(q);
383 assert(c);
384
385 if (ret != 0) {
386 log_error("Failed to resolve host: %s", gai_strerror(ret));
387 goto fail;
388 }
389
390 c->resolve_query = sd_resolve_query_unref(c->resolve_query);
391
41c817e7
YW
392 if (connection_start(c, ai->ai_addr, ai->ai_addrlen) < 0)
393 goto fail;
394
395 return 0;
fb69d709
LP
396
397fail:
9e12d5bf 398 connection_release(c);
fb69d709
LP
399 return 0; /* ignore errors, continue serving */
400}
401
402static int resolve_remote(Connection *c) {
403
404 static const struct addrinfo hints = {
405 .ai_family = AF_UNSPEC,
406 .ai_socktype = SOCK_STREAM,
fb69d709
LP
407 };
408
fb69d709 409 const char *node, *service;
fb69d709
LP
410 int r;
411
15a3e96f 412 if (IN_SET(arg_remote_host[0], '/', '@')) {
f36a9d59
ZJS
413 union sockaddr_union sa;
414 int sa_len;
15a3e96f 415
f36a9d59 416 r = sockaddr_un_set_path(&sa.un, arg_remote_host);
41c817e7
YW
417 if (r < 0)
418 return log_error_errno(r, "Specified address doesn't fit in an AF_UNIX address, refusing: %m");
f36a9d59 419 sa_len = r;
fb69d709 420
f36a9d59 421 return connection_start(c, &sa.sa, sa_len);
fb69d709
LP
422 }
423
424 service = strrchr(arg_remote_host, ':');
425 if (service) {
2f82562b
LP
426 node = strndupa_safe(arg_remote_host,
427 service - arg_remote_host);
313cefa1 428 service++;
fb69d709
LP
429 } else {
430 node = arg_remote_host;
431 service = "80";
432 }
433
434 log_debug("Looking up address info for %s:%s", node, service);
f2935c77 435 r = resolve_getaddrinfo(c->context->resolve, &c->resolve_query, node, service, &hints, resolve_handler, NULL, c);
41c817e7
YW
436 if (r < 0)
437 return log_error_errno(r, "Failed to resolve remote host: %m");
fb69d709
LP
438
439 return 0;
fb69d709
LP
440}
441
f4a717aa 442static int context_add_connection(Context *context, int fd) {
8569a776
LP
443 int r;
444
445 assert(context);
8569a776 446
f4a717aa
YW
447 _cleanup_close_ int nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
448 if (nfd < 0) {
449 if (!ERRNO_IS_ACCEPT_AGAIN(errno))
450 log_warning_errno(errno, "Failed to accept() socket, ignoring: %m");
451
452 return -errno;
912b54ad
DS
453 }
454
f4a717aa
YW
455 if (DEBUG_LOGGING) {
456 _cleanup_free_ char *peer = NULL;
457 (void) getpeername_pretty(nfd, true, &peer);
458 log_debug("New connection from %s", strna(peer));
459 }
460
461 if (set_size(context->connections) > arg_connections_max)
462 return log_warning_errno(SYNTHETIC_ERRNO(EBUSY), "Hit connection limit, refusing connection.");
463
a3ada90a
ZJS
464 r = sd_event_source_set_enabled(context->idle_time, SD_EVENT_OFF);
465 if (r < 0)
466 log_warning_errno(r, "Unable to disable idle timer, continuing: %m");
9e12d5bf 467
f4a717aa
YW
468 _cleanup_(connection_freep) Connection *c = new(Connection, 1);
469 if (!c)
470 return log_oom();
8569a776 471
80ce54ad 472 *c = (Connection) {
f4a717aa
YW
473 .server_fd = TAKE_FD(nfd),
474 .client_fd = -EBADF,
475 .server_to_client_buffer = EBADF_PAIR,
476 .client_to_server_buffer = EBADF_PAIR,
80ce54ad 477 };
8569a776 478
f4a717aa
YW
479 r = set_ensure_put(&context->connections, &connection_hash_ops, c);
480 if (r < 0)
481 return log_oom();
482
483 c->context = context;
912b54ad 484
41c817e7
YW
485 r = resolve_remote(c);
486 if (r < 0)
487 return r;
488
489 TAKE_PTR(c);
490 return 0;
40976028
DS
491}
492
493static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
99534007 494 Context *context = ASSERT_PTR(userdata);
f4a717aa 495 int r;
40976028 496
8569a776
LP
497 assert(s);
498 assert(fd >= 0);
40976028 499 assert(revents & EPOLLIN);
8569a776 500
f4a717aa
YW
501 if (context_add_connection(context, fd) < 0)
502 context_reset_timer(context);
8569a776 503
40976028 504 r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
ccaa30c1
LP
505 if (r < 0)
506 return log_error_errno(r, "Error while re-enabling listener with ONESHOT: %m");
40976028 507
96c374d0 508 return 1;
912b54ad
DS
509}
510
fb69d709 511static int add_listen_socket(Context *context, int fd) {
8569a776
LP
512 int r;
513
514 assert(context);
8569a776 515 assert(fd >= 0);
912b54ad 516
8569a776 517 r = sd_is_socket(fd, 0, SOCK_STREAM, 1);
f647962d
MS
518 if (r < 0)
519 return log_error_errno(r, "Failed to determine socket type: %m");
baaa35ad
ZJS
520 if (r == 0)
521 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
522 "Passed in socket is not a stream socket.");
912b54ad 523
8569a776 524 r = fd_nonblock(fd, true);
f647962d
MS
525 if (r < 0)
526 return log_error_errno(r, "Failed to mark file descriptor non-blocking: %m");
912b54ad 527
37edefb3 528 _cleanup_(sd_event_source_unrefp) sd_event_source *source = NULL;
fb69d709 529 r = sd_event_add_io(context->event, &source, fd, EPOLLIN, accept_cb, context);
f647962d
MS
530 if (r < 0)
531 return log_error_errno(r, "Failed to add event source: %m");
912b54ad 532
ccaa30c1
LP
533 r = sd_event_source_set_exit_on_failure(source, true);
534 if (r < 0)
535 return log_error_errno(r, "Failed to enable exit-on-failure logic: %m");
40976028
DS
536
537 /* Set the watcher to oneshot in case other processes are also
538 * watching to accept(). */
8569a776 539 r = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
f647962d
MS
540 if (r < 0)
541 return log_error_errno(r, "Failed to enable oneshot mode: %m");
912b54ad 542
37edefb3
YW
543 r = set_ensure_consume(&context->listen, &event_source_hash_ops, TAKE_PTR(source));
544 if (r < 0)
545 return log_error_errno(r, "Failed to add source to set: %m");
546
8569a776 547 return 0;
912b54ad
DS
548}
549
37ec0fdd
LP
550static int help(void) {
551 _cleanup_free_ char *link = NULL;
9e12d5bf 552 _cleanup_free_ char *time_link = NULL;
37ec0fdd
LP
553 int r;
554
555 r = terminal_urlify_man("systemd-socket-proxyd", "8", &link);
9e12d5bf
EA
556 if (r < 0)
557 return log_oom();
558 r = terminal_urlify_man("systemd.time", "7", &time_link);
37ec0fdd
LP
559 if (r < 0)
560 return log_oom();
561
601185b4
ZJS
562 printf("%1$s [HOST:PORT]\n"
563 "%1$s [SOCKET]\n\n"
1220625a 564 "%2$sBidirectionally proxy local sockets to another (possibly remote) socket.%3$s\n\n"
23d0fff7 565 " -c --connections-max= Set the maximum number of connections to be accepted\n"
9e12d5bf 566 " --exit-idle-time= Exit when without a connection for this duration. See\n"
1220625a 567 " the %4$s for time span format\n"
8cf030b3 568 " -h --help Show this help\n"
37ec0fdd 569 " --version Show package version\n"
1220625a 570 "\nSee the %5$s for details.\n",
bc556335 571 program_invocation_short_name,
1220625a
BD
572 ansi_highlight(),
573 ansi_normal(),
574 time_link,
575 link);
37ec0fdd
LP
576
577 return 0;
912b54ad
DS
578}
579
8569a776 580static int parse_argv(int argc, char *argv[]) {
912b54ad
DS
581
582 enum {
8cf030b3 583 ARG_VERSION = 0x100,
9e12d5bf 584 ARG_EXIT_IDLE,
8cf030b3 585 ARG_IGNORE_ENV
912b54ad
DS
586 };
587
588 static const struct option options[] = {
dc3b8afb 589 { "connections-max", required_argument, NULL, 'c' },
9e12d5bf 590 { "exit-idle-time", required_argument, NULL, ARG_EXIT_IDLE },
dc3b8afb
DK
591 { "help", no_argument, NULL, 'h' },
592 { "version", no_argument, NULL, ARG_VERSION },
eb9da376 593 {}
912b54ad
DS
594 };
595
dc3b8afb 596 int c, r;
912b54ad
DS
597
598 assert(argc >= 0);
599 assert(argv);
600
dc3b8afb 601 while ((c = getopt_long(argc, argv, "c:h", options, NULL)) >= 0)
912b54ad
DS
602
603 switch (c) {
604
605 case 'h':
37ec0fdd
LP
606 return help();
607
608 case ARG_VERSION:
609 return version();
912b54ad 610
dc3b8afb
DK
611 case 'c':
612 r = safe_atou(optarg, &arg_connections_max);
613 if (r < 0) {
614 log_error("Failed to parse --connections-max= argument: %s", optarg);
615 return r;
616 }
617
baaa35ad
ZJS
618 if (arg_connections_max < 1)
619 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
620 "Connection limit is too low.");
dc3b8afb
DK
621
622 break;
623
9e12d5bf
EA
624 case ARG_EXIT_IDLE:
625 r = parse_sec(optarg, &arg_exit_idle_time);
626 if (r < 0)
627 return log_error_errno(r, "Failed to parse --exit-idle-time= argument: %s", optarg);
628 break;
629
eb9da376 630 case '?':
912b54ad 631 return -EINVAL;
eb9da376
LP
632
633 default:
04499a70 634 assert_not_reached();
912b54ad 635 }
912b54ad 636
baaa35ad
ZJS
637 if (optind >= argc)
638 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
639 "Not enough parameters.");
912b54ad 640
baaa35ad
ZJS
641 if (argc != optind+1)
642 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
643 "Too many parameters.");
912b54ad 644
8569a776 645 arg_remote_host = argv[optind];
912b54ad
DS
646 return 1;
647}
648
a160567e 649static int run(int argc, char *argv[]) {
f4a717aa 650 _cleanup_(context_done) Context context = {};
37fe75a2 651 _unused_ _cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
8569a776 652 int r, n, fd;
912b54ad 653
aa976d87 654 log_setup();
912b54ad 655
8569a776 656 r = parse_argv(argc, argv);
912b54ad 657 if (r <= 0)
a160567e 658 return r;
912b54ad 659
fb69d709 660 r = sd_event_default(&context.event);
a160567e
YW
661 if (r < 0)
662 return log_error_errno(r, "Failed to allocate event loop: %m");
912b54ad 663
fb69d709 664 r = sd_resolve_default(&context.resolve);
a160567e
YW
665 if (r < 0)
666 return log_error_errno(r, "Failed to allocate resolver: %m");
fb69d709
LP
667
668 r = sd_resolve_attach_event(context.resolve, context.event, 0);
a160567e
YW
669 if (r < 0)
670 return log_error_errno(r, "Failed to attach resolver: %m");
fb69d709
LP
671
672 sd_event_set_watchdog(context.event, true);
cde93897 673
a160567e
YW
674 r = sd_listen_fds(1);
675 if (r < 0)
676 return log_error_errno(r, "Failed to receive sockets from parent.");
677 if (r == 0)
678 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Didn't get any sockets passed in.");
679
680 n = r;
8cf030b3
LP
681
682 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
fb69d709 683 r = add_listen_socket(&context, fd);
8569a776 684 if (r < 0)
a160567e 685 return r;
912b54ad
DS
686 }
687
792a8bfb 688 notify_stop = notify_start(NOTIFY_READY_MESSAGE, NOTIFY_STOPPING_MESSAGE);
fb69d709 689 r = sd_event_loop(context.event);
a160567e
YW
690 if (r < 0)
691 return log_error_errno(r, "Failed to run event loop: %m");
8569a776 692
a160567e 693 return 0;
912b54ad 694}
a160567e
YW
695
696DEFINE_MAIN_FUNCTION(run);