Fix compilation of server.cc on hpux.
[official-gcc.git] / c++tools / server.cc
blob79905eca9accc45928cca4859ff7e5183fb0e7cb
1 /* C++ modules. Experimental!
2 Copyright (C) 2018-2023 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-2023 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 /* 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))
369 noisy ("cannot resolve '%s': %s", arg, gai_strerror (e));
370 ok = false;
372 else
373 for (addrinfo *next = addrs; next; next = next->ai_next)
374 if (next->ai_family == AF_INET6)
376 netmask mask (((const sockaddr_in6 *)next->ai_addr)->sin6_addr, bits);
377 netmask_set.insert (mask);
379 freeaddrinfo (addrs);
380 #endif
381 return ok;
384 /* Process args, return index to first non-arg. */
386 static int
387 process_args (int argc, char **argv)
389 static const struct option options[] =
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 }
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)
407 switch (opt)
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. */
444 if (bad_accept)
445 error ("failed to resolve all accept addresses");
447 return optind;
450 #if NETWORKING
452 /* Manipulate the EPOLL state, or do nothing, if there is epoll. */
454 #ifdef HAVE_EPOLL
455 static inline void
456 do_epoll_ctl (int epoll_fd, int code, int event, int fd, unsigned data)
458 epoll_event ev;
459 ev.events = event;
460 ev.data.u32 = data;
461 if (epoll_ctl (epoll_fd, code, fd, &ev))
463 noisy ("epoll_ctl error:%s", xstrerror (errno));
464 gcc_unreachable ();
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
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
482 /* A terminate signal. Shutdown gracefully. */
484 static void
485 term_signal (int sig)
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);
493 /* A kill signal. Shutdown immediately. */
495 static void
496 kill_signal (int sig)
498 signal (sig, SIG_DFL);
499 int sock_fd = kill_sock_fd;
500 if (sock_fd >= 0)
501 close (sock_fd);
502 exit (2);
505 bool process_server (Cody::Server *server, unsigned slot, int epoll_fd)
507 switch (server->GetDirection ())
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;
516 case Cody::Server::WRITING:
517 if (int err = server->Write ())
518 return !(err == EINTR || err == EAGAIN);
519 server->PrepareToRead ();
520 break;
522 default:
523 // We should never get here
524 return true;
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);
533 return false;
536 void close_server (Cody::Server *server, int epoll_fd)
538 my_epoll_ctl (epoll_fd, EPOLL_CTL_DEL, EPOLLIN, server->GetFDRead (), 0);
540 close (server->GetFDRead ());
542 delete server;
545 int open_server (bool ip6, int sock_fd)
547 sockaddr_in6 addr;
548 socklen_t addr_len = sizeof (addr);
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)
558 error ("cannot accept: %s", xstrerror (errno));
559 flag_one = true;
561 else if (ip6)
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 ())
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:;
581 if (client_fd >= 0)
582 flag_noisy && noisy ("Accepting connection from '%s'", str ? str : "");
585 return client_fd;
588 /* A server listening on bound socket SOCK_FD. */
590 static void
591 server (bool ipv6, int sock_fd, module_resolver *resolver)
593 int epoll_fd = -1;
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);
602 #if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)
603 sigset_t mask;
605 sigset_t block;
606 sigemptyset (&block);
607 sigaddset (&block, SIGTERM);
608 sigprocmask (SIG_BLOCK, &block, &mask);
610 #endif
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);
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)
627 /* Wait for one or more events. */
628 bool eintr = false;
629 int event_count;
631 if (epoll_fd >= 0)
633 #ifdef HAVE_EPOLL
634 event_count = epoll_pwait (epoll_fd, events, max_events, -1, &mask);
635 #endif
637 else
639 #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
640 FD_ZERO (&readers);
641 FD_ZERO (&writers);
643 unsigned limit = 0;
644 if (sock_fd >= 0
645 && !(term || (live && (flag_one || flag_sequential))))
647 FD_SET (sock_fd, &readers);
648 limit = sock_fd + 1;
651 if (term_pipe && term_pipe[0] >= 0)
653 FD_SET (term_pipe[0], &readers);
654 if (unsigned (term_pipe[0]) >= limit)
655 limit = term_pipe[0] + 1;
658 for (auto iter = connections.begin ();
659 iter != connections.end (); ++iter)
660 if (auto *server = *iter)
662 int fd = -1;
663 switch (server->GetDirection ())
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;
677 if (fd >= 0 && limit <= unsigned (fd))
678 limit = fd + 1;
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))
688 /* Fake up an interrupted system call. */
689 event_count = -1;
690 errno = EINTR;
692 #endif
695 if (event_count < 0)
697 // Error in waiting
698 if (errno == EINTR)
700 flag_noisy && noisy ("Interrupted wait");
701 eintr = true;
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;
714 auto iter = connections.begin ();
715 while (event_count--)
717 // Process an event
718 int active = -2;
720 if (epoll_fd >= 0)
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
728 else
730 for (; iter != connections.end (); ++iter)
731 if (auto *server = *iter)
733 bool found = false;
734 switch (server->GetDirection ())
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;
748 if (found)
750 active = iter - connections.begin ();
751 ++iter;
752 break;
756 #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
757 if (active < 0 && sock_fd >= 0 && FD_ISSET (sock_fd, &readers))
758 active = -1;
759 #endif
762 if (active >= 0)
764 // Do the action
765 auto *server = connections[active];
766 if (process_server (server, active, epoll_fd))
768 connections[active] = nullptr;
769 close_server (server, epoll_fd);
770 live--;
771 if (flag_sequential)
772 my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, sock_fd, 0);
775 else if (active == -1 && !eintr)
777 // New connection
778 int fd = open_server (ipv6, sock_fd);
779 if (fd >= 0)
781 #if !defined (HAVE_ACCEPT4) \
782 && (defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT))
783 int flags = fcntl (fd, F_GETFL, 0);
784 fcntl (fd, F_SETFL, flags | O_NONBLOCK);
785 #endif
786 auto *server = new Cody::Server (resolver, fd);
788 unsigned slot = connections.size ();
789 if (live == slot)
790 connections.push_back (server);
791 else
792 for (auto iter = connections.begin (); ; ++iter)
793 if (!*iter)
795 *iter = server;
796 slot = iter - connections.begin ();
797 break;
799 live++;
800 my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, fd, slot + 1);
804 if (sock_fd >= 0
805 && (term || (live && (flag_one || flag_sequential))))
807 /* Stop paying attention to sock_fd. */
808 my_epoll_ctl (epoll_fd, EPOLL_CTL_DEL, EPOLLIN, sock_fd, 0);
809 if (flag_one || term)
811 close (sock_fd);
812 sock_fd = -1;
817 #if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)
818 /* Restore the signal mask. */
819 sigprocmask (SIG_SETMASK, &mask, NULL);
820 #endif
822 gcc_assert (sock_fd < 0);
823 if (epoll_fd >= 0)
824 close (epoll_fd);
826 if (term_pipe && term_pipe[0] >= 0)
828 close (term_pipe[0]);
829 close (term_pipe[1]);
833 #endif
835 static int maybe_parse_socket (std::string &option, module_resolver *r)
837 /* Local or ipv6 address. */
838 auto last = option.find_last_of ('?');
839 if (last != option.npos)
841 r->set_ident (option.c_str () + last + 1);
842 option.erase (last);
844 int fd = -2;
845 char const *errmsg = nullptr;
847 /* Does it look like a socket? */
848 if (option[0] == '=')
850 /* A local socket. */
851 #if CODY_NETWORKING
852 fd = Cody::ListenLocal (&errmsg, option.c_str () + 1);
853 #endif
855 else
857 auto colon = option.find_last_of (':');
858 if (colon != option.npos)
860 /* Try a hostname:port address. */
861 char const *cptr = option.c_str () + colon;
862 char *endp;
863 unsigned port = strtoul (cptr + 1, &endp, 10);
865 if (port && endp != cptr + 1 && !*endp)
867 /* Ends in ':number', treat as ipv6 domain socket. */
868 option.erase (colon);
869 #if CODY_NETWORKING
870 fd = Cody::ListenInet6 (&errmsg, option.c_str (), port);
871 #endif
876 if (errmsg)
877 error ("failed to open socket: %s", errmsg);
879 return fd;
883 main (int argc, char *argv[])
885 const char *p = argv[0] + strlen (argv[0]);
886 while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
887 --p;
888 progname = p;
890 #ifdef SIGSEGV
891 signal (SIGSEGV, crash_signal);
892 #endif
893 #ifdef SIGILL
894 signal (SIGILL, crash_signal);
895 #endif
896 #ifdef SIGBUS
897 signal (SIGBUS, crash_signal);
898 #endif
899 #ifdef SIGABRT
900 signal (SIGABRT, crash_signal);
901 #endif
902 #ifdef SIGFPE
903 signal (SIGFPE, crash_signal);
904 #endif
905 #ifdef SIGPIPE
906 /* Ignore sigpipe, so read/write get an error. */
907 signal (SIGPIPE, SIG_IGN);
908 #endif
909 #if NETWORKING
910 #ifdef SIGINT
911 signal (SIGINT, kill_signal);
912 #endif
913 #endif
915 int argno = process_args (argc, argv);
917 std::string name;
918 int sock_fd = -1; /* Socket fd, otherwise stdin/stdout. */
919 module_resolver r (flag_map, flag_xlate);
921 if (argno != argc)
923 name = argv[argno];
924 sock_fd = maybe_parse_socket (name, &r);
925 if (!name.empty ())
926 argno++;
929 if (argno != argc)
930 for (; argno != argc; argno++)
932 std::string option = argv[argno];
933 char const *prefix = nullptr;
934 auto ident = option.find_last_of ('?');
935 if (ident != option.npos)
937 prefix = option.c_str () + ident + 1;
938 option[ident] = 0;
940 int fd = open (option.c_str (), O_RDONLY | O_CLOEXEC);
941 int err = 0;
942 if (fd < 0)
943 err = errno;
944 else
946 err = r.read_tuple_file (fd, prefix, false);
947 close (fd);
950 if (err)
951 error ("failed reading '%s': %s", option.c_str (), xstrerror (err));
953 else
954 r.set_default_map (true);
956 if (flag_root)
957 r.set_repo (flag_root);
959 #ifdef HAVE_AF_INET6
960 netmask_set_t::iterator end = netmask_set.end ();
961 for (netmask_set_t::iterator iter = netmask_set.begin ();
962 iter != end; ++iter)
964 netmask_vec_t::iterator e = accept_addrs.end ();
965 for (netmask_vec_t::iterator i = accept_addrs.begin (); i != e; ++i)
966 if (i->includes (iter->addr))
967 goto present;
968 accept_addrs.push_back (*iter);
969 present:;
971 #endif
973 #if NETWORKING
974 if (sock_fd >= 0)
976 server (name[0] != '=', sock_fd, &r);
977 if (name[0] == '=')
978 unlink (name.c_str () + 1);
980 else
981 #endif
983 auto server = Cody::Server (&r, 0, 1);
985 int err = 0;
986 for (;;)
988 server.PrepareToRead ();
989 while ((err = server.Read ()))
991 if (err == EINTR || err == EAGAIN)
992 continue;
993 goto done;
996 server.ProcessRequests ();
998 server.PrepareToWrite ();
999 while ((err = server.Write ()))
1001 if (err == EINTR || err == EAGAIN)
1002 continue;
1003 goto done;
1006 done:;
1007 if (err > 0)
1008 error ("communication error:%s", xstrerror (err));
1011 return 0;