]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/socket-proxy/socket-proxyd.c
util-lib: split our string related calls from util.[ch] into its own file string...
[thirdparty/systemd.git] / src / socket-proxy / socket-proxyd.c
CommitLineData
912b54ad
DS
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 David Strauss
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
912b54ad 22#include <errno.h>
3f6fd1ba 23#include <fcntl.h>
912b54ad 24#include <getopt.h>
3f6fd1ba 25#include <netdb.h>
912b54ad
DS
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
912b54ad
DS
29#include <sys/socket.h>
30#include <sys/un.h>
31#include <unistd.h>
32
912b54ad
DS
33#include "sd-daemon.h"
34#include "sd-event.h"
fb69d709 35#include "sd-resolve.h"
3f6fd1ba 36
96c374d0 37#include "log.h"
3f6fd1ba
LP
38#include "path-util.h"
39#include "set.h"
912b54ad 40#include "socket-util.h"
07630cea 41#include "string-util.h"
912b54ad 42#include "util.h"
8569a776
LP
43
44#define BUFFER_SIZE (256 * 1024)
45#define CONNECTIONS_MAX 256
912b54ad 46
fb69d709
LP
47static const char *arg_remote_host = NULL;
48
8569a776 49typedef struct Context {
fb69d709
LP
50 sd_event *event;
51 sd_resolve *resolve;
52
8569a776
LP
53 Set *listen;
54 Set *connections;
55} Context;
912b54ad 56
8569a776 57typedef struct Connection {
e633ea1c
LP
58 Context *context;
59
8569a776
LP
60 int server_fd, client_fd;
61 int server_to_client_buffer[2]; /* a pipe */
62 int client_to_server_buffer[2]; /* a pipe */
912b54ad 63
8569a776
LP
64 size_t server_to_client_buffer_full, client_to_server_buffer_full;
65 size_t server_to_client_buffer_size, client_to_server_buffer_size;
66
67 sd_event_source *server_event_source, *client_event_source;
912b54ad 68
fb69d709
LP
69 sd_resolve_query *resolve_query;
70} Connection;
912b54ad 71
8569a776
LP
72static void connection_free(Connection *c) {
73 assert(c);
32d3c809 74
e633ea1c
LP
75 if (c->context)
76 set_remove(c->context->connections, c);
77
8569a776
LP
78 sd_event_source_unref(c->server_event_source);
79 sd_event_source_unref(c->client_event_source);
32d3c809 80
03e334a1
LP
81 safe_close(c->server_fd);
82 safe_close(c->client_fd);
32d3c809 83
3d94f76c
LP
84 safe_close_pair(c->server_to_client_buffer);
85 safe_close_pair(c->client_to_server_buffer);
32d3c809 86
fb69d709
LP
87 sd_resolve_query_unref(c->resolve_query);
88
8569a776
LP
89 free(c);
90}
32d3c809 91
8569a776
LP
92static void context_free(Context *context) {
93 sd_event_source *es;
94 Connection *c;
95
96 assert(context);
97
98 while ((es = set_steal_first(context->listen)))
99 sd_event_source_unref(es);
100
e633ea1c 101 while ((c = set_first(context->connections)))
8569a776
LP
102 connection_free(c);
103
104 set_free(context->listen);
105 set_free(context->connections);
32d3c809 106
fb69d709
LP
107 sd_event_unref(context->event);
108 sd_resolve_unref(context->resolve);
8569a776 109}
912b54ad 110
8569a776
LP
111static int connection_create_pipes(Connection *c, int buffer[2], size_t *sz) {
112 int r;
912b54ad 113
8569a776
LP
114 assert(c);
115 assert(buffer);
116 assert(sz);
912b54ad 117
8569a776
LP
118 if (buffer[0] >= 0)
119 return 0;
912b54ad 120
8569a776 121 r = pipe2(buffer, O_CLOEXEC|O_NONBLOCK);
4a62c710
MS
122 if (r < 0)
123 return log_error_errno(errno, "Failed to allocate pipe buffer: %m");
912b54ad 124
25dbe4f5 125 (void) fcntl(buffer[0], F_SETPIPE_SZ, BUFFER_SIZE);
8569a776
LP
126
127 r = fcntl(buffer[0], F_GETPIPE_SZ);
4a62c710
MS
128 if (r < 0)
129 return log_error_errno(errno, "Failed to get pipe buffer size: %m");
912b54ad 130
8569a776
LP
131 assert(r > 0);
132 *sz = r;
133
912b54ad
DS
134 return 0;
135}
136
8569a776
LP
137static int connection_shovel(
138 Connection *c,
139 int *from, int buffer[2], int *to,
140 size_t *full, size_t *sz,
141 sd_event_source **from_source, sd_event_source **to_source) {
142
143 bool shoveled;
144
145 assert(c);
146 assert(from);
147 assert(buffer);
148 assert(buffer[0] >= 0);
149 assert(buffer[1] >= 0);
150 assert(to);
151 assert(full);
152 assert(sz);
153 assert(from_source);
154 assert(to_source);
155
156 do {
157 ssize_t z;
158
159 shoveled = false;
160
161 if (*full < *sz && *from >= 0 && *to >= 0) {
162 z = splice(*from, NULL, buffer[1], NULL, *sz - *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
163 if (z > 0) {
164 *full += z;
165 shoveled = true;
166 } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) {
167 *from_source = sd_event_source_unref(*from_source);
03e334a1 168 *from = safe_close(*from);
4a62c710
MS
169 } else if (errno != EAGAIN && errno != EINTR)
170 return log_error_errno(errno, "Failed to splice: %m");
912b54ad
DS
171 }
172
8569a776
LP
173 if (*full > 0 && *to >= 0) {
174 z = splice(buffer[0], NULL, *to, NULL, *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
175 if (z > 0) {
176 *full -= z;
177 shoveled = true;
178 } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) {
179 *to_source = sd_event_source_unref(*to_source);
03e334a1 180 *to = safe_close(*to);
4a62c710
MS
181 } else if (errno != EAGAIN && errno != EINTR)
182 return log_error_errno(errno, "Failed to splice: %m");
8569a776
LP
183 }
184 } while (shoveled);
912b54ad 185
8569a776 186 return 0;
912b54ad
DS
187}
188
fb69d709 189static int connection_enable_event_sources(Connection *c);
8569a776
LP
190
191static int traffic_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
192 Connection *c = userdata;
912b54ad
DS
193 int r;
194
8569a776
LP
195 assert(s);
196 assert(fd >= 0);
197 assert(c);
912b54ad 198
8569a776
LP
199 r = connection_shovel(c,
200 &c->server_fd, c->server_to_client_buffer, &c->client_fd,
201 &c->server_to_client_buffer_full, &c->server_to_client_buffer_size,
202 &c->server_event_source, &c->client_event_source);
203 if (r < 0)
204 goto quit;
912b54ad 205
8569a776
LP
206 r = connection_shovel(c,
207 &c->client_fd, c->client_to_server_buffer, &c->server_fd,
208 &c->client_to_server_buffer_full, &c->client_to_server_buffer_size,
209 &c->client_event_source, &c->server_event_source);
210 if (r < 0)
211 goto quit;
912b54ad 212
8569a776
LP
213 /* EOF on both sides? */
214 if (c->server_fd == -1 && c->client_fd == -1)
215 goto quit;
912b54ad 216
8569a776
LP
217 /* Server closed, and all data written to client? */
218 if (c->server_fd == -1 && c->server_to_client_buffer_full <= 0)
219 goto quit;
912b54ad 220
8569a776
LP
221 /* Client closed, and all data written to server? */
222 if (c->client_fd == -1 && c->client_to_server_buffer_full <= 0)
223 goto quit;
912b54ad 224
fb69d709 225 r = connection_enable_event_sources(c);
8569a776
LP
226 if (r < 0)
227 goto quit;
912b54ad 228
8569a776 229 return 1;
912b54ad 230
8569a776
LP
231quit:
232 connection_free(c);
233 return 0; /* ignore errors, continue serving */
912b54ad
DS
234}
235
fb69d709 236static int connection_enable_event_sources(Connection *c) {
8569a776
LP
237 uint32_t a = 0, b = 0;
238 int r;
912b54ad 239
8569a776 240 assert(c);
912b54ad 241
8569a776
LP
242 if (c->server_to_client_buffer_full > 0)
243 b |= EPOLLOUT;
244 if (c->server_to_client_buffer_full < c->server_to_client_buffer_size)
245 a |= EPOLLIN;
912b54ad 246
8569a776
LP
247 if (c->client_to_server_buffer_full > 0)
248 a |= EPOLLOUT;
249 if (c->client_to_server_buffer_full < c->client_to_server_buffer_size)
250 b |= EPOLLIN;
912b54ad 251
8569a776
LP
252 if (c->server_event_source)
253 r = sd_event_source_set_io_events(c->server_event_source, a);
254 else if (c->server_fd >= 0)
fb69d709 255 r = sd_event_add_io(c->context->event, &c->server_event_source, c->server_fd, a, traffic_cb, c);
8569a776
LP
256 else
257 r = 0;
258
f647962d
MS
259 if (r < 0)
260 return log_error_errno(r, "Failed to set up server event source: %m");
912b54ad 261
8569a776
LP
262 if (c->client_event_source)
263 r = sd_event_source_set_io_events(c->client_event_source, b);
264 else if (c->client_fd >= 0)
fb69d709 265 r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, b, traffic_cb, c);
8569a776
LP
266 else
267 r = 0;
912b54ad 268
f647962d
MS
269 if (r < 0)
270 return log_error_errno(r, "Failed to set up client event source: %m");
912b54ad 271
8569a776 272 return 0;
912b54ad
DS
273}
274
fb69d709
LP
275static int connection_complete(Connection *c) {
276 int r;
277
278 assert(c);
279
280 r = connection_create_pipes(c, c->server_to_client_buffer, &c->server_to_client_buffer_size);
281 if (r < 0)
282 goto fail;
283
284 r = connection_create_pipes(c, c->client_to_server_buffer, &c->client_to_server_buffer_size);
285 if (r < 0)
286 goto fail;
287
288 r = connection_enable_event_sources(c);
289 if (r < 0)
290 goto fail;
291
292 return 0;
293
294fail:
295 connection_free(c);
296 return 0; /* ignore errors, continue serving */
297}
298
8569a776
LP
299static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
300 Connection *c = userdata;
301 socklen_t solen;
302 int error, r;
303
304 assert(s);
305 assert(fd >= 0);
306 assert(c);
307
308 solen = sizeof(error);
309 r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &solen);
310 if (r < 0) {
56f64d95 311 log_error_errno(errno, "Failed to issue SO_ERROR: %m");
40976028
DS
312 goto fail;
313 }
912b54ad 314
8569a776 315 if (error != 0) {
279d3c9c 316 log_error_errno(error, "Failed to connect to remote host: %m");
912b54ad
DS
317 goto fail;
318 }
319
8569a776
LP
320 c->client_event_source = sd_event_source_unref(c->client_event_source);
321
fb69d709 322 return connection_complete(c);
912b54ad 323
fb69d709
LP
324fail:
325 connection_free(c);
326 return 0; /* ignore errors, continue serving */
327}
912b54ad 328
fb69d709
LP
329static int connection_start(Connection *c, struct sockaddr *sa, socklen_t salen) {
330 int r;
331
332 assert(c);
333 assert(sa);
334 assert(salen);
335
336 c->client_fd = socket(sa->sa_family, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
337 if (c->client_fd < 0) {
56f64d95 338 log_error_errno(errno, "Failed to get remote socket: %m");
8569a776 339 goto fail;
fb69d709
LP
340 }
341
342 r = connect(c->client_fd, sa, salen);
343 if (r < 0) {
344 if (errno == EINPROGRESS) {
345 r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, EPOLLOUT, connect_cb, c);
346 if (r < 0) {
da927ba9 347 log_error_errno(r, "Failed to add connection socket: %m");
fb69d709
LP
348 goto fail;
349 }
350
351 r = sd_event_source_set_enabled(c->client_event_source, SD_EVENT_ONESHOT);
352 if (r < 0) {
da927ba9 353 log_error_errno(r, "Failed to enable oneshot event source: %m");
fb69d709
LP
354 goto fail;
355 }
356 } else {
56f64d95 357 log_error_errno(errno, "Failed to connect to remote host: %m");
fb69d709
LP
358 goto fail;
359 }
360 } else {
361 r = connection_complete(c);
362 if (r < 0)
363 goto fail;
364 }
912b54ad 365
8569a776 366 return 0;
912b54ad 367
8569a776
LP
368fail:
369 connection_free(c);
370 return 0; /* ignore errors, continue serving */
371}
372
fb69d709
LP
373static int resolve_cb(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata) {
374 Connection *c = userdata;
375
376 assert(q);
377 assert(c);
378
379 if (ret != 0) {
380 log_error("Failed to resolve host: %s", gai_strerror(ret));
381 goto fail;
382 }
383
384 c->resolve_query = sd_resolve_query_unref(c->resolve_query);
385
386 return connection_start(c, ai->ai_addr, ai->ai_addrlen);
387
388fail:
389 connection_free(c);
390 return 0; /* ignore errors, continue serving */
391}
392
393static int resolve_remote(Connection *c) {
394
395 static const struct addrinfo hints = {
396 .ai_family = AF_UNSPEC,
397 .ai_socktype = SOCK_STREAM,
398 .ai_flags = AI_ADDRCONFIG
399 };
400
1ec6af16 401 union sockaddr_union sa = {};
fb69d709 402 const char *node, *service;
8569a776 403 socklen_t salen;
fb69d709
LP
404 int r;
405
406 if (path_is_absolute(arg_remote_host)) {
407 sa.un.sun_family = AF_UNIX;
408 strncpy(sa.un.sun_path, arg_remote_host, sizeof(sa.un.sun_path)-1);
409 sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0;
410
411 salen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path);
412
413 return connection_start(c, &sa.sa, salen);
414 }
415
416 if (arg_remote_host[0] == '@') {
417 sa.un.sun_family = AF_UNIX;
418 sa.un.sun_path[0] = 0;
419 strncpy(sa.un.sun_path+1, arg_remote_host+1, sizeof(sa.un.sun_path)-2);
420 sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0;
421
422 salen = offsetof(union sockaddr_union, un.sun_path) + 1 + strlen(sa.un.sun_path + 1);
423
424 return connection_start(c, &sa.sa, salen);
425 }
426
427 service = strrchr(arg_remote_host, ':');
428 if (service) {
429 node = strndupa(arg_remote_host, service - arg_remote_host);
430 service ++;
431 } else {
432 node = arg_remote_host;
433 service = "80";
434 }
435
436 log_debug("Looking up address info for %s:%s", node, service);
437 r = sd_resolve_getaddrinfo(c->context->resolve, &c->resolve_query, node, service, &hints, resolve_cb, c);
438 if (r < 0) {
da927ba9 439 log_error_errno(r, "Failed to resolve remote host: %m");
fb69d709
LP
440 goto fail;
441 }
442
443 return 0;
444
445fail:
446 connection_free(c);
447 return 0; /* ignore errors, continue serving */
448}
449
450static int add_connection_socket(Context *context, int fd) {
8569a776
LP
451 Connection *c;
452 int r;
453
454 assert(context);
8569a776
LP
455 assert(fd >= 0);
456
457 if (set_size(context->connections) > CONNECTIONS_MAX) {
458 log_warning("Hit connection limit, refusing connection.");
03e334a1 459 safe_close(fd);
8569a776 460 return 0;
912b54ad
DS
461 }
462
d5099efc 463 r = set_ensure_allocated(&context->connections, NULL);
fb69d709
LP
464 if (r < 0) {
465 log_oom();
466 return 0;
467 }
912b54ad 468
8569a776 469 c = new0(Connection, 1);
fb69d709
LP
470 if (!c) {
471 log_oom();
472 return 0;
473 }
8569a776 474
e633ea1c 475 c->context = context;
8569a776
LP
476 c->server_fd = fd;
477 c->client_fd = -1;
478 c->server_to_client_buffer[0] = c->server_to_client_buffer[1] = -1;
479 c->client_to_server_buffer[0] = c->client_to_server_buffer[1] = -1;
480
e633ea1c
LP
481 r = set_put(context->connections, c);
482 if (r < 0) {
483 free(c);
fb69d709
LP
484 log_oom();
485 return 0;
8569a776 486 }
912b54ad 487
fb69d709 488 return resolve_remote(c);
40976028
DS
489}
490
491static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
7b77ed8c 492 _cleanup_free_ char *peer = NULL;
8569a776
LP
493 Context *context = userdata;
494 int nfd = -1, r;
40976028 495
8569a776
LP
496 assert(s);
497 assert(fd >= 0);
40976028 498 assert(revents & EPOLLIN);
8569a776
LP
499 assert(context);
500
501 nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
7b77ed8c
LP
502 if (nfd < 0) {
503 if (errno != -EAGAIN)
56f64d95 504 log_warning_errno(errno, "Failed to accept() socket: %m");
7b77ed8c 505 } else {
8569a776
LP
506 getpeername_pretty(nfd, &peer);
507 log_debug("New connection from %s", strna(peer));
40976028 508
fb69d709 509 r = add_connection_socket(context, nfd);
f4bd42aa 510 if (r < 0) {
da927ba9 511 log_error_errno(r, "Failed to accept connection, ignoring: %m");
03e334a1 512 safe_close(fd);
f4bd42aa 513 }
7b77ed8c 514 }
8569a776 515
40976028
DS
516 r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
517 if (r < 0) {
da927ba9 518 log_error_errno(r, "Error while re-enabling listener with ONESHOT: %m");
fb69d709 519 sd_event_exit(context->event, r);
40976028
DS
520 return r;
521 }
522
96c374d0 523 return 1;
912b54ad
DS
524}
525
fb69d709 526static int add_listen_socket(Context *context, int fd) {
8569a776
LP
527 sd_event_source *source;
528 int r;
529
530 assert(context);
8569a776 531 assert(fd >= 0);
912b54ad 532
d5099efc 533 r = set_ensure_allocated(&context->listen, NULL);
96c374d0 534 if (r < 0) {
8569a776
LP
535 log_oom();
536 return r;
537 }
538
539 r = sd_is_socket(fd, 0, SOCK_STREAM, 1);
f647962d
MS
540 if (r < 0)
541 return log_error_errno(r, "Failed to determine socket type: %m");
8569a776
LP
542 if (r == 0) {
543 log_error("Passed in socket is not a stream socket.");
544 return -EINVAL;
545 }
912b54ad 546
8569a776 547 r = fd_nonblock(fd, true);
f647962d
MS
548 if (r < 0)
549 return log_error_errno(r, "Failed to mark file descriptor non-blocking: %m");
912b54ad 550
fb69d709 551 r = sd_event_add_io(context->event, &source, fd, EPOLLIN, accept_cb, context);
f647962d
MS
552 if (r < 0)
553 return log_error_errno(r, "Failed to add event source: %m");
912b54ad 554
8569a776 555 r = set_put(context->listen, source);
96c374d0 556 if (r < 0) {
da927ba9 557 log_error_errno(r, "Failed to add source to set: %m");
8569a776 558 sd_event_source_unref(source);
40976028
DS
559 return r;
560 }
561
562 /* Set the watcher to oneshot in case other processes are also
563 * watching to accept(). */
8569a776 564 r = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
f647962d
MS
565 if (r < 0)
566 return log_error_errno(r, "Failed to enable oneshot mode: %m");
912b54ad 567
8569a776 568 return 0;
912b54ad
DS
569}
570
601185b4
ZJS
571static void help(void) {
572 printf("%1$s [HOST:PORT]\n"
573 "%1$s [SOCKET]\n\n"
8569a776 574 "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n"
8cf030b3
LP
575 " -h --help Show this help\n"
576 " --version Show package version\n",
912b54ad 577 program_invocation_short_name);
912b54ad
DS
578}
579
8569a776 580static int parse_argv(int argc, char *argv[]) {
912b54ad
DS
581
582 enum {
8cf030b3
LP
583 ARG_VERSION = 0x100,
584 ARG_IGNORE_ENV
912b54ad
DS
585 };
586
587 static const struct option options[] = {
8cf030b3
LP
588 { "help", no_argument, NULL, 'h' },
589 { "version", no_argument, NULL, ARG_VERSION },
eb9da376 590 {}
912b54ad
DS
591 };
592
8cf030b3 593 int c;
912b54ad
DS
594
595 assert(argc >= 0);
596 assert(argv);
597
601185b4 598 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
912b54ad
DS
599
600 switch (c) {
601
602 case 'h':
601185b4
ZJS
603 help();
604 return 0;
912b54ad
DS
605
606 case ARG_VERSION:
3f6fd1ba 607 return version();
912b54ad 608
eb9da376 609 case '?':
912b54ad 610 return -EINVAL;
eb9da376
LP
611
612 default:
613 assert_not_reached("Unhandled option");
912b54ad 614 }
912b54ad 615
8569a776
LP
616 if (optind >= argc) {
617 log_error("Not enough parameters.");
912b54ad
DS
618 return -EINVAL;
619 }
620
8569a776
LP
621 if (argc != optind+1) {
622 log_error("Too many parameters.");
912b54ad
DS
623 return -EINVAL;
624 }
625
8569a776 626 arg_remote_host = argv[optind];
912b54ad
DS
627 return 1;
628}
629
630int main(int argc, char *argv[]) {
8569a776
LP
631 Context context = {};
632 int r, n, fd;
912b54ad
DS
633
634 log_parse_environment();
635 log_open();
636
8569a776 637 r = parse_argv(argc, argv);
912b54ad
DS
638 if (r <= 0)
639 goto finish;
640
fb69d709 641 r = sd_event_default(&context.event);
8569a776 642 if (r < 0) {
da927ba9 643 log_error_errno(r, "Failed to allocate event loop: %m");
8569a776
LP
644 goto finish;
645 }
912b54ad 646
fb69d709
LP
647 r = sd_resolve_default(&context.resolve);
648 if (r < 0) {
da927ba9 649 log_error_errno(r, "Failed to allocate resolver: %m");
fb69d709
LP
650 goto finish;
651 }
652
653 r = sd_resolve_attach_event(context.resolve, context.event, 0);
654 if (r < 0) {
da927ba9 655 log_error_errno(r, "Failed to attach resolver: %m");
fb69d709
LP
656 goto finish;
657 }
658
659 sd_event_set_watchdog(context.event, true);
cde93897 660
8cf030b3
LP
661 n = sd_listen_fds(1);
662 if (n < 0) {
663 log_error("Failed to receive sockets from parent.");
664 r = n;
665 goto finish;
666 } else if (n == 0) {
667 log_error("Didn't get any sockets passed in.");
668 r = -EINVAL;
669 goto finish;
670 }
671
672 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
fb69d709 673 r = add_listen_socket(&context, fd);
8569a776 674 if (r < 0)
96c374d0 675 goto finish;
912b54ad
DS
676 }
677
fb69d709 678 r = sd_event_loop(context.event);
912b54ad 679 if (r < 0) {
da927ba9 680 log_error_errno(r, "Failed to run event loop: %m");
912b54ad
DS
681 goto finish;
682 }
683
912b54ad 684finish:
8569a776
LP
685 context_free(&context);
686
912b54ad
DS
687 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
688}