Daily bump.
[official-gcc.git] / c++tools / server.cc
bloba60eefe63b0c4ca2070f9819c97465af521da061
1 /* C++ modules. Experimental!
2 Copyright (C) 2018-2021 Free Software Foundation, Inc.
3 Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
5 This file is part of GCC.
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.
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.
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/>. */
21 #include "config.h"
22 #include "resolver.h"
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>
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
64 #ifndef AI_NUMERICSERV
65 #define AI_NUMERICSERV 0
66 #endif
68 #include <getopt.h>
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
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"
88 #if !HOST_HAS_O_CLOEXEC
89 #define O_CLOEXEC 0
90 #endif
92 #ifndef IS_DIR_SEPARATOR
93 #define IS_DIR_SEPARATOR(C) ((C) == '/')
94 #endif
95 #ifndef DIR_SEPARATOR
96 #define DIR_SEPARATOR '/'
97 #endif
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
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
121 #if NETWORKING
122 struct netmask {
123 in6_addr addr;
124 unsigned bits;
126 netmask (const in6_addr &a, unsigned b)
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);
141 bool includes (const in6_addr &a) const
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;
154 /* Netmask comparison. */
155 struct netmask_cmp {
156 bool operator() (const netmask &a, const netmask &b) const
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;
167 typedef std::set<netmask, netmask_cmp> netmask_set_t;
168 typedef std::vector<netmask> netmask_vec_t;
169 #endif
171 const char *progname;
173 /* Speak thoughts out loud. */
174 static bool flag_noisy = false;
176 /* One and done. */
177 static bool flag_one = false;
179 /* Serialize connections. */
180 static bool flag_sequential = false;
182 /* Fallback to default if map file is unrewarding. */
183 static bool flag_map = false;
185 /* Fallback to xlate if map file is unrewarding. */
186 static bool flag_xlate = false;
188 /* Root binary directory. */
189 static const char *flag_root = "gcm.cache";
191 #if NETWORKING
192 static netmask_set_t netmask_set;
194 static netmask_vec_t accept_addrs;
195 #endif
197 /* Strip out the source directory from FILE. */
199 static const char *
200 trim_src_file (const char *file)
202 static const char me[] = __FILE__;
203 unsigned pos = 0;
205 while (file[pos] == me[pos] && me[pos])
206 pos++;
207 while (pos && !IS_DIR_SEPARATOR (me[pos-1]))
208 pos--;
210 return file + pos;
213 /* Die screaming. */
215 void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD
216 internal_error (const char *fmt, ...)
218 fprintf (stderr, "%s:Internal error ", progname);
219 va_list args;
221 va_start (args, fmt);
222 vfprintf (stderr, fmt, args);
223 va_end (args);
224 fprintf (stderr, "\n");
226 exit (2);
229 /* Hooked to from gcc_assert & gcc_unreachable. */
231 #if ENABLE_ASSERT_CHECKING
232 void ATTRIBUTE_NORETURN ATTRIBUTE_COLD
233 fancy_abort (const char *file, int line, const char *func)
235 internal_error ("in %s, at %s:%d", func, trim_src_file (file), line);
237 #endif
239 /* Exploded on a signal. */
241 static void ATTRIBUTE_NORETURN ATTRIBUTE_COLD
242 crash_signal (int sig)
244 signal (sig, SIG_DFL);
245 // strsignal is not portable :(
246 internal_error ("signal %d", sig);
249 /* A fatal error of some kind. */
251 static void ATTRIBUTE_NORETURN ATTRIBUTE_COLD ATTRIBUTE_PRINTF_1
252 error (const char *msg, ...)
254 fprintf (stderr, "%s:error: ", progname);
255 va_list args;
257 va_start (args, msg);
258 vfprintf (stderr, msg, args);
259 va_end (args);
260 fprintf (stderr, "\n");
262 exit (1);
265 #if NETWORKING
266 /* Progress messages to the user. */
267 static bool ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD
268 noisy (const char *fmt, ...)
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");
277 return false;
279 #endif
281 /* More messages to the user. */
283 static void ATTRIBUTE_PRINTF_2
284 fnotice (FILE *file, const char *fmt, ...)
286 va_list args;
288 va_start (args, fmt);
289 vfprintf (file, fmt, args);
290 va_end (args);
293 static void ATTRIBUTE_NORETURN
294 print_usage (int error_p)
296 FILE *file = error_p ? stderr : stdout;
297 int status = error_p ? 1 : 0;
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);
316 /* Print version information and exit. */
318 static void ATTRIBUTE_NORETURN
319 print_version (void)
321 fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string);
322 fprintf (stdout, "Copyright %s 2018-2021 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);
331 /* ARG is a netmask to accept from. Add it to the table. Return
332 false if we fail to resolve it. */
334 static bool
335 accept_from (char *arg ATTRIBUTE_UNUSED)
337 bool ok = true;
338 #if HAVE_AF_INET6
339 unsigned bits = sizeof (in6_addr) * 8;
340 char *slash = strrchr (arg, '/');
341 if (slash)
343 *slash = 0;
344 if (slash[1])
346 char *endp;
347 bits = strtoul (slash + 1, &endp, 0);
351 addrinfo hints;
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;
362 struct addrinfo *addrs = NULL;
363 if (int e = getaddrinfo (slash == arg ? NULL : arg, "0", &hints, &addrs))
365 noisy ("cannot resolve '%s': %s", arg, gai_strerror (e));
366 ok = false;
368 else
369 for (addrinfo *next = addrs; next; next = next->ai_next)
370 if (next->ai_family == AF_INET6)
372 netmask mask (((const sockaddr_in6 *)next->ai_addr)->sin6_addr, bits);
373 netmask_set.insert (mask);
375 freeaddrinfo (addrs);
376 #endif
377 return ok;
380 /* Process args, return index to first non-arg. */
382 static int
383 process_args (int argc, char **argv)
385 static const struct option options[] =
387 { "accept", required_argument, NULL, 'a' },
388 { "help", no_argument, NULL, 'h' },
389 { "map", no_argument, NULL, 'm' },
390 { "noisy", no_argument, NULL, 'n' },
391 { "one", no_argument, NULL, '1' },
392 { "root", required_argument, NULL, 'r' },
393 { "sequential", no_argument, NULL, 's' },
394 { "translate",no_argument, NULL, 't' },
395 { "version", no_argument, NULL, 'v' },
396 { 0, 0, 0, 0 }
398 int opt;
399 bool bad_accept = false;
400 const char *opts = "a:fhmn1r:stv";
401 while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1)
403 switch (opt)
405 case 'a':
406 if (!accept_from (optarg))
407 bad_accept = true;
408 break;
409 case 'h':
410 print_usage (false);
411 /* print_usage will exit. */
412 case 'f': // deprecated alias
413 case 'm':
414 flag_map = true;
415 break;
416 case 'n':
417 flag_noisy = true;
418 break;
419 case '1':
420 flag_one = true;
421 break;
422 case 'r':
423 flag_root = optarg;
424 break;
425 case 's':
426 flag_sequential = true;
427 break;
428 case 't':
429 flag_xlate = true;
430 break;
431 case 'v':
432 print_version ();
433 /* print_version will exit. */
434 default:
435 print_usage (true);
436 /* print_usage will exit. */
440 if (bad_accept)
441 error ("failed to resolve all accept addresses");
443 return optind;
446 #if NETWORKING
448 /* Manipulate the EPOLL state, or do nothing, if there is epoll. */
450 #ifdef HAVE_EPOLL
451 static inline void
452 do_epoll_ctl (int epoll_fd, int code, int event, int fd, unsigned data)
454 epoll_event ev;
455 ev.events = event;
456 ev.data.u32 = data;
457 if (epoll_ctl (epoll_fd, code, fd, &ev))
459 noisy ("epoll_ctl error:%s", xstrerror (errno));
460 gcc_unreachable ();
463 #define my_epoll_ctl(EFD,C,EV,FD,CL) \
464 ((EFD) >= 0 ? do_epoll_ctl (EFD,C,EV,FD,CL) : (void)0)
465 #else
466 #define my_epoll_ctl(EFD,C,EV,FD,CL) ((void)(EFD), (void)(FD), (void)(CL))
467 #endif
469 /* We increment this to tell the server to shut down. */
470 static volatile int term = false;
471 static volatile int kill_sock_fd = -1;
472 #if !defined (HAVE_PSELECT) && defined (HAVE_SELECT)
473 static int term_pipe[2] = {-1, -1};
474 #else
475 #define term_pipe ((int *)NULL)
476 #endif
478 /* A terminate signal. Shutdown gracefully. */
480 static void
481 term_signal (int sig)
483 signal (sig, term_signal);
484 term = term + 1;
485 if (term_pipe && term_pipe[1] >= 0)
486 write (term_pipe[1], &term_pipe[1], 1);
489 /* A kill signal. Shutdown immediately. */
491 static void
492 kill_signal (int sig)
494 signal (sig, SIG_DFL);
495 int sock_fd = kill_sock_fd;
496 if (sock_fd >= 0)
497 close (sock_fd);
498 exit (2);
501 bool process_server (Cody::Server *server, unsigned slot, int epoll_fd)
503 switch (server->GetDirection ())
505 case Cody::Server::READING:
506 if (int err = server->Read ())
507 return !(err == EINTR || err == EAGAIN);
508 server->ProcessRequests ();
509 server->PrepareToWrite ();
510 break;
512 case Cody::Server::WRITING:
513 if (int err = server->Write ())
514 return !(err == EINTR || err == EAGAIN);
515 server->PrepareToRead ();
516 break;
518 default:
519 // We should never get here
520 return true;
523 // We've changed direction, so update epoll
524 gcc_assert (server->GetFDRead () == server->GetFDWrite ());
525 my_epoll_ctl (epoll_fd, EPOLL_CTL_MOD,
526 server->GetDirection () == Cody::Server::READING
527 ? EPOLLIN : EPOLLOUT, server->GetFDRead (), slot + 1);
529 return false;
532 void close_server (Cody::Server *server, int epoll_fd)
534 my_epoll_ctl (epoll_fd, EPOLL_CTL_DEL, EPOLLIN, server->GetFDRead (), 0);
536 close (server->GetFDRead ());
538 delete server;
541 int open_server (bool ip6, int sock_fd)
543 sockaddr_in6 addr;
544 socklen_t addr_len = sizeof (addr);
546 #ifdef HAVE_ACCEPT4
547 int client_fd = accept4 (sock_fd, ip6 ? (sockaddr *)&addr : nullptr,
548 &addr_len, SOCK_NONBLOCK);
549 #else
550 int client_fd = accept (sock_fd, ip6 ? (sockaddr *)&addr : nullptr, &addr_len);
551 #endif
552 if (client_fd < 0)
554 error ("cannot accept: %s", xstrerror (errno));
555 flag_one = true;
557 else if (ip6)
559 const char *str = NULL;
560 #if HAVE_INET_NTOP
561 char name[INET6_ADDRSTRLEN];
562 str = inet_ntop (addr.sin6_family, &addr.sin6_addr, name, sizeof (name));
563 #endif
564 if (!accept_addrs.empty ())
566 netmask_vec_t::iterator e = accept_addrs.end ();
567 for (netmask_vec_t::iterator i = accept_addrs.begin ();
568 i != e; ++i)
569 if (i->includes (addr.sin6_addr))
570 goto present;
571 close (client_fd);
572 client_fd = -1;
573 noisy ("Rejecting connection from disallowed source '%s'",
574 str ? str : "");
575 present:;
577 if (client_fd >= 0)
578 flag_noisy && noisy ("Accepting connection from '%s'", str ? str : "");
581 return client_fd;
584 /* A server listening on bound socket SOCK_FD. */
586 static void
587 server (bool ipv6, int sock_fd, module_resolver *resolver)
589 int epoll_fd = -1;
591 signal (SIGTERM, term_signal);
592 #ifdef HAVE_EPOLL
593 epoll_fd = epoll_create (1);
594 #endif
595 if (epoll_fd >= 0)
596 my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, sock_fd, 0);
598 #if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)
599 sigset_t mask;
601 sigset_t block;
602 sigemptyset (&block);
603 sigaddset (&block, SIGTERM);
604 sigprocmask (SIG_BLOCK, &block, &mask);
606 #endif
608 #ifdef HAVE_EPOLL
609 const unsigned max_events = 20;
610 epoll_event events[max_events];
611 #endif
612 #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
613 fd_set readers, writers;
614 #endif
615 if (term_pipe)
616 pipe (term_pipe);
618 // We need stable references to servers, so this array can contain nulls
619 std::vector<Cody::Server *> connections;
620 unsigned live = 0;
621 while (sock_fd >= 0 || live)
623 /* Wait for one or more events. */
624 bool eintr = false;
625 int event_count;
627 if (epoll_fd >= 0)
629 #ifdef HAVE_EPOLL
630 event_count = epoll_pwait (epoll_fd, events, max_events, -1, &mask);
631 #endif
633 else
635 #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
636 FD_ZERO (&readers);
637 FD_ZERO (&writers);
639 unsigned limit = 0;
640 if (sock_fd >= 0
641 && !(term || (live && (flag_one || flag_sequential))))
643 FD_SET (sock_fd, &readers);
644 limit = sock_fd + 1;
647 if (term_pipe && term_pipe[0] >= 0)
649 FD_SET (term_pipe[0], &readers);
650 if (unsigned (term_pipe[0]) >= limit)
651 limit = term_pipe[0] + 1;
654 for (auto iter = connections.begin ();
655 iter != connections.end (); ++iter)
656 if (auto *server = *iter)
658 int fd = -1;
659 switch (server->GetDirection ())
661 case Cody::Server::READING:
662 fd = server->GetFDRead ();
663 FD_SET (fd, &readers);
664 break;
665 case Cody::Server::WRITING:
666 fd = server->GetFDWrite ();
667 FD_SET (fd, &writers);
668 break;
669 default:
670 break;
673 if (fd >= 0 && limit <= unsigned (fd))
674 limit = fd + 1;
677 #ifdef HAVE_PSELECT
678 event_count = pselect (limit, &readers, &writers, NULL, NULL, &mask);
679 #else
680 event_count = select (limit, &readers, &writers, NULL, NULL);
681 #endif
682 if (term_pipe && FD_ISSET (term_pipe[0], &readers))
684 /* Fake up an interrupted system call. */
685 event_count = -1;
686 errno = EINTR;
688 #endif
691 if (event_count < 0)
693 // Error in waiting
694 if (errno == EINTR)
696 flag_noisy && noisy ("Interrupted wait");
697 eintr = true;
699 else
700 error ("cannot %s: %s", epoll_fd >= 0 ? "epoll_wait"
701 #ifdef HAVE_PSELECT
702 : "pselect",
703 #else
704 : "select",
705 #endif
706 xstrerror (errno));
707 event_count = 0;
710 auto iter = connections.begin ();
711 while (event_count--)
713 // Process an event
714 int active = -2;
716 if (epoll_fd >= 0)
718 #ifdef HAVE_EPOLL
719 /* See PR c++/88664 for why a temporary is used. */
720 unsigned data = events[event_count].data.u32;
721 active = int (data) - 1;
722 #endif
724 else
726 for (; iter != connections.end (); ++iter)
727 if (auto *server = *iter)
729 bool found = false;
730 switch (server->GetDirection ())
732 #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
733 case Cody::Server::READING:
734 found = FD_ISSET (server->GetFDRead (), &readers);
735 break;
736 case Cody::Server::WRITING:
737 found = FD_ISSET (server->GetFDWrite (), &writers);
738 break;
739 #endif
740 default:
741 break;
744 if (found)
746 active = iter - connections.begin ();
747 ++iter;
748 break;
752 if (active < 0 && sock_fd >= 0 && FD_ISSET (sock_fd, &readers))
753 active = -1;
756 if (active >= 0)
758 // Do the action
759 auto *server = connections[active];
760 if (process_server (server, active, epoll_fd))
762 connections[active] = nullptr;
763 close_server (server, epoll_fd);
764 live--;
765 if (flag_sequential)
766 my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, sock_fd, 0);
769 else if (active == -1 && !eintr)
771 // New connection
772 int fd = open_server (ipv6, sock_fd);
773 if (fd >= 0)
775 #if !defined (HAVE_ACCEPT4) \
776 && (defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT))
777 int flags = fcntl (fd, F_GETFL, 0);
778 fcntl (fd, F_SETFL, flags | O_NONBLOCK);
779 #endif
780 auto *server = new Cody::Server (resolver, fd);
782 unsigned slot = connections.size ();
783 if (live == slot)
784 connections.push_back (server);
785 else
786 for (auto iter = connections.begin (); ; ++iter)
787 if (!*iter)
789 *iter = server;
790 slot = iter - connections.begin ();
791 break;
793 live++;
794 my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, fd, slot + 1);
798 if (sock_fd >= 0
799 && (term || (live && (flag_one || flag_sequential))))
801 /* Stop paying attention to sock_fd. */
802 my_epoll_ctl (epoll_fd, EPOLL_CTL_DEL, EPOLLIN, sock_fd, 0);
803 if (flag_one || term)
805 close (sock_fd);
806 sock_fd = -1;
811 #if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)
812 /* Restore the signal mask. */
813 sigprocmask (SIG_SETMASK, &mask, NULL);
814 #endif
816 gcc_assert (sock_fd < 0);
817 if (epoll_fd >= 0)
818 close (epoll_fd);
820 if (term_pipe && term_pipe[0] >= 0)
822 close (term_pipe[0]);
823 close (term_pipe[1]);
827 #endif
829 static int maybe_parse_socket (std::string &option, module_resolver *r)
831 /* Local or ipv6 address. */
832 auto last = option.find_last_of ('?');
833 if (last != option.npos)
835 r->set_ident (option.c_str () + last + 1);
836 option.erase (last);
838 int fd = -2;
839 char const *errmsg = nullptr;
841 /* Does it look like a socket? */
842 if (option[0] == '=')
844 /* A local socket. */
845 #if CODY_NETWORKING
846 fd = Cody::ListenLocal (&errmsg, option.c_str () + 1);
847 #endif
849 else
851 auto colon = option.find_last_of (':');
852 if (colon != option.npos)
854 /* Try a hostname:port address. */
855 char const *cptr = option.c_str () + colon;
856 char *endp;
857 unsigned port = strtoul (cptr + 1, &endp, 10);
859 if (port && endp != cptr + 1 && !*endp)
861 /* Ends in ':number', treat as ipv6 domain socket. */
862 option.erase (colon);
863 #if CODY_NETWORKING
864 fd = Cody::ListenInet6 (&errmsg, option.c_str (), port);
865 #endif
870 if (errmsg)
871 error ("failed to open socket: %s", errmsg);
873 return fd;
877 main (int argc, char *argv[])
879 const char *p = argv[0] + strlen (argv[0]);
880 while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
881 --p;
882 progname = p;
884 #ifdef SIGSEGV
885 signal (SIGSEGV, crash_signal);
886 #endif
887 #ifdef SIGILL
888 signal (SIGILL, crash_signal);
889 #endif
890 #ifdef SIGBUS
891 signal (SIGBUS, crash_signal);
892 #endif
893 #ifdef SIGABRT
894 signal (SIGABRT, crash_signal);
895 #endif
896 #ifdef SIGFPE
897 signal (SIGFPE, crash_signal);
898 #endif
899 #ifdef SIGPIPE
900 /* Ignore sigpipe, so read/write get an error. */
901 signal (SIGPIPE, SIG_IGN);
902 #endif
903 #if NETWORKING
904 #ifdef SIGINT
905 signal (SIGINT, kill_signal);
906 #endif
907 #endif
909 int argno = process_args (argc, argv);
911 std::string name;
912 int sock_fd = -1; /* Socket fd, otherwise stdin/stdout. */
913 module_resolver r (flag_map, flag_xlate);
915 if (argno != argc)
917 name = argv[argno];
918 sock_fd = maybe_parse_socket (name, &r);
919 if (!name.empty ())
920 argno++;
923 if (argno != argc)
924 for (; argno != argc; argno++)
926 std::string option = argv[argno];
927 char const *prefix = nullptr;
928 auto ident = option.find_last_of ('?');
929 if (ident != option.npos)
931 prefix = option.c_str () + ident + 1;
932 option[ident] = 0;
934 int fd = open (option.c_str (), O_RDONLY | O_CLOEXEC);
935 int err = 0;
936 if (fd < 0)
937 err = errno;
938 else
940 err = r.read_tuple_file (fd, prefix, false);
941 close (fd);
944 if (err)
945 error ("failed reading '%s': %s", option.c_str (), xstrerror (err));
947 else
948 r.set_default_map (true);
950 if (flag_root)
951 r.set_repo (flag_root);
953 #ifdef HAVE_AF_INET6
954 netmask_set_t::iterator end = netmask_set.end ();
955 for (netmask_set_t::iterator iter = netmask_set.begin ();
956 iter != end; ++iter)
958 netmask_vec_t::iterator e = accept_addrs.end ();
959 for (netmask_vec_t::iterator i = accept_addrs.begin (); i != e; ++i)
960 if (i->includes (iter->addr))
961 goto present;
962 accept_addrs.push_back (*iter);
963 present:;
965 #endif
967 #if NETWORKING
968 if (sock_fd >= 0)
970 server (name[0] != '=', sock_fd, &r);
971 if (name[0] == '=')
972 unlink (name.c_str () + 1);
974 else
975 #endif
977 auto server = Cody::Server (&r, 0, 1);
979 int err = 0;
980 for (;;)
982 server.PrepareToRead ();
983 while ((err = server.Read ()))
985 if (err == EINTR || err == EAGAIN)
986 continue;
987 goto done;
990 server.ProcessRequests ();
992 server.PrepareToWrite ();
993 while ((err = server.Write ()))
995 if (err == EINTR || err == EAGAIN)
996 continue;
997 goto done;
1000 done:;
1001 if (err > 0)
1002 error ("communication error:%s", xstrerror (err));
1005 return 0;