12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010 |
- /* C++ modules. Experimental!
- Copyright (C) 2018-2022 Free Software Foundation, Inc.
- Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
- This file is part of GCC.
- GCC is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3, or (at your option)
- any later version.
- GCC is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with GCC; see the file COPYING3. If not see
- <http://www.gnu.org/licenses/>. */
- #include "config.h"
- #include "resolver.h"
- // C++
- #include <set>
- #include <vector>
- #include <map>
- // C
- #include <csignal>
- #include <cstring>
- #include <cstdarg>
- #include <cstdlib>
- // OS
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- // Network
- /* Include network stuff first. Excitingly OSX10.14 uses bcmp here, which
- we poison later! */
- #if defined (HAVE_AF_UNIX) || defined (HAVE_AF_INET6)
- /* socket, bind, listen, accept{4} */
- # define NETWORKING 1
- # include <sys/socket.h>
- # ifdef HAVE_AF_UNIX
- /* sockaddr_un */
- # include <sys/un.h>
- # endif
- # include <netinet/in.h>
- # ifdef HAVE_AF_INET6
- /* sockaddr_in6, getaddrinfo, freeaddrinfo, gai_sterror, ntohs, htons. */
- # include <netdb.h>
- # endif
- #ifdef HAVE_INET_NTOP
- /* inet_ntop. */
- #include <arpa/inet.h>
- #endif
- #endif
- #ifndef HAVE_AF_INET6
- # define gai_strerror(X) ""
- #endif
- #ifndef AI_NUMERICSERV
- #define AI_NUMERICSERV 0
- #endif
- #include <getopt.h>
- // Select or epoll
- #if NETWORKING
- #ifdef HAVE_EPOLL
- /* epoll_create, epoll_ctl, epoll_pwait */
- #include <sys/epoll.h>
- #endif
- #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
- /* pselect or select */
- #include <sys/select.h>
- #endif
- #endif
- // GCC
- #include "version.h"
- #include "ansidecl.h"
- #define HAVE_DECL_BASENAME 1 /* See comment in gcc/configure.ac. */
- #include "libiberty.h"
- #if !HOST_HAS_O_CLOEXEC
- #define O_CLOEXEC 0
- #endif
- #ifndef IS_DIR_SEPARATOR
- #define IS_DIR_SEPARATOR(C) ((C) == '/')
- #endif
- #ifndef DIR_SEPARATOR
- #define DIR_SEPARATOR '/'
- #endif
- /* Imported from libcpp/system.h
- Use gcc_assert(EXPR) to test invariants. */
- #if ENABLE_ASSERT_CHECKING
- #define gcc_assert(EXPR) \
- ((void)(!(EXPR) ? fancy_abort (__FILE__, __LINE__, __FUNCTION__), 0 : 0))
- #elif (GCC_VERSION >= 4005)
- #define gcc_assert(EXPR) \
- ((void)(__builtin_expect (!(EXPR), 0) ? __builtin_unreachable (), 0 : 0))
- #else
- /* Include EXPR, so that unused variable warnings do not occur. */
- #define gcc_assert(EXPR) ((void)(0 && (EXPR)))
- #endif
- /* Use gcc_unreachable() to mark unreachable locations (like an
- unreachable default case of a switch. Do not use gcc_assert(0). */
- #if (GCC_VERSION >= 4005) && !ENABLE_ASSERT_CHECKING
- #define gcc_unreachable() __builtin_unreachable ()
- #else
- #define gcc_unreachable() (fancy_abort (__FILE__, __LINE__, __FUNCTION__))
- #endif
- #if NETWORKING
- struct netmask {
- in6_addr addr;
- unsigned bits;
- netmask (const in6_addr &a, unsigned b)
- {
- if (b > sizeof (in6_addr) * 8)
- b = sizeof (in6_addr) * 8;
- bits = b;
- unsigned byte = (b + 7) / 8;
- unsigned ix = 0;
- for (ix = 0; ix < byte; ix++)
- addr.s6_addr[ix] = a.s6_addr[ix];
- for (; ix != sizeof (in6_addr); ix++)
- addr.s6_addr[ix] = 0;
- if (b & 3)
- addr.s6_addr[b/7] &= (255 << 8) >> (b & 3);
- }
- bool includes (const in6_addr &a) const
- {
- unsigned byte = bits / 8;
- for (unsigned ix = 0; ix != byte; ix++)
- if (addr.s6_addr[ix] != a.s6_addr[ix])
- return false;
- if (bits & 3)
- if ((addr.s6_addr[byte] ^ a.s6_addr[byte]) >> (8 - (bits & 3)))
- return false;
- return true;
- }
- };
- /* Netmask comparison. */
- struct netmask_cmp {
- bool operator() (const netmask &a, const netmask &b) const
- {
- if (a.bits != b.bits)
- return a.bits < b.bits;
- for (unsigned ix = 0; ix != sizeof (in6_addr); ix++)
- if (a.addr.s6_addr[ix] != b.addr.s6_addr[ix])
- return a.addr.s6_addr[ix] < b.addr.s6_addr[ix];
- return false;
- }
- };
- typedef std::set<netmask, netmask_cmp> netmask_set_t;
- typedef std::vector<netmask> netmask_vec_t;
- #endif
- const char *progname;
- /* Speak thoughts out loud. */
- static bool flag_noisy = false;
- /* One and done. */
- static bool flag_one = false;
- /* Serialize connections. */
- static bool flag_sequential = false;
- /* Fallback to default if map file is unrewarding. */
- static bool flag_map = false;
- /* Fallback to xlate if map file is unrewarding. */
- static bool flag_xlate = false;
- /* Root binary directory. */
- static const char *flag_root = "gcm.cache";
- #if NETWORKING
- static netmask_set_t netmask_set;
- static netmask_vec_t accept_addrs;
- #endif
- /* Strip out the source directory from FILE. */
- static const char *
- trim_src_file (const char *file)
- {
- static const char me[] = __FILE__;
- unsigned pos = 0;
- while (file[pos] == me[pos] && me[pos])
- pos++;
- while (pos && !IS_DIR_SEPARATOR (me[pos-1]))
- pos--;
- return file + pos;
- }
- /* Die screaming. */
- void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD
- internal_error (const char *fmt, ...)
- {
- fprintf (stderr, "%s:Internal error ", progname);
- va_list args;
- va_start (args, fmt);
- vfprintf (stderr, fmt, args);
- va_end (args);
- fprintf (stderr, "\n");
- exit (2);
- }
- /* Hooked to from gcc_assert & gcc_unreachable. */
- #if ENABLE_ASSERT_CHECKING
- void ATTRIBUTE_NORETURN ATTRIBUTE_COLD
- fancy_abort (const char *file, int line, const char *func)
- {
- internal_error ("in %s, at %s:%d", func, trim_src_file (file), line);
- }
- #endif
- /* Exploded on a signal. */
- static void ATTRIBUTE_NORETURN ATTRIBUTE_COLD
- crash_signal (int sig)
- {
- signal (sig, SIG_DFL);
- // strsignal is not portable :(
- internal_error ("signal %d", sig);
- }
- /* A fatal error of some kind. */
- static void ATTRIBUTE_NORETURN ATTRIBUTE_COLD ATTRIBUTE_PRINTF_1
- error (const char *msg, ...)
- {
- fprintf (stderr, "%s:error: ", progname);
- va_list args;
- va_start (args, msg);
- vfprintf (stderr, msg, args);
- va_end (args);
- fprintf (stderr, "\n");
- exit (1);
- }
- #if NETWORKING
- /* Progress messages to the user. */
- static bool ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD
- noisy (const char *fmt, ...)
- {
- fprintf (stderr, "%s:", progname);
- va_list args;
- va_start (args, fmt);
- vfprintf (stderr, fmt, args);
- va_end (args);
- fprintf (stderr, "\n");
- return false;
- }
- #endif
- /* More messages to the user. */
- static void ATTRIBUTE_PRINTF_2
- fnotice (FILE *file, const char *fmt, ...)
- {
- va_list args;
- va_start (args, fmt);
- vfprintf (file, fmt, args);
- va_end (args);
- }
- static void ATTRIBUTE_NORETURN
- print_usage (int error_p)
- {
- FILE *file = error_p ? stderr : stdout;
- int status = error_p ? 1 : 0;
- fnotice (file, "Usage: %s [OPTION...] [CONNECTION] [MAPPINGS...] \n\n",
- progname);
- fnotice (file, "C++ Module Mapper.\n\n");
- fnotice (file, " -a, --accept Netmask to accept from\n");
- fnotice (file, " -f, --fallback Use fallback for missing mappings\n");
- fnotice (file, " -h, --help Print this help, then exit\n");
- fnotice (file, " -n, --noisy Print progress messages\n");
- fnotice (file, " -1, --one One connection and then exit\n");
- fnotice (file, " -r, --root DIR Root compiled module directory\n");
- fnotice (file, " -s, --sequential Process connections sequentially\n");
- fnotice (file, " -v, --version Print version number, then exit\n");
- fnotice (file, "Send SIGTERM(%d) to terminate\n", SIGTERM);
- fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
- bug_report_url);
- exit (status);
- }
- /* Print version information and exit. */
- static void ATTRIBUTE_NORETURN
- print_version (void)
- {
- fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string);
- fprintf (stdout, "Copyright %s 2018-2022 Free Software Foundation, Inc.\n",
- ("(C)"));
- fnotice (stdout,
- ("This is free software; see the source for copying conditions.\n"
- "There is NO warranty; not even for MERCHANTABILITY or \n"
- "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
- exit (0);
- }
- /* ARG is a netmask to accept from. Add it to the table. Return
- false if we fail to resolve it. */
- static bool
- accept_from (char *arg ATTRIBUTE_UNUSED)
- {
- bool ok = true;
- #if HAVE_AF_INET6
- unsigned bits = sizeof (in6_addr) * 8;
- char *slash = strrchr (arg, '/');
- if (slash)
- {
- *slash = 0;
- if (slash[1])
- {
- char *endp;
- bits = strtoul (slash + 1, &endp, 0);
- }
- }
- addrinfo hints;
- hints.ai_flags = AI_NUMERICSERV;
- hints.ai_family = AF_INET6;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_protocol = 0;
- hints.ai_addrlen = 0;
- hints.ai_addr = NULL;
- hints.ai_canonname = NULL;
- hints.ai_next = NULL;
- struct addrinfo *addrs = NULL;
- /* getaddrinfo requires either hostname or servname to be non-null, so that we must
- set a port number (to cover the case that the string passed contains just /NN).
- Use an arbitrary in-range port number, but avoiding "0" which triggers a bug on
- some BSD variants. */
- if (int e = getaddrinfo (slash == arg ? NULL : arg, "1", &hints, &addrs))
- {
- noisy ("cannot resolve '%s': %s", arg, gai_strerror (e));
- ok = false;
- }
- else
- for (addrinfo *next = addrs; next; next = next->ai_next)
- if (next->ai_family == AF_INET6)
- {
- netmask mask (((const sockaddr_in6 *)next->ai_addr)->sin6_addr, bits);
- netmask_set.insert (mask);
- }
- freeaddrinfo (addrs);
- #endif
- return ok;
- }
- /* Process args, return index to first non-arg. */
- static int
- process_args (int argc, char **argv)
- {
- static const struct option options[] =
- {
- { "accept", required_argument, NULL, 'a' },
- { "help", no_argument, NULL, 'h' },
- { "map", no_argument, NULL, 'm' },
- { "noisy", no_argument, NULL, 'n' },
- { "one", no_argument, NULL, '1' },
- { "root", required_argument, NULL, 'r' },
- { "sequential", no_argument, NULL, 's' },
- { "translate",no_argument, NULL, 't' },
- { "version", no_argument, NULL, 'v' },
- { 0, 0, 0, 0 }
- };
- int opt;
- bool bad_accept = false;
- const char *opts = "a:fhmn1r:stv";
- while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1)
- {
- switch (opt)
- {
- case 'a':
- if (!accept_from (optarg))
- bad_accept = true;
- break;
- case 'h':
- print_usage (false);
- /* print_usage will exit. */
- case 'f': // deprecated alias
- case 'm':
- flag_map = true;
- break;
- case 'n':
- flag_noisy = true;
- break;
- case '1':
- flag_one = true;
- break;
- case 'r':
- flag_root = optarg;
- break;
- case 's':
- flag_sequential = true;
- break;
- case 't':
- flag_xlate = true;
- break;
- case 'v':
- print_version ();
- /* print_version will exit. */
- default:
- print_usage (true);
- /* print_usage will exit. */
- }
- }
- if (bad_accept)
- error ("failed to resolve all accept addresses");
- return optind;
- }
- #if NETWORKING
- /* Manipulate the EPOLL state, or do nothing, if there is epoll. */
- #ifdef HAVE_EPOLL
- static inline void
- do_epoll_ctl (int epoll_fd, int code, int event, int fd, unsigned data)
- {
- epoll_event ev;
- ev.events = event;
- ev.data.u32 = data;
- if (epoll_ctl (epoll_fd, code, fd, &ev))
- {
- noisy ("epoll_ctl error:%s", xstrerror (errno));
- gcc_unreachable ();
- }
- }
- #define my_epoll_ctl(EFD,C,EV,FD,CL) \
- ((EFD) >= 0 ? do_epoll_ctl (EFD,C,EV,FD,CL) : (void)0)
- #else
- #define my_epoll_ctl(EFD,C,EV,FD,CL) ((void)(EFD), (void)(FD), (void)(CL))
- #endif
- /* We increment this to tell the server to shut down. */
- static volatile int term = false;
- static volatile int kill_sock_fd = -1;
- #if !defined (HAVE_PSELECT) && defined (HAVE_SELECT)
- static int term_pipe[2] = {-1, -1};
- #else
- #define term_pipe ((int *)NULL)
- #endif
- /* A terminate signal. Shutdown gracefully. */
- static void
- term_signal (int sig)
- {
- signal (sig, term_signal);
- term = term + 1;
- if (term_pipe && term_pipe[1] >= 0)
- write (term_pipe[1], &term_pipe[1], 1);
- }
- /* A kill signal. Shutdown immediately. */
- static void
- kill_signal (int sig)
- {
- signal (sig, SIG_DFL);
- int sock_fd = kill_sock_fd;
- if (sock_fd >= 0)
- close (sock_fd);
- exit (2);
- }
- bool process_server (Cody::Server *server, unsigned slot, int epoll_fd)
- {
- switch (server->GetDirection ())
- {
- case Cody::Server::READING:
- if (int err = server->Read ())
- return !(err == EINTR || err == EAGAIN);
- server->ProcessRequests ();
- server->PrepareToWrite ();
- break;
- case Cody::Server::WRITING:
- if (int err = server->Write ())
- return !(err == EINTR || err == EAGAIN);
- server->PrepareToRead ();
- break;
- default:
- // We should never get here
- return true;
- }
- // We've changed direction, so update epoll
- gcc_assert (server->GetFDRead () == server->GetFDWrite ());
- my_epoll_ctl (epoll_fd, EPOLL_CTL_MOD,
- server->GetDirection () == Cody::Server::READING
- ? EPOLLIN : EPOLLOUT, server->GetFDRead (), slot + 1);
- return false;
- }
- void close_server (Cody::Server *server, int epoll_fd)
- {
- my_epoll_ctl (epoll_fd, EPOLL_CTL_DEL, EPOLLIN, server->GetFDRead (), 0);
- close (server->GetFDRead ());
-
- delete server;
- }
- int open_server (bool ip6, int sock_fd)
- {
- sockaddr_in6 addr;
- socklen_t addr_len = sizeof (addr);
- #ifdef HAVE_ACCEPT4
- int client_fd = accept4 (sock_fd, ip6 ? (sockaddr *)&addr : nullptr,
- &addr_len, SOCK_NONBLOCK);
- #else
- int client_fd = accept (sock_fd, ip6 ? (sockaddr *)&addr : nullptr, &addr_len);
- #endif
- if (client_fd < 0)
- {
- error ("cannot accept: %s", xstrerror (errno));
- flag_one = true;
- }
- else if (ip6)
- {
- const char *str = NULL;
- #if HAVE_INET_NTOP
- char name[INET6_ADDRSTRLEN];
- str = inet_ntop (addr.sin6_family, &addr.sin6_addr, name, sizeof (name));
- #endif
- if (!accept_addrs.empty ())
- {
- netmask_vec_t::iterator e = accept_addrs.end ();
- for (netmask_vec_t::iterator i = accept_addrs.begin ();
- i != e; ++i)
- if (i->includes (addr.sin6_addr))
- goto present;
- close (client_fd);
- client_fd = -1;
- noisy ("Rejecting connection from disallowed source '%s'",
- str ? str : "");
- present:;
- }
- if (client_fd >= 0)
- flag_noisy && noisy ("Accepting connection from '%s'", str ? str : "");
- }
- return client_fd;
- }
- /* A server listening on bound socket SOCK_FD. */
- static void
- server (bool ipv6, int sock_fd, module_resolver *resolver)
- {
- int epoll_fd = -1;
- signal (SIGTERM, term_signal);
- #ifdef HAVE_EPOLL
- epoll_fd = epoll_create (1);
- #endif
- if (epoll_fd >= 0)
- my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, sock_fd, 0);
- #if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)
- sigset_t mask;
- {
- sigset_t block;
- sigemptyset (&block);
- sigaddset (&block, SIGTERM);
- sigprocmask (SIG_BLOCK, &block, &mask);
- }
- #endif
- #ifdef HAVE_EPOLL
- const unsigned max_events = 20;
- epoll_event events[max_events];
- #endif
- #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
- fd_set readers, writers;
- #endif
- if (term_pipe)
- pipe (term_pipe);
- // We need stable references to servers, so this array can contain nulls
- std::vector<Cody::Server *> connections;
- unsigned live = 0;
- while (sock_fd >= 0 || live)
- {
- /* Wait for one or more events. */
- bool eintr = false;
- int event_count;
- if (epoll_fd >= 0)
- {
- #ifdef HAVE_EPOLL
- event_count = epoll_pwait (epoll_fd, events, max_events, -1, &mask);
- #endif
- }
- else
- {
- #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
- FD_ZERO (&readers);
- FD_ZERO (&writers);
- unsigned limit = 0;
- if (sock_fd >= 0
- && !(term || (live && (flag_one || flag_sequential))))
- {
- FD_SET (sock_fd, &readers);
- limit = sock_fd + 1;
- }
- if (term_pipe && term_pipe[0] >= 0)
- {
- FD_SET (term_pipe[0], &readers);
- if (unsigned (term_pipe[0]) >= limit)
- limit = term_pipe[0] + 1;
- }
- for (auto iter = connections.begin ();
- iter != connections.end (); ++iter)
- if (auto *server = *iter)
- {
- int fd = -1;
- switch (server->GetDirection ())
- {
- case Cody::Server::READING:
- fd = server->GetFDRead ();
- FD_SET (fd, &readers);
- break;
- case Cody::Server::WRITING:
- fd = server->GetFDWrite ();
- FD_SET (fd, &writers);
- break;
- default:
- break;
- }
- if (fd >= 0 && limit <= unsigned (fd))
- limit = fd + 1;
- }
- #ifdef HAVE_PSELECT
- event_count = pselect (limit, &readers, &writers, NULL, NULL, &mask);
- #else
- event_count = select (limit, &readers, &writers, NULL, NULL);
- #endif
- if (term_pipe && FD_ISSET (term_pipe[0], &readers))
- {
- /* Fake up an interrupted system call. */
- event_count = -1;
- errno = EINTR;
- }
- #endif
- }
- if (event_count < 0)
- {
- // Error in waiting
- if (errno == EINTR)
- {
- flag_noisy && noisy ("Interrupted wait");
- eintr = true;
- }
- else
- error ("cannot %s: %s", epoll_fd >= 0 ? "epoll_wait"
- #ifdef HAVE_PSELECT
- : "pselect",
- #else
- : "select",
- #endif
- xstrerror (errno));
- event_count = 0;
- }
- auto iter = connections.begin ();
- while (event_count--)
- {
- // Process an event
- int active = -2;
- if (epoll_fd >= 0)
- {
- #ifdef HAVE_EPOLL
- /* See PR c++/88664 for why a temporary is used. */
- unsigned data = events[event_count].data.u32;
- active = int (data) - 1;
- #endif
- }
- else
- {
- for (; iter != connections.end (); ++iter)
- if (auto *server = *iter)
- {
- bool found = false;
- switch (server->GetDirection ())
- {
- #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
- case Cody::Server::READING:
- found = FD_ISSET (server->GetFDRead (), &readers);
- break;
- case Cody::Server::WRITING:
- found = FD_ISSET (server->GetFDWrite (), &writers);
- break;
- #endif
- default:
- break;
- }
- if (found)
- {
- active = iter - connections.begin ();
- ++iter;
- break;
- }
- }
- if (active < 0 && sock_fd >= 0 && FD_ISSET (sock_fd, &readers))
- active = -1;
- }
- if (active >= 0)
- {
- // Do the action
- auto *server = connections[active];
- if (process_server (server, active, epoll_fd))
- {
- connections[active] = nullptr;
- close_server (server, epoll_fd);
- live--;
- if (flag_sequential)
- my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, sock_fd, 0);
- }
- }
- else if (active == -1 && !eintr)
- {
- // New connection
- int fd = open_server (ipv6, sock_fd);
- if (fd >= 0)
- {
- #if !defined (HAVE_ACCEPT4) \
- && (defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT))
- int flags = fcntl (fd, F_GETFL, 0);
- fcntl (fd, F_SETFL, flags | O_NONBLOCK);
- #endif
- auto *server = new Cody::Server (resolver, fd);
- unsigned slot = connections.size ();
- if (live == slot)
- connections.push_back (server);
- else
- for (auto iter = connections.begin (); ; ++iter)
- if (!*iter)
- {
- *iter = server;
- slot = iter - connections.begin ();
- break;
- }
- live++;
- my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, fd, slot + 1);
- }
- }
- if (sock_fd >= 0
- && (term || (live && (flag_one || flag_sequential))))
- {
- /* Stop paying attention to sock_fd. */
- my_epoll_ctl (epoll_fd, EPOLL_CTL_DEL, EPOLLIN, sock_fd, 0);
- if (flag_one || term)
- {
- close (sock_fd);
- sock_fd = -1;
- }
- }
- }
- }
- #if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)
- /* Restore the signal mask. */
- sigprocmask (SIG_SETMASK, &mask, NULL);
- #endif
- gcc_assert (sock_fd < 0);
- if (epoll_fd >= 0)
- close (epoll_fd);
- if (term_pipe && term_pipe[0] >= 0)
- {
- close (term_pipe[0]);
- close (term_pipe[1]);
- }
- }
- #endif
- static int maybe_parse_socket (std::string &option, module_resolver *r)
- {
- /* Local or ipv6 address. */
- auto last = option.find_last_of ('?');
- if (last != option.npos)
- {
- r->set_ident (option.c_str () + last + 1);
- option.erase (last);
- }
- int fd = -2;
- char const *errmsg = nullptr;
- /* Does it look like a socket? */
- if (option[0] == '=')
- {
- /* A local socket. */
- #if CODY_NETWORKING
- fd = Cody::ListenLocal (&errmsg, option.c_str () + 1);
- #endif
- }
- else
- {
- auto colon = option.find_last_of (':');
- if (colon != option.npos)
- {
- /* Try a hostname:port address. */
- char const *cptr = option.c_str () + colon;
- char *endp;
- unsigned port = strtoul (cptr + 1, &endp, 10);
- if (port && endp != cptr + 1 && !*endp)
- {
- /* Ends in ':number', treat as ipv6 domain socket. */
- option.erase (colon);
- #if CODY_NETWORKING
- fd = Cody::ListenInet6 (&errmsg, option.c_str (), port);
- #endif
- }
- }
- }
- if (errmsg)
- error ("failed to open socket: %s", errmsg);
- return fd;
- }
- int
- main (int argc, char *argv[])
- {
- const char *p = argv[0] + strlen (argv[0]);
- while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
- --p;
- progname = p;
- #ifdef SIGSEGV
- signal (SIGSEGV, crash_signal);
- #endif
- #ifdef SIGILL
- signal (SIGILL, crash_signal);
- #endif
- #ifdef SIGBUS
- signal (SIGBUS, crash_signal);
- #endif
- #ifdef SIGABRT
- signal (SIGABRT, crash_signal);
- #endif
- #ifdef SIGFPE
- signal (SIGFPE, crash_signal);
- #endif
- #ifdef SIGPIPE
- /* Ignore sigpipe, so read/write get an error. */
- signal (SIGPIPE, SIG_IGN);
- #endif
- #if NETWORKING
- #ifdef SIGINT
- signal (SIGINT, kill_signal);
- #endif
- #endif
- int argno = process_args (argc, argv);
- std::string name;
- int sock_fd = -1; /* Socket fd, otherwise stdin/stdout. */
- module_resolver r (flag_map, flag_xlate);
- if (argno != argc)
- {
- name = argv[argno];
- sock_fd = maybe_parse_socket (name, &r);
- if (!name.empty ())
- argno++;
- }
- if (argno != argc)
- for (; argno != argc; argno++)
- {
- std::string option = argv[argno];
- char const *prefix = nullptr;
- auto ident = option.find_last_of ('?');
- if (ident != option.npos)
- {
- prefix = option.c_str () + ident + 1;
- option[ident] = 0;
- }
- int fd = open (option.c_str (), O_RDONLY | O_CLOEXEC);
- int err = 0;
- if (fd < 0)
- err = errno;
- else
- {
- err = r.read_tuple_file (fd, prefix, false);
- close (fd);
- }
- if (err)
- error ("failed reading '%s': %s", option.c_str (), xstrerror (err));
- }
- else
- r.set_default_map (true);
- if (flag_root)
- r.set_repo (flag_root);
- #ifdef HAVE_AF_INET6
- netmask_set_t::iterator end = netmask_set.end ();
- for (netmask_set_t::iterator iter = netmask_set.begin ();
- iter != end; ++iter)
- {
- netmask_vec_t::iterator e = accept_addrs.end ();
- for (netmask_vec_t::iterator i = accept_addrs.begin (); i != e; ++i)
- if (i->includes (iter->addr))
- goto present;
- accept_addrs.push_back (*iter);
- present:;
- }
- #endif
- #if NETWORKING
- if (sock_fd >= 0)
- {
- server (name[0] != '=', sock_fd, &r);
- if (name[0] == '=')
- unlink (name.c_str () + 1);
- }
- else
- #endif
- {
- auto server = Cody::Server (&r, 0, 1);
- int err = 0;
- for (;;)
- {
- server.PrepareToRead ();
- while ((err = server.Read ()))
- {
- if (err == EINTR || err == EAGAIN)
- continue;
- goto done;
- }
- server.ProcessRequests ();
- server.PrepareToWrite ();
- while ((err = server.Write ()))
- {
- if (err == EINTR || err == EAGAIN)
- continue;
- goto done;
- }
- }
- done:;
- if (err > 0)
- error ("communication error:%s", xstrerror (err));
- }
- return 0;
- }
|