gcc/c++tools/server.cc
Iain Sandoe 41f01c9715 c++tools: Work around a BSD bug in getaddrinfo().
Some versions of the BSD getaddrinfo() call do not work with the specific
input of "0" for the servname entry (a segv results).  Since we are making
the call with a dummy port number, the value is actually no important, other
than it should be in range.  Work around the BSD bug by using "1" instead.

Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>

c++tools/ChangeLog:

	* server.cc (accept_from): Use "1" as the dummy port number.
2022-03-18 15:23:49 +00:00

1011 lines
23 KiB
C++

/* 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;
}