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