1/* C++ modules.  Experimental!
2   Copyright (C) 2018-2022 Free Software Foundation, Inc.
3   Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
4
5   This file is part of GCC.
6
7   GCC is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3, or (at your option)
10   any later version.
11
12   GCC is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3.  If not see
19<http://www.gnu.org/licenses/>.  */
20
21#include "config.h"
22#include "resolver.h"
23
24// C++
25#include <set>
26#include <vector>
27#include <map>
28// C
29#include <csignal>
30#include <cstring>
31#include <cstdarg>
32#include <cstdlib>
33// OS
34#include <unistd.h>
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <fcntl.h>
38
39// Network
40/* Include network stuff first.  Excitingly OSX10.14 uses bcmp here, which
41   we poison later!  */
42#if defined (HAVE_AF_UNIX) || defined (HAVE_AF_INET6)
43/* socket, bind, listen, accept{4}  */
44# define NETWORKING 1
45# include <sys/socket.h>
46# ifdef HAVE_AF_UNIX
47/* sockaddr_un  */
48#  include <sys/un.h>
49# endif
50# include <netinet/in.h>
51# ifdef HAVE_AF_INET6
52/* sockaddr_in6, getaddrinfo, freeaddrinfo, gai_sterror, ntohs, htons.  */
53#  include <netdb.h>
54# endif
55#ifdef HAVE_INET_NTOP
56/* inet_ntop.  */
57#include <arpa/inet.h>
58#endif
59#endif
60#ifndef HAVE_AF_INET6
61# define gai_strerror(X) ""
62#endif
63
64#ifndef AI_NUMERICSERV
65#define AI_NUMERICSERV 0
66#endif
67
68#include <getopt.h>
69
70// Select or epoll
71#if NETWORKING
72#ifdef HAVE_EPOLL
73/* epoll_create, epoll_ctl, epoll_pwait  */
74#include <sys/epoll.h>
75#endif
76#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
77/* pselect or select  */
78#include <sys/select.h>
79#endif
80#endif
81
82// GCC
83#include "version.h"
84#include "ansidecl.h"
85#define HAVE_DECL_BASENAME 1 /* See comment in gcc/configure.ac.  */
86#include "libiberty.h"
87
88#if !HOST_HAS_O_CLOEXEC
89#define O_CLOEXEC 0
90#endif
91
92#ifndef IS_DIR_SEPARATOR
93#define IS_DIR_SEPARATOR(C) ((C) == '/')
94#endif
95#ifndef DIR_SEPARATOR
96#define DIR_SEPARATOR '/'
97#endif
98
99/* Imported from libcpp/system.h
100   Use gcc_assert(EXPR) to test invariants.  */
101#if ENABLE_ASSERT_CHECKING
102#define gcc_assert(EXPR)                                                \
103   ((void)(!(EXPR) ? fancy_abort (__FILE__, __LINE__, __FUNCTION__), 0 : 0))
104#elif (GCC_VERSION >= 4005)
105#define gcc_assert(EXPR)                                                \
106  ((void)(__builtin_expect (!(EXPR), 0) ? __builtin_unreachable (), 0 : 0))
107#else
108/* Include EXPR, so that unused variable warnings do not occur.  */
109#define gcc_assert(EXPR) ((void)(0 && (EXPR)))
110#endif
111
112/* Use gcc_unreachable() to mark unreachable locations (like an
113   unreachable default case of a switch.  Do not use gcc_assert(0).  */
114#if (GCC_VERSION >= 4005) && !ENABLE_ASSERT_CHECKING
115#define gcc_unreachable() __builtin_unreachable ()
116#else
117#define gcc_unreachable() (fancy_abort (__FILE__, __LINE__, __FUNCTION__))
118#endif
119
120
121#if NETWORKING
122struct netmask {
123  in6_addr addr;
124  unsigned bits;
125
126  netmask (const in6_addr &a, unsigned b)
127  {
128    if (b > sizeof (in6_addr) * 8)
129      b = sizeof (in6_addr) * 8;
130    bits = b;
131    unsigned byte = (b + 7) / 8;
132    unsigned ix = 0;
133    for (ix = 0; ix < byte; ix++)
134      addr.s6_addr[ix] = a.s6_addr[ix];
135    for (; ix != sizeof (in6_addr); ix++)
136      addr.s6_addr[ix] = 0;
137    if (b & 3)
138      addr.s6_addr[b/7] &= (255 << 8) >> (b & 3);
139  }
140
141  bool includes (const in6_addr &a) const
142  {
143    unsigned byte = bits / 8;
144    for (unsigned ix = 0; ix != byte; ix++)
145      if (addr.s6_addr[ix] != a.s6_addr[ix])
146	return false;
147    if (bits & 3)
148      if ((addr.s6_addr[byte] ^ a.s6_addr[byte]) >> (8 - (bits & 3)))
149	return false;
150    return true;
151  }
152};
153
154/* Netmask comparison.  */
155struct netmask_cmp {
156  bool operator() (const netmask &a, const netmask &b) const
157  {
158    if (a.bits != b.bits)
159      return a.bits < b.bits;
160    for (unsigned ix = 0; ix != sizeof (in6_addr); ix++)
161      if (a.addr.s6_addr[ix] != b.addr.s6_addr[ix])
162	return a.addr.s6_addr[ix] < b.addr.s6_addr[ix];
163    return false;
164  }
165};
166
167typedef std::set<netmask, netmask_cmp> netmask_set_t;
168typedef std::vector<netmask> netmask_vec_t;
169#endif
170
171const char *progname;
172
173/* Speak thoughts out loud.  */
174static bool flag_noisy = false;
175
176/* One and done.  */
177static bool flag_one = false;
178
179/* Serialize connections.  */
180static bool flag_sequential = false;
181
182/* Fallback to default if map file is unrewarding.  */
183static bool flag_map = false;
184
185/* Fallback to xlate if map file is unrewarding.  */
186static bool flag_xlate = false;
187
188/* Root binary directory.  */
189static const char *flag_root = "gcm.cache";
190
191#if NETWORKING
192static netmask_set_t netmask_set;
193
194static netmask_vec_t accept_addrs;
195#endif
196
197/* Strip out the source directory from FILE.  */
198
199static const char *
200trim_src_file (const char *file)
201{
202  static const char me[] = __FILE__;
203  unsigned pos = 0;
204
205  while (file[pos] == me[pos] && me[pos])
206    pos++;
207  while (pos && !IS_DIR_SEPARATOR (me[pos-1]))
208    pos--;
209
210  return file + pos;
211}
212
213/* Die screaming.  */
214
215void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD
216internal_error (const char *fmt, ...)
217{
218  fprintf (stderr, "%s:Internal error ", progname);
219  va_list args;
220
221  va_start (args, fmt);
222  vfprintf (stderr, fmt, args);
223  va_end (args);
224  fprintf (stderr, "\n");
225
226  exit (2);
227}
228
229/* Hooked to from gcc_assert & gcc_unreachable.  */
230
231#if ENABLE_ASSERT_CHECKING
232void ATTRIBUTE_NORETURN ATTRIBUTE_COLD
233fancy_abort (const char *file, int line, const char *func)
234{
235  internal_error ("in %s, at %s:%d", func, trim_src_file (file), line);
236}
237#endif
238
239/* Exploded on a signal.  */
240
241static void ATTRIBUTE_NORETURN ATTRIBUTE_COLD
242crash_signal (int sig)
243{
244  signal (sig, SIG_DFL);
245  // strsignal is not portable :(
246  internal_error ("signal %d", sig);
247}
248
249/* A fatal error of some kind.  */
250
251static void ATTRIBUTE_NORETURN ATTRIBUTE_COLD ATTRIBUTE_PRINTF_1
252error (const char *msg, ...)
253{
254  fprintf (stderr, "%s:error: ", progname);
255  va_list args;
256
257  va_start (args, msg);
258  vfprintf (stderr, msg, args);
259  va_end (args);
260  fprintf (stderr, "\n");
261
262  exit (1);
263}
264
265#if NETWORKING
266/* Progress messages to the user.  */
267static bool ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD
268noisy (const char *fmt, ...)
269{
270  fprintf (stderr, "%s:", progname);
271  va_list args;
272  va_start (args, fmt);
273  vfprintf (stderr, fmt, args);
274  va_end (args);
275  fprintf (stderr, "\n");
276
277  return false;
278}
279#endif
280
281/* More messages to the user.  */
282
283static void ATTRIBUTE_PRINTF_2
284fnotice (FILE *file, const char *fmt, ...)
285{
286  va_list args;
287
288  va_start (args, fmt);
289  vfprintf (file, fmt, args);
290  va_end (args);
291}
292
293static void ATTRIBUTE_NORETURN
294print_usage (int error_p)
295{
296  FILE *file = error_p ? stderr : stdout;
297  int status = error_p ? 1 : 0;
298
299  fnotice (file, "Usage: %s [OPTION...] [CONNECTION] [MAPPINGS...] \n\n",
300	   progname);
301  fnotice (file, "C++ Module Mapper.\n\n");
302  fnotice (file, "  -a, --accept     Netmask to accept from\n");
303  fnotice (file, "  -f, --fallback   Use fallback for missing mappings\n");
304  fnotice (file, "  -h, --help       Print this help, then exit\n");
305  fnotice (file, "  -n, --noisy      Print progress messages\n");
306  fnotice (file, "  -1, --one        One connection and then exit\n");
307  fnotice (file, "  -r, --root DIR   Root compiled module directory\n");
308  fnotice (file, "  -s, --sequential Process connections sequentially\n");
309  fnotice (file, "  -v, --version    Print version number, then exit\n");
310  fnotice (file, "Send SIGTERM(%d) to terminate\n", SIGTERM);
311  fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
312	   bug_report_url);
313  exit (status);
314}
315
316/* Print version information and exit.  */
317
318static void ATTRIBUTE_NORETURN
319print_version (void)
320{
321  fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string);
322  fprintf (stdout, "Copyright %s 2018-2022 Free Software Foundation, Inc.\n",
323	   ("(C)"));
324  fnotice (stdout,
325	   ("This is free software; see the source for copying conditions.\n"
326	    "There is NO warranty; not even for MERCHANTABILITY or \n"
327	    "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
328  exit (0);
329}
330
331/* ARG is a netmask to accept from.  Add it to the table.  Return
332   false if we fail to resolve it.  */
333
334static bool
335accept_from (char *arg ATTRIBUTE_UNUSED)
336{
337  bool ok = true;
338#if HAVE_AF_INET6
339  unsigned bits = sizeof (in6_addr) * 8;
340  char *slash = strrchr (arg, '/');
341  if (slash)
342    {
343      *slash = 0;
344      if (slash[1])
345	{
346	  char *endp;
347	  bits = strtoul (slash + 1, &endp, 0);
348	}
349    }
350
351  addrinfo hints;
352
353  hints.ai_flags = AI_NUMERICSERV;
354  hints.ai_family = AF_INET6;
355  hints.ai_socktype = SOCK_STREAM;
356  hints.ai_protocol = 0;
357  hints.ai_addrlen = 0;
358  hints.ai_addr = NULL;
359  hints.ai_canonname = NULL;
360  hints.ai_next = NULL;
361
362  struct addrinfo *addrs = NULL;
363  /* getaddrinfo requires either hostname or servname to be non-null, so that we must
364     set a port number (to cover the case that the string passed contains just /NN).
365     Use an arbitrary in-range port number, but avoiding "0" which triggers a bug on
366     some BSD variants.  */
367  if (int e = getaddrinfo (slash == arg ? NULL : arg, "1", &hints, &addrs))
368    {
369      noisy ("cannot resolve '%s': %s", arg, gai_strerror (e));
370      ok = false;
371    }
372  else
373    for (addrinfo *next = addrs; next; next = next->ai_next)
374      if (next->ai_family == AF_INET6)
375	{
376	  netmask mask (((const sockaddr_in6 *)next->ai_addr)->sin6_addr, bits);
377	  netmask_set.insert (mask);
378	}
379  freeaddrinfo (addrs);
380#endif
381  return ok;
382}
383
384/* Process args, return index to first non-arg.  */
385
386static int
387process_args (int argc, char **argv)
388{
389  static const struct option options[] =
390    {
391     { "accept", required_argument, NULL, 'a' },
392     { "help",	no_argument,	NULL, 'h' },
393     { "map",   no_argument,	NULL, 'm' },
394     { "noisy",	no_argument,	NULL, 'n' },
395     { "one",	no_argument,	NULL, '1' },
396     { "root",	required_argument, NULL, 'r' },
397     { "sequential", no_argument, NULL, 's' },
398     { "translate",no_argument,	NULL, 't' },
399     { "version", no_argument,	NULL, 'v' },
400     { 0, 0, 0, 0 }
401    };
402  int opt;
403  bool bad_accept = false;
404  const char *opts = "a:fhmn1r:stv";
405  while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1)
406    {
407      switch (opt)
408	{
409	case 'a':
410	  if (!accept_from (optarg))
411	    bad_accept = true;
412	  break;
413	case 'h':
414	  print_usage (false);
415	  /* print_usage will exit.  */
416	case 'f': // deprecated alias
417	case 'm':
418	  flag_map = true;
419	  break;
420	case 'n':
421	  flag_noisy = true;
422	  break;
423	case '1':
424	  flag_one = true;
425	  break;
426	case 'r':
427	  flag_root = optarg;
428	  break;
429	case 's':
430	  flag_sequential = true;
431	  break;
432	case 't':
433	  flag_xlate = true;
434	  break;
435	case 'v':
436	  print_version ();
437	  /* print_version will exit.  */
438	default:
439	  print_usage (true);
440	  /* print_usage will exit.  */
441	}
442    }
443
444  if (bad_accept)
445    error ("failed to resolve all accept addresses");
446
447  return optind;
448}
449
450#if NETWORKING
451
452/* Manipulate the EPOLL state, or do nothing, if there is epoll.  */
453
454#ifdef HAVE_EPOLL
455static inline void
456do_epoll_ctl (int epoll_fd, int code, int event, int fd, unsigned data)
457{
458  epoll_event ev;
459  ev.events = event;
460  ev.data.u32 = data;
461  if (epoll_ctl (epoll_fd, code, fd, &ev))
462    {
463      noisy ("epoll_ctl error:%s", xstrerror (errno));
464      gcc_unreachable ();
465    }
466}
467#define my_epoll_ctl(EFD,C,EV,FD,CL) \
468  ((EFD) >= 0 ? do_epoll_ctl (EFD,C,EV,FD,CL) : (void)0)
469#else
470#define my_epoll_ctl(EFD,C,EV,FD,CL) ((void)(EFD), (void)(FD), (void)(CL))
471#endif
472
473/* We increment this to tell the server to shut down.  */
474static volatile int term = false;
475static volatile int kill_sock_fd = -1;
476#if !defined (HAVE_PSELECT) && defined (HAVE_SELECT)
477static int term_pipe[2] = {-1, -1};
478#else
479#define term_pipe ((int *)NULL)
480#endif
481
482/* A terminate signal.  Shutdown gracefully.  */
483
484static void
485term_signal (int sig)
486{
487  signal (sig, term_signal);
488  term = term + 1;
489  if (term_pipe && term_pipe[1] >= 0)
490    write (term_pipe[1], &term_pipe[1], 1);
491}
492
493/* A kill signal.  Shutdown immediately.  */
494
495static void
496kill_signal (int sig)
497{
498  signal (sig, SIG_DFL);
499  int sock_fd = kill_sock_fd;
500  if (sock_fd >= 0)
501    close (sock_fd);
502  exit (2);
503}
504
505bool process_server (Cody::Server *server, unsigned slot, int epoll_fd)
506{
507  switch (server->GetDirection ())
508    {
509    case Cody::Server::READING:
510      if (int err = server->Read ())
511	return !(err == EINTR || err == EAGAIN);
512      server->ProcessRequests ();
513      server->PrepareToWrite ();
514      break;
515
516    case Cody::Server::WRITING:
517      if (int err = server->Write ())
518	return !(err == EINTR || err == EAGAIN);
519      server->PrepareToRead ();
520      break;
521
522    default:
523      // We should never get here
524      return true;
525    }
526
527  // We've changed direction, so update epoll
528  gcc_assert (server->GetFDRead () == server->GetFDWrite ());
529  my_epoll_ctl (epoll_fd, EPOLL_CTL_MOD,
530		server->GetDirection () == Cody::Server::READING
531		? EPOLLIN : EPOLLOUT, server->GetFDRead (), slot + 1);
532
533  return false;
534}
535
536void close_server (Cody::Server *server, int epoll_fd)
537{
538  my_epoll_ctl (epoll_fd, EPOLL_CTL_DEL, EPOLLIN, server->GetFDRead (), 0);
539
540  close (server->GetFDRead ());
541
542  delete server;
543}
544
545int open_server (bool ip6, int sock_fd)
546{
547  sockaddr_in6 addr;
548  socklen_t addr_len = sizeof (addr);
549
550#ifdef HAVE_ACCEPT4
551  int client_fd = accept4 (sock_fd, ip6 ? (sockaddr *)&addr : nullptr,
552			   &addr_len, SOCK_NONBLOCK);
553#else
554  int client_fd = accept (sock_fd, ip6 ? (sockaddr *)&addr : nullptr, &addr_len);
555#endif
556  if (client_fd < 0)
557    {
558      error ("cannot accept: %s", xstrerror (errno));
559      flag_one = true;
560    }
561  else if (ip6)
562    {
563      const char *str = NULL;
564#if HAVE_INET_NTOP
565      char name[INET6_ADDRSTRLEN];
566      str = inet_ntop (addr.sin6_family, &addr.sin6_addr, name, sizeof (name));
567#endif
568      if (!accept_addrs.empty ())
569	{
570	  netmask_vec_t::iterator e = accept_addrs.end ();
571	  for (netmask_vec_t::iterator i = accept_addrs.begin ();
572	       i != e; ++i)
573	    if (i->includes (addr.sin6_addr))
574	      goto present;
575	  close (client_fd);
576	  client_fd = -1;
577	  noisy ("Rejecting connection from disallowed source '%s'",
578		 str ? str : "");
579	present:;
580	}
581      if (client_fd >= 0)
582	flag_noisy && noisy ("Accepting connection from '%s'", str ? str : "");
583    }
584
585  return client_fd;
586}
587
588/* A server listening on bound socket SOCK_FD.  */
589
590static void
591server (bool ipv6, int sock_fd, module_resolver *resolver)
592{
593  int epoll_fd = -1;
594
595  signal (SIGTERM, term_signal);
596#ifdef HAVE_EPOLL
597  epoll_fd = epoll_create (1);
598#endif
599  if (epoll_fd >= 0)
600    my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, sock_fd, 0);
601
602#if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)
603  sigset_t mask;
604  {
605    sigset_t block;
606    sigemptyset (&block);
607    sigaddset (&block, SIGTERM);
608    sigprocmask (SIG_BLOCK, &block, &mask);
609  }
610#endif
611
612#ifdef HAVE_EPOLL
613  const unsigned max_events = 20;
614  epoll_event events[max_events];
615#endif
616#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
617  fd_set readers, writers;
618#endif
619  if (term_pipe)
620    pipe (term_pipe);
621
622  // We need stable references to servers, so this array can contain nulls
623  std::vector<Cody::Server *> connections;
624  unsigned live = 0;
625  while (sock_fd >= 0 || live)
626    {
627      /* Wait for one or more events.  */
628      bool eintr = false;
629      int event_count;
630
631      if (epoll_fd >= 0)
632	{
633#ifdef HAVE_EPOLL
634	  event_count = epoll_pwait (epoll_fd, events, max_events, -1, &mask);
635#endif
636	}
637      else
638	{
639#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
640	  FD_ZERO (&readers);
641	  FD_ZERO (&writers);
642
643	  unsigned limit = 0;
644	  if (sock_fd >= 0
645	      && !(term || (live && (flag_one || flag_sequential))))
646	    {
647	      FD_SET (sock_fd, &readers);
648	      limit = sock_fd + 1;
649	    }
650
651	  if (term_pipe && term_pipe[0] >= 0)
652	    {
653	      FD_SET (term_pipe[0], &readers);
654	      if (unsigned (term_pipe[0]) >= limit)
655		limit = term_pipe[0] + 1;
656	    }
657
658	  for (auto iter = connections.begin ();
659	       iter != connections.end (); ++iter)
660	    if (auto *server = *iter)
661	      {
662		int fd = -1;
663		switch (server->GetDirection ())
664		  {
665		  case Cody::Server::READING:
666		    fd = server->GetFDRead ();
667		    FD_SET (fd, &readers);
668		    break;
669		  case Cody::Server::WRITING:
670		    fd = server->GetFDWrite ();
671		    FD_SET (fd, &writers);
672		    break;
673		  default:
674		    break;
675		  }
676
677		if (fd >= 0 && limit <= unsigned (fd))
678		  limit = fd + 1;
679	      }
680
681#ifdef HAVE_PSELECT
682	  event_count = pselect (limit, &readers, &writers, NULL, NULL, &mask);
683#else
684	  event_count = select (limit, &readers, &writers, NULL, NULL);
685#endif
686	  if (term_pipe && FD_ISSET (term_pipe[0], &readers))
687	    {
688	      /* Fake up an interrupted system call.  */
689	      event_count = -1;
690	      errno = EINTR;
691	    }
692#endif
693	}
694
695      if (event_count < 0)
696	{
697	  // Error in waiting
698	  if (errno == EINTR)
699	    {
700	      flag_noisy && noisy ("Interrupted wait");
701	      eintr = true;
702	    }
703	  else
704	    error ("cannot %s: %s", epoll_fd >= 0 ? "epoll_wait"
705#ifdef HAVE_PSELECT
706		   : "pselect",
707#else
708		   : "select",
709#endif
710		   xstrerror (errno));
711	  event_count = 0;
712	}
713
714      auto iter = connections.begin ();
715      while (event_count--)
716	{
717	  // Process an event
718	  int active = -2;
719
720	  if (epoll_fd >= 0)
721	    {
722#ifdef HAVE_EPOLL
723	      /* See PR c++/88664 for why a temporary is used.  */
724	      unsigned data = events[event_count].data.u32;
725	      active = int (data) - 1;
726#endif
727	    }
728	  else
729	    {
730	      for (; iter != connections.end (); ++iter)
731		if (auto *server = *iter)
732		  {
733		    bool found = false;
734		    switch (server->GetDirection ())
735		      {
736#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
737		      case Cody::Server::READING:
738			found = FD_ISSET (server->GetFDRead (), &readers);
739			break;
740		      case Cody::Server::WRITING:
741			found = FD_ISSET (server->GetFDWrite (), &writers);
742			break;
743#endif
744		      default:
745			break;
746		      }
747
748		    if (found)
749		      {
750			active = iter - connections.begin ();
751			++iter;
752			break;
753		      }
754		  }
755
756	      if (active < 0 && sock_fd >= 0 && FD_ISSET (sock_fd, &readers))
757		active = -1;
758	    }
759
760	  if (active >= 0)
761	    {
762	      // Do the action
763	      auto *server = connections[active];
764	      if (process_server (server, active, epoll_fd))
765		{
766		  connections[active] = nullptr;
767		  close_server (server, epoll_fd);
768		  live--;
769		  if (flag_sequential)
770		    my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, sock_fd, 0);
771		}
772	    }
773	  else if (active == -1 && !eintr)
774	    {
775	      // New connection
776	      int fd = open_server (ipv6, sock_fd);
777	      if (fd >= 0)
778		{
779#if !defined (HAVE_ACCEPT4) \
780  && (defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT))
781		  int flags = fcntl (fd, F_GETFL, 0);
782		  fcntl (fd, F_SETFL, flags | O_NONBLOCK);
783#endif
784		  auto *server = new Cody::Server (resolver, fd);
785
786		  unsigned slot = connections.size ();
787		  if (live == slot)
788		    connections.push_back (server);
789		  else
790		    for (auto iter = connections.begin (); ; ++iter)
791		      if (!*iter)
792			{
793			  *iter = server;
794			  slot = iter - connections.begin ();
795			  break;
796			}
797		  live++;
798		  my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, fd, slot + 1);
799		}
800	    }
801
802	  if (sock_fd >= 0
803	      && (term || (live && (flag_one || flag_sequential))))
804	    {
805	      /* Stop paying attention to sock_fd.  */
806	      my_epoll_ctl (epoll_fd, EPOLL_CTL_DEL, EPOLLIN, sock_fd, 0);
807	      if (flag_one || term)
808		{
809		  close (sock_fd);
810		  sock_fd = -1;
811		}
812	    }
813	}
814    }
815#if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)
816  /* Restore the signal mask.  */
817  sigprocmask (SIG_SETMASK, &mask, NULL);
818#endif
819
820  gcc_assert (sock_fd < 0);
821  if (epoll_fd >= 0)
822    close (epoll_fd);
823
824  if (term_pipe && term_pipe[0] >= 0)
825    {
826      close (term_pipe[0]);
827      close (term_pipe[1]);
828    }
829}
830
831#endif
832
833static int maybe_parse_socket (std::string &option, module_resolver *r)
834{
835  /* Local or ipv6 address.  */
836  auto last = option.find_last_of ('?');
837  if (last != option.npos)
838    {
839      r->set_ident (option.c_str () + last + 1);
840      option.erase (last);
841    }
842  int fd = -2;
843  char const *errmsg = nullptr;
844
845  /* Does it look like a socket?  */
846  if (option[0] == '=')
847    {
848      /* A local socket.  */
849#if CODY_NETWORKING
850      fd = Cody::ListenLocal (&errmsg, option.c_str () + 1);
851#endif
852    }
853  else
854    {
855      auto colon = option.find_last_of (':');
856      if (colon != option.npos)
857	{
858	  /* Try a hostname:port address.  */
859	  char const *cptr = option.c_str () + colon;
860	  char *endp;
861	  unsigned port = strtoul (cptr + 1, &endp, 10);
862
863	  if (port && endp != cptr + 1 && !*endp)
864	    {
865	      /* Ends in ':number', treat as ipv6 domain socket.  */
866	      option.erase (colon);
867#if CODY_NETWORKING
868	      fd = Cody::ListenInet6 (&errmsg, option.c_str (), port);
869#endif
870	    }
871	}
872    }
873
874  if (errmsg)
875    error ("failed to open socket: %s", errmsg);
876
877  return fd;
878}
879
880int
881main (int argc, char *argv[])
882{
883  const char *p = argv[0] + strlen (argv[0]);
884  while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
885    --p;
886  progname = p;
887
888#ifdef SIGSEGV
889  signal (SIGSEGV, crash_signal);
890#endif
891#ifdef SIGILL
892  signal (SIGILL, crash_signal);
893#endif
894#ifdef SIGBUS
895  signal (SIGBUS, crash_signal);
896#endif
897#ifdef SIGABRT
898  signal (SIGABRT, crash_signal);
899#endif
900#ifdef SIGFPE
901  signal (SIGFPE, crash_signal);
902#endif
903#ifdef SIGPIPE
904  /* Ignore sigpipe, so read/write get an error.  */
905  signal (SIGPIPE, SIG_IGN);
906#endif
907#if NETWORKING
908#ifdef SIGINT
909  signal (SIGINT, kill_signal);
910#endif
911#endif
912
913  int argno = process_args (argc, argv);
914
915  std::string name;
916  int sock_fd = -1; /* Socket fd, otherwise stdin/stdout.  */
917  module_resolver r (flag_map, flag_xlate);
918
919  if (argno != argc)
920    {
921      name = argv[argno];
922      sock_fd = maybe_parse_socket (name, &r);
923      if (!name.empty ())
924	argno++;
925    }
926
927  if (argno != argc)
928    for (; argno != argc; argno++)
929      {
930	std::string option = argv[argno];
931	char const *prefix = nullptr;
932	auto ident = option.find_last_of ('?');
933	if (ident != option.npos)
934	  {
935	    prefix = option.c_str () + ident + 1;
936	    option[ident] = 0;
937	  }
938	int fd = open (option.c_str (), O_RDONLY | O_CLOEXEC);
939	int err = 0;
940	if (fd < 0)
941	  err = errno;
942	else
943	  {
944	    err = r.read_tuple_file (fd, prefix, false);
945	    close (fd);
946	  }
947
948	if (err)
949	  error ("failed reading '%s': %s", option.c_str (), xstrerror (err));
950      }
951  else
952    r.set_default_map (true);
953
954  if (flag_root)
955    r.set_repo (flag_root);
956
957#ifdef HAVE_AF_INET6
958  netmask_set_t::iterator end = netmask_set.end ();
959  for (netmask_set_t::iterator iter = netmask_set.begin ();
960       iter != end; ++iter)
961    {
962      netmask_vec_t::iterator e = accept_addrs.end ();
963      for (netmask_vec_t::iterator i = accept_addrs.begin (); i != e; ++i)
964	if (i->includes (iter->addr))
965	  goto present;
966      accept_addrs.push_back (*iter);
967    present:;
968    }
969#endif
970
971#if NETWORKING
972  if (sock_fd >= 0)
973    {
974      server (name[0] != '=', sock_fd, &r);
975      if (name[0] == '=')
976	unlink (name.c_str () + 1);
977    }
978  else
979#endif
980    {
981      auto server = Cody::Server (&r, 0, 1);
982
983      int err = 0;
984      for (;;)
985	{
986	  server.PrepareToRead ();
987	  while ((err = server.Read ()))
988	    {
989	      if (err == EINTR || err == EAGAIN)
990		continue;
991	      goto done;
992	    }
993
994	  server.ProcessRequests ();
995
996	  server.PrepareToWrite ();
997	  while ((err = server.Write ()))
998	    {
999	      if (err == EINTR || err == EAGAIN)
1000		continue;
1001	      goto done;
1002	    }
1003	}
1004    done:;
1005      if (err > 0)
1006	error ("communication error:%s", xstrerror (err));
1007    }
1008
1009  return 0;
1010}
1011