]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/socket-proxy/socket-proxyd.c
udevd: check correct return value of fcntl() (#4758)
[thirdparty/systemd.git] / src / socket-proxy / socket-proxyd.c
CommitLineData
912b54ad
DS
1/***
2 This file is part of systemd.
3
4 Copyright 2013 David Strauss
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
912b54ad 20#include <errno.h>
3f6fd1ba 21#include <fcntl.h>
912b54ad 22#include <getopt.h>
3f6fd1ba 23#include <netdb.h>
912b54ad
DS
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
912b54ad
DS
27#include <sys/socket.h>
28#include <sys/un.h>
29#include <unistd.h>
30
912b54ad
DS
31#include "sd-daemon.h"
32#include "sd-event.h"
fb69d709 33#include "sd-resolve.h"
3f6fd1ba 34
b5efdb8a 35#include "alloc-util.h"
3ffd4af2 36#include "fd-util.h"
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;
fb69d709
LP
403 int r;
404
405 if (path_is_absolute(arg_remote_host)) {
406 sa.un.sun_family = AF_UNIX;
fc2fffe7
LP
407 strncpy(sa.un.sun_path, arg_remote_host, sizeof(sa.un.sun_path));
408 return connection_start(c, &sa.sa, SOCKADDR_UN_LEN(sa.un));
fb69d709
LP
409 }
410
411 if (arg_remote_host[0] == '@') {
412 sa.un.sun_family = AF_UNIX;
413 sa.un.sun_path[0] = 0;
fc2fffe7
LP
414 strncpy(sa.un.sun_path+1, arg_remote_host+1, sizeof(sa.un.sun_path)-1);
415 return connection_start(c, &sa.sa, SOCKADDR_UN_LEN(sa.un));
fb69d709
LP
416 }
417
418 service = strrchr(arg_remote_host, ':');
419 if (service) {
420 node = strndupa(arg_remote_host, service - arg_remote_host);
313cefa1 421 service++;
fb69d709
LP
422 } else {
423 node = arg_remote_host;
424 service = "80";
425 }
426
427 log_debug("Looking up address info for %s:%s", node, service);
428 r = sd_resolve_getaddrinfo(c->context->resolve, &c->resolve_query, node, service, &hints, resolve_cb, c);
429 if (r < 0) {
da927ba9 430 log_error_errno(r, "Failed to resolve remote host: %m");
fb69d709
LP
431 goto fail;
432 }
433
434 return 0;
435
436fail:
437 connection_free(c);
438 return 0; /* ignore errors, continue serving */
439}
440
441static int add_connection_socket(Context *context, int fd) {
8569a776
LP
442 Connection *c;
443 int r;
444
445 assert(context);
8569a776
LP
446 assert(fd >= 0);
447
448 if (set_size(context->connections) > CONNECTIONS_MAX) {
449 log_warning("Hit connection limit, refusing connection.");
03e334a1 450 safe_close(fd);
8569a776 451 return 0;
912b54ad
DS
452 }
453
d5099efc 454 r = set_ensure_allocated(&context->connections, NULL);
fb69d709
LP
455 if (r < 0) {
456 log_oom();
457 return 0;
458 }
912b54ad 459
8569a776 460 c = new0(Connection, 1);
fb69d709
LP
461 if (!c) {
462 log_oom();
463 return 0;
464 }
8569a776 465
e633ea1c 466 c->context = context;
8569a776
LP
467 c->server_fd = fd;
468 c->client_fd = -1;
469 c->server_to_client_buffer[0] = c->server_to_client_buffer[1] = -1;
470 c->client_to_server_buffer[0] = c->client_to_server_buffer[1] = -1;
471
e633ea1c
LP
472 r = set_put(context->connections, c);
473 if (r < 0) {
474 free(c);
fb69d709
LP
475 log_oom();
476 return 0;
8569a776 477 }
912b54ad 478
fb69d709 479 return resolve_remote(c);
40976028
DS
480}
481
482static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
7b77ed8c 483 _cleanup_free_ char *peer = NULL;
8569a776
LP
484 Context *context = userdata;
485 int nfd = -1, r;
40976028 486
8569a776
LP
487 assert(s);
488 assert(fd >= 0);
40976028 489 assert(revents & EPOLLIN);
8569a776
LP
490 assert(context);
491
492 nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
7b77ed8c
LP
493 if (nfd < 0) {
494 if (errno != -EAGAIN)
56f64d95 495 log_warning_errno(errno, "Failed to accept() socket: %m");
7b77ed8c 496 } else {
366b7db4 497 getpeername_pretty(nfd, true, &peer);
8569a776 498 log_debug("New connection from %s", strna(peer));
40976028 499
fb69d709 500 r = add_connection_socket(context, nfd);
f4bd42aa 501 if (r < 0) {
da927ba9 502 log_error_errno(r, "Failed to accept connection, ignoring: %m");
03e334a1 503 safe_close(fd);
f4bd42aa 504 }
7b77ed8c 505 }
8569a776 506
40976028
DS
507 r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
508 if (r < 0) {
da927ba9 509 log_error_errno(r, "Error while re-enabling listener with ONESHOT: %m");
fb69d709 510 sd_event_exit(context->event, r);
40976028
DS
511 return r;
512 }
513
96c374d0 514 return 1;
912b54ad
DS
515}
516
fb69d709 517static int add_listen_socket(Context *context, int fd) {
8569a776
LP
518 sd_event_source *source;
519 int r;
520
521 assert(context);
8569a776 522 assert(fd >= 0);
912b54ad 523
d5099efc 524 r = set_ensure_allocated(&context->listen, NULL);
96c374d0 525 if (r < 0) {
8569a776
LP
526 log_oom();
527 return r;
528 }
529
530 r = sd_is_socket(fd, 0, SOCK_STREAM, 1);
f647962d
MS
531 if (r < 0)
532 return log_error_errno(r, "Failed to determine socket type: %m");
8569a776
LP
533 if (r == 0) {
534 log_error("Passed in socket is not a stream socket.");
535 return -EINVAL;
536 }
912b54ad 537
8569a776 538 r = fd_nonblock(fd, true);
f647962d
MS
539 if (r < 0)
540 return log_error_errno(r, "Failed to mark file descriptor non-blocking: %m");
912b54ad 541
fb69d709 542 r = sd_event_add_io(context->event, &source, fd, EPOLLIN, accept_cb, context);
f647962d
MS
543 if (r < 0)
544 return log_error_errno(r, "Failed to add event source: %m");
912b54ad 545
8569a776 546 r = set_put(context->listen, source);
96c374d0 547 if (r < 0) {
da927ba9 548 log_error_errno(r, "Failed to add source to set: %m");
8569a776 549 sd_event_source_unref(source);
40976028
DS
550 return r;
551 }
552
553 /* Set the watcher to oneshot in case other processes are also
554 * watching to accept(). */
8569a776 555 r = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
f647962d
MS
556 if (r < 0)
557 return log_error_errno(r, "Failed to enable oneshot mode: %m");
912b54ad 558
8569a776 559 return 0;
912b54ad
DS
560}
561
601185b4
ZJS
562static void help(void) {
563 printf("%1$s [HOST:PORT]\n"
564 "%1$s [SOCKET]\n\n"
8569a776 565 "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n"
8cf030b3
LP
566 " -h --help Show this help\n"
567 " --version Show package version\n",
912b54ad 568 program_invocation_short_name);
912b54ad
DS
569}
570
8569a776 571static int parse_argv(int argc, char *argv[]) {
912b54ad
DS
572
573 enum {
8cf030b3
LP
574 ARG_VERSION = 0x100,
575 ARG_IGNORE_ENV
912b54ad
DS
576 };
577
578 static const struct option options[] = {
8cf030b3
LP
579 { "help", no_argument, NULL, 'h' },
580 { "version", no_argument, NULL, ARG_VERSION },
eb9da376 581 {}
912b54ad
DS
582 };
583
8cf030b3 584 int c;
912b54ad
DS
585
586 assert(argc >= 0);
587 assert(argv);
588
601185b4 589 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
912b54ad
DS
590
591 switch (c) {
592
593 case 'h':
601185b4
ZJS
594 help();
595 return 0;
912b54ad
DS
596
597 case ARG_VERSION:
3f6fd1ba 598 return version();
912b54ad 599
eb9da376 600 case '?':
912b54ad 601 return -EINVAL;
eb9da376
LP
602
603 default:
604 assert_not_reached("Unhandled option");
912b54ad 605 }
912b54ad 606
8569a776
LP
607 if (optind >= argc) {
608 log_error("Not enough parameters.");
912b54ad
DS
609 return -EINVAL;
610 }
611
8569a776
LP
612 if (argc != optind+1) {
613 log_error("Too many parameters.");
912b54ad
DS
614 return -EINVAL;
615 }
616
8569a776 617 arg_remote_host = argv[optind];
912b54ad
DS
618 return 1;
619}
620
621int main(int argc, char *argv[]) {
8569a776
LP
622 Context context = {};
623 int r, n, fd;
912b54ad
DS
624
625 log_parse_environment();
626 log_open();
627
8569a776 628 r = parse_argv(argc, argv);
912b54ad
DS
629 if (r <= 0)
630 goto finish;
631
fb69d709 632 r = sd_event_default(&context.event);
8569a776 633 if (r < 0) {
da927ba9 634 log_error_errno(r, "Failed to allocate event loop: %m");
8569a776
LP
635 goto finish;
636 }
912b54ad 637
fb69d709
LP
638 r = sd_resolve_default(&context.resolve);
639 if (r < 0) {
da927ba9 640 log_error_errno(r, "Failed to allocate resolver: %m");
fb69d709
LP
641 goto finish;
642 }
643
644 r = sd_resolve_attach_event(context.resolve, context.event, 0);
645 if (r < 0) {
da927ba9 646 log_error_errno(r, "Failed to attach resolver: %m");
fb69d709
LP
647 goto finish;
648 }
649
650 sd_event_set_watchdog(context.event, true);
cde93897 651
8cf030b3
LP
652 n = sd_listen_fds(1);
653 if (n < 0) {
654 log_error("Failed to receive sockets from parent.");
655 r = n;
656 goto finish;
657 } else if (n == 0) {
658 log_error("Didn't get any sockets passed in.");
659 r = -EINVAL;
660 goto finish;
661 }
662
663 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
fb69d709 664 r = add_listen_socket(&context, fd);
8569a776 665 if (r < 0)
96c374d0 666 goto finish;
912b54ad
DS
667 }
668
fb69d709 669 r = sd_event_loop(context.event);
912b54ad 670 if (r < 0) {
da927ba9 671 log_error_errno(r, "Failed to run event loop: %m");
912b54ad
DS
672 goto finish;
673 }
674
912b54ad 675finish:
8569a776
LP
676 context_free(&context);
677
912b54ad
DS
678 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
679}