xref: /netbsd-src/external/gpl3/gcc/dist/c++tools/server.cc (revision b1e838363e3c6fc78a55519254d99869742dd33c)
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 
17 You should have received a copy of the GNU General Public License
18 along 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
122 struct netmask {
123   in6_addr addr;
124   unsigned bits;
125 
netmasknetmask126   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 
includesnetmask141   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.  */
155 struct netmask_cmp {
operator ()netmask_cmp156   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 
167 typedef std::set<netmask, netmask_cmp> netmask_set_t;
168 typedef std::vector<netmask> netmask_vec_t;
169 #endif
170 
171 const char *progname;
172 
173 /* Speak thoughts out loud.  */
174 static bool flag_noisy = false;
175 
176 /* One and done.  */
177 static bool flag_one = false;
178 
179 /* Serialize connections.  */
180 static bool flag_sequential = false;
181 
182 /* Fallback to default if map file is unrewarding.  */
183 static bool flag_map = false;
184 
185 /* Fallback to xlate if map file is unrewarding.  */
186 static bool flag_xlate = false;
187 
188 /* Root binary directory.  */
189 static const char *flag_root = "gcm.cache";
190 
191 #if NETWORKING
192 static netmask_set_t netmask_set;
193 
194 static netmask_vec_t accept_addrs;
195 #endif
196 
197 /* Strip out the source directory from FILE.  */
198 
199 static const char *
trim_src_file(const char * file)200 trim_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 
215 void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD
internal_error(const char * fmt,...)216 internal_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
232 void ATTRIBUTE_NORETURN ATTRIBUTE_COLD
fancy_abort(const char * file,int line,const char * func)233 fancy_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 
241 static void ATTRIBUTE_NORETURN ATTRIBUTE_COLD
crash_signal(int sig)242 crash_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 
251 static void ATTRIBUTE_NORETURN ATTRIBUTE_COLD ATTRIBUTE_PRINTF_1
error(const char * msg,...)252 error (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.  */
267 static bool ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD
noisy(const char * fmt,...)268 noisy (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 
283 static void ATTRIBUTE_PRINTF_2
fnotice(FILE * file,const char * fmt,...)284 fnotice (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 
293 static void ATTRIBUTE_NORETURN
print_usage(int error_p)294 print_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 
318 static void ATTRIBUTE_NORETURN
print_version(void)319 print_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 
334 static bool
accept_from(char * arg ATTRIBUTE_UNUSED)335 accept_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 
386 static int
process_args(int argc,char ** argv)387 process_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
455 static inline void
do_epoll_ctl(int epoll_fd,int code,int event,int fd,unsigned data)456 do_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.  */
474 static volatile int term = false;
475 static volatile int kill_sock_fd = -1;
476 #if !defined (HAVE_PSELECT) && defined (HAVE_SELECT)
477 static int term_pipe[2] = {-1, -1};
478 #else
479 #define term_pipe ((int *)NULL)
480 #endif
481 
482 /* A terminate signal.  Shutdown gracefully.  */
483 
484 static void
term_signal(int sig)485 term_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 
495 static void
kill_signal(int sig)496 kill_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 
process_server(Cody::Server * server,unsigned slot,int epoll_fd)505 bool 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 
close_server(Cody::Server * server,int epoll_fd)536 void 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 
open_server(bool ip6,int sock_fd)545 int 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 
590 static void
server(bool ipv6,int sock_fd,module_resolver * resolver)591 server (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 
maybe_parse_socket(std::string & option,module_resolver * r)833 static 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 
880 int
main(int argc,char * argv[])881 main (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