LCOV - code coverage report
Current view: top level - socket-proxy - socket-proxyd.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 30 360 8.3 %
Date: 2019-08-22 15:41:25 Functions: 5 18 27.8 %

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

Generated by: LCOV version 1.14