1/* C++ modules. Experimental! 2 Copyright (C) 2018-2022 Free Software Foundation, Inc. 3 Written by Nathan Sidwell <nathan@acm.org> while at FaceBook 4 5 This file is part of GCC. 6 7 GCC is free software; you can redistribute it and/or modify it 8 under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3, or (at your option) 10 any later version. 11 12 GCC is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17You should have received a copy of the GNU General Public License 18along with GCC; see the file COPYING3. If not see 19<http://www.gnu.org/licenses/>. */ 20 21#include "config.h" 22#include "resolver.h" 23 24// C++ 25#include <set> 26#include <vector> 27#include <map> 28// C 29#include <csignal> 30#include <cstring> 31#include <cstdarg> 32#include <cstdlib> 33// OS 34#include <unistd.h> 35#include <sys/types.h> 36#include <sys/stat.h> 37#include <fcntl.h> 38 39// Network 40/* Include network stuff first. Excitingly OSX10.14 uses bcmp here, which 41 we poison later! */ 42#if defined (HAVE_AF_UNIX) || defined (HAVE_AF_INET6) 43/* socket, bind, listen, accept{4} */ 44# define NETWORKING 1 45# include <sys/socket.h> 46# ifdef HAVE_AF_UNIX 47/* sockaddr_un */ 48# include <sys/un.h> 49# endif 50# include <netinet/in.h> 51# ifdef HAVE_AF_INET6 52/* sockaddr_in6, getaddrinfo, freeaddrinfo, gai_sterror, ntohs, htons. */ 53# include <netdb.h> 54# endif 55#ifdef HAVE_INET_NTOP 56/* inet_ntop. */ 57#include <arpa/inet.h> 58#endif 59#endif 60#ifndef HAVE_AF_INET6 61# define gai_strerror(X) "" 62#endif 63 64#ifndef AI_NUMERICSERV 65#define AI_NUMERICSERV 0 66#endif 67 68#include <getopt.h> 69 70// Select or epoll 71#if NETWORKING 72#ifdef HAVE_EPOLL 73/* epoll_create, epoll_ctl, epoll_pwait */ 74#include <sys/epoll.h> 75#endif 76#if defined (HAVE_PSELECT) || defined (HAVE_SELECT) 77/* pselect or select */ 78#include <sys/select.h> 79#endif 80#endif 81 82// GCC 83#include "version.h" 84#include "ansidecl.h" 85#define HAVE_DECL_BASENAME 1 /* See comment in gcc/configure.ac. */ 86#include "libiberty.h" 87 88#if !HOST_HAS_O_CLOEXEC 89#define O_CLOEXEC 0 90#endif 91 92#ifndef IS_DIR_SEPARATOR 93#define IS_DIR_SEPARATOR(C) ((C) == '/') 94#endif 95#ifndef DIR_SEPARATOR 96#define DIR_SEPARATOR '/' 97#endif 98 99/* Imported from libcpp/system.h 100 Use gcc_assert(EXPR) to test invariants. */ 101#if ENABLE_ASSERT_CHECKING 102#define gcc_assert(EXPR) \ 103 ((void)(!(EXPR) ? fancy_abort (__FILE__, __LINE__, __FUNCTION__), 0 : 0)) 104#elif (GCC_VERSION >= 4005) 105#define gcc_assert(EXPR) \ 106 ((void)(__builtin_expect (!(EXPR), 0) ? __builtin_unreachable (), 0 : 0)) 107#else 108/* Include EXPR, so that unused variable warnings do not occur. */ 109#define gcc_assert(EXPR) ((void)(0 && (EXPR))) 110#endif 111 112/* Use gcc_unreachable() to mark unreachable locations (like an 113 unreachable default case of a switch. Do not use gcc_assert(0). */ 114#if (GCC_VERSION >= 4005) && !ENABLE_ASSERT_CHECKING 115#define gcc_unreachable() __builtin_unreachable () 116#else 117#define gcc_unreachable() (fancy_abort (__FILE__, __LINE__, __FUNCTION__)) 118#endif 119 120 121#if NETWORKING 122struct netmask { 123 in6_addr addr; 124 unsigned bits; 125 126 netmask (const in6_addr &a, unsigned b) 127 { 128 if (b > sizeof (in6_addr) * 8) 129 b = sizeof (in6_addr) * 8; 130 bits = b; 131 unsigned byte = (b + 7) / 8; 132 unsigned ix = 0; 133 for (ix = 0; ix < byte; ix++) 134 addr.s6_addr[ix] = a.s6_addr[ix]; 135 for (; ix != sizeof (in6_addr); ix++) 136 addr.s6_addr[ix] = 0; 137 if (b & 3) 138 addr.s6_addr[b/7] &= (255 << 8) >> (b & 3); 139 } 140 141 bool includes (const in6_addr &a) const 142 { 143 unsigned byte = bits / 8; 144 for (unsigned ix = 0; ix != byte; ix++) 145 if (addr.s6_addr[ix] != a.s6_addr[ix]) 146 return false; 147 if (bits & 3) 148 if ((addr.s6_addr[byte] ^ a.s6_addr[byte]) >> (8 - (bits & 3))) 149 return false; 150 return true; 151 } 152}; 153 154/* Netmask comparison. */ 155struct netmask_cmp { 156 bool operator() (const netmask &a, const netmask &b) const 157 { 158 if (a.bits != b.bits) 159 return a.bits < b.bits; 160 for (unsigned ix = 0; ix != sizeof (in6_addr); ix++) 161 if (a.addr.s6_addr[ix] != b.addr.s6_addr[ix]) 162 return a.addr.s6_addr[ix] < b.addr.s6_addr[ix]; 163 return false; 164 } 165}; 166 167typedef std::set<netmask, netmask_cmp> netmask_set_t; 168typedef std::vector<netmask> netmask_vec_t; 169#endif 170 171const char *progname; 172 173/* Speak thoughts out loud. */ 174static bool flag_noisy = false; 175 176/* One and done. */ 177static bool flag_one = false; 178 179/* Serialize connections. */ 180static bool flag_sequential = false; 181 182/* Fallback to default if map file is unrewarding. */ 183static bool flag_map = false; 184 185/* Fallback to xlate if map file is unrewarding. */ 186static bool flag_xlate = false; 187 188/* Root binary directory. */ 189static const char *flag_root = "gcm.cache"; 190 191#if NETWORKING 192static netmask_set_t netmask_set; 193 194static netmask_vec_t accept_addrs; 195#endif 196 197/* Strip out the source directory from FILE. */ 198 199static const char * 200trim_src_file (const char *file) 201{ 202 static const char me[] = __FILE__; 203 unsigned pos = 0; 204 205 while (file[pos] == me[pos] && me[pos]) 206 pos++; 207 while (pos && !IS_DIR_SEPARATOR (me[pos-1])) 208 pos--; 209 210 return file + pos; 211} 212 213/* Die screaming. */ 214 215void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD 216internal_error (const char *fmt, ...) 217{ 218 fprintf (stderr, "%s:Internal error ", progname); 219 va_list args; 220 221 va_start (args, fmt); 222 vfprintf (stderr, fmt, args); 223 va_end (args); 224 fprintf (stderr, "\n"); 225 226 exit (2); 227} 228 229/* Hooked to from gcc_assert & gcc_unreachable. */ 230 231#if ENABLE_ASSERT_CHECKING 232void ATTRIBUTE_NORETURN ATTRIBUTE_COLD 233fancy_abort (const char *file, int line, const char *func) 234{ 235 internal_error ("in %s, at %s:%d", func, trim_src_file (file), line); 236} 237#endif 238 239/* Exploded on a signal. */ 240 241static void ATTRIBUTE_NORETURN ATTRIBUTE_COLD 242crash_signal (int sig) 243{ 244 signal (sig, SIG_DFL); 245 // strsignal is not portable :( 246 internal_error ("signal %d", sig); 247} 248 249/* A fatal error of some kind. */ 250 251static void ATTRIBUTE_NORETURN ATTRIBUTE_COLD ATTRIBUTE_PRINTF_1 252error (const char *msg, ...) 253{ 254 fprintf (stderr, "%s:error: ", progname); 255 va_list args; 256 257 va_start (args, msg); 258 vfprintf (stderr, msg, args); 259 va_end (args); 260 fprintf (stderr, "\n"); 261 262 exit (1); 263} 264 265#if NETWORKING 266/* Progress messages to the user. */ 267static bool ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD 268noisy (const char *fmt, ...) 269{ 270 fprintf (stderr, "%s:", progname); 271 va_list args; 272 va_start (args, fmt); 273 vfprintf (stderr, fmt, args); 274 va_end (args); 275 fprintf (stderr, "\n"); 276 277 return false; 278} 279#endif 280 281/* More messages to the user. */ 282 283static void ATTRIBUTE_PRINTF_2 284fnotice (FILE *file, const char *fmt, ...) 285{ 286 va_list args; 287 288 va_start (args, fmt); 289 vfprintf (file, fmt, args); 290 va_end (args); 291} 292 293static void ATTRIBUTE_NORETURN 294print_usage (int error_p) 295{ 296 FILE *file = error_p ? stderr : stdout; 297 int status = error_p ? 1 : 0; 298 299 fnotice (file, "Usage: %s [OPTION...] [CONNECTION] [MAPPINGS...] \n\n", 300 progname); 301 fnotice (file, "C++ Module Mapper.\n\n"); 302 fnotice (file, " -a, --accept Netmask to accept from\n"); 303 fnotice (file, " -f, --fallback Use fallback for missing mappings\n"); 304 fnotice (file, " -h, --help Print this help, then exit\n"); 305 fnotice (file, " -n, --noisy Print progress messages\n"); 306 fnotice (file, " -1, --one One connection and then exit\n"); 307 fnotice (file, " -r, --root DIR Root compiled module directory\n"); 308 fnotice (file, " -s, --sequential Process connections sequentially\n"); 309 fnotice (file, " -v, --version Print version number, then exit\n"); 310 fnotice (file, "Send SIGTERM(%d) to terminate\n", SIGTERM); 311 fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", 312 bug_report_url); 313 exit (status); 314} 315 316/* Print version information and exit. */ 317 318static void ATTRIBUTE_NORETURN 319print_version (void) 320{ 321 fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string); 322 fprintf (stdout, "Copyright %s 2018-2022 Free Software Foundation, Inc.\n", 323 ("(C)")); 324 fnotice (stdout, 325 ("This is free software; see the source for copying conditions.\n" 326 "There is NO warranty; not even for MERCHANTABILITY or \n" 327 "FITNESS FOR A PARTICULAR PURPOSE.\n\n")); 328 exit (0); 329} 330 331/* ARG is a netmask to accept from. Add it to the table. Return 332 false if we fail to resolve it. */ 333 334static bool 335accept_from (char *arg ATTRIBUTE_UNUSED) 336{ 337 bool ok = true; 338#if HAVE_AF_INET6 339 unsigned bits = sizeof (in6_addr) * 8; 340 char *slash = strrchr (arg, '/'); 341 if (slash) 342 { 343 *slash = 0; 344 if (slash[1]) 345 { 346 char *endp; 347 bits = strtoul (slash + 1, &endp, 0); 348 } 349 } 350 351 addrinfo hints; 352 353 hints.ai_flags = AI_NUMERICSERV; 354 hints.ai_family = AF_INET6; 355 hints.ai_socktype = SOCK_STREAM; 356 hints.ai_protocol = 0; 357 hints.ai_addrlen = 0; 358 hints.ai_addr = NULL; 359 hints.ai_canonname = NULL; 360 hints.ai_next = NULL; 361 362 struct addrinfo *addrs = NULL; 363 /* getaddrinfo requires either hostname or servname to be non-null, so that we must 364 set a port number (to cover the case that the string passed contains just /NN). 365 Use an arbitrary in-range port number, but avoiding "0" which triggers a bug on 366 some BSD variants. */ 367 if (int e = getaddrinfo (slash == arg ? NULL : arg, "1", &hints, &addrs)) 368 { 369 noisy ("cannot resolve '%s': %s", arg, gai_strerror (e)); 370 ok = false; 371 } 372 else 373 for (addrinfo *next = addrs; next; next = next->ai_next) 374 if (next->ai_family == AF_INET6) 375 { 376 netmask mask (((const sockaddr_in6 *)next->ai_addr)->sin6_addr, bits); 377 netmask_set.insert (mask); 378 } 379 freeaddrinfo (addrs); 380#endif 381 return ok; 382} 383 384/* Process args, return index to first non-arg. */ 385 386static int 387process_args (int argc, char **argv) 388{ 389 static const struct option options[] = 390 { 391 { "accept", required_argument, NULL, 'a' }, 392 { "help", no_argument, NULL, 'h' }, 393 { "map", no_argument, NULL, 'm' }, 394 { "noisy", no_argument, NULL, 'n' }, 395 { "one", no_argument, NULL, '1' }, 396 { "root", required_argument, NULL, 'r' }, 397 { "sequential", no_argument, NULL, 's' }, 398 { "translate",no_argument, NULL, 't' }, 399 { "version", no_argument, NULL, 'v' }, 400 { 0, 0, 0, 0 } 401 }; 402 int opt; 403 bool bad_accept = false; 404 const char *opts = "a:fhmn1r:stv"; 405 while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1) 406 { 407 switch (opt) 408 { 409 case 'a': 410 if (!accept_from (optarg)) 411 bad_accept = true; 412 break; 413 case 'h': 414 print_usage (false); 415 /* print_usage will exit. */ 416 case 'f': // deprecated alias 417 case 'm': 418 flag_map = true; 419 break; 420 case 'n': 421 flag_noisy = true; 422 break; 423 case '1': 424 flag_one = true; 425 break; 426 case 'r': 427 flag_root = optarg; 428 break; 429 case 's': 430 flag_sequential = true; 431 break; 432 case 't': 433 flag_xlate = true; 434 break; 435 case 'v': 436 print_version (); 437 /* print_version will exit. */ 438 default: 439 print_usage (true); 440 /* print_usage will exit. */ 441 } 442 } 443 444 if (bad_accept) 445 error ("failed to resolve all accept addresses"); 446 447 return optind; 448} 449 450#if NETWORKING 451 452/* Manipulate the EPOLL state, or do nothing, if there is epoll. */ 453 454#ifdef HAVE_EPOLL 455static inline void 456do_epoll_ctl (int epoll_fd, int code, int event, int fd, unsigned data) 457{ 458 epoll_event ev; 459 ev.events = event; 460 ev.data.u32 = data; 461 if (epoll_ctl (epoll_fd, code, fd, &ev)) 462 { 463 noisy ("epoll_ctl error:%s", xstrerror (errno)); 464 gcc_unreachable (); 465 } 466} 467#define my_epoll_ctl(EFD,C,EV,FD,CL) \ 468 ((EFD) >= 0 ? do_epoll_ctl (EFD,C,EV,FD,CL) : (void)0) 469#else 470#define my_epoll_ctl(EFD,C,EV,FD,CL) ((void)(EFD), (void)(FD), (void)(CL)) 471#endif 472 473/* We increment this to tell the server to shut down. */ 474static volatile int term = false; 475static volatile int kill_sock_fd = -1; 476#if !defined (HAVE_PSELECT) && defined (HAVE_SELECT) 477static int term_pipe[2] = {-1, -1}; 478#else 479#define term_pipe ((int *)NULL) 480#endif 481 482/* A terminate signal. Shutdown gracefully. */ 483 484static void 485term_signal (int sig) 486{ 487 signal (sig, term_signal); 488 term = term + 1; 489 if (term_pipe && term_pipe[1] >= 0) 490 write (term_pipe[1], &term_pipe[1], 1); 491} 492 493/* A kill signal. Shutdown immediately. */ 494 495static void 496kill_signal (int sig) 497{ 498 signal (sig, SIG_DFL); 499 int sock_fd = kill_sock_fd; 500 if (sock_fd >= 0) 501 close (sock_fd); 502 exit (2); 503} 504 505bool process_server (Cody::Server *server, unsigned slot, int epoll_fd) 506{ 507 switch (server->GetDirection ()) 508 { 509 case Cody::Server::READING: 510 if (int err = server->Read ()) 511 return !(err == EINTR || err == EAGAIN); 512 server->ProcessRequests (); 513 server->PrepareToWrite (); 514 break; 515 516 case Cody::Server::WRITING: 517 if (int err = server->Write ()) 518 return !(err == EINTR || err == EAGAIN); 519 server->PrepareToRead (); 520 break; 521 522 default: 523 // We should never get here 524 return true; 525 } 526 527 // We've changed direction, so update epoll 528 gcc_assert (server->GetFDRead () == server->GetFDWrite ()); 529 my_epoll_ctl (epoll_fd, EPOLL_CTL_MOD, 530 server->GetDirection () == Cody::Server::READING 531 ? EPOLLIN : EPOLLOUT, server->GetFDRead (), slot + 1); 532 533 return false; 534} 535 536void close_server (Cody::Server *server, int epoll_fd) 537{ 538 my_epoll_ctl (epoll_fd, EPOLL_CTL_DEL, EPOLLIN, server->GetFDRead (), 0); 539 540 close (server->GetFDRead ()); 541 542 delete server; 543} 544 545int open_server (bool ip6, int sock_fd) 546{ 547 sockaddr_in6 addr; 548 socklen_t addr_len = sizeof (addr); 549 550#ifdef HAVE_ACCEPT4 551 int client_fd = accept4 (sock_fd, ip6 ? (sockaddr *)&addr : nullptr, 552 &addr_len, SOCK_NONBLOCK); 553#else 554 int client_fd = accept (sock_fd, ip6 ? (sockaddr *)&addr : nullptr, &addr_len); 555#endif 556 if (client_fd < 0) 557 { 558 error ("cannot accept: %s", xstrerror (errno)); 559 flag_one = true; 560 } 561 else if (ip6) 562 { 563 const char *str = NULL; 564#if HAVE_INET_NTOP 565 char name[INET6_ADDRSTRLEN]; 566 str = inet_ntop (addr.sin6_family, &addr.sin6_addr, name, sizeof (name)); 567#endif 568 if (!accept_addrs.empty ()) 569 { 570 netmask_vec_t::iterator e = accept_addrs.end (); 571 for (netmask_vec_t::iterator i = accept_addrs.begin (); 572 i != e; ++i) 573 if (i->includes (addr.sin6_addr)) 574 goto present; 575 close (client_fd); 576 client_fd = -1; 577 noisy ("Rejecting connection from disallowed source '%s'", 578 str ? str : ""); 579 present:; 580 } 581 if (client_fd >= 0) 582 flag_noisy && noisy ("Accepting connection from '%s'", str ? str : ""); 583 } 584 585 return client_fd; 586} 587 588/* A server listening on bound socket SOCK_FD. */ 589 590static void 591server (bool ipv6, int sock_fd, module_resolver *resolver) 592{ 593 int epoll_fd = -1; 594 595 signal (SIGTERM, term_signal); 596#ifdef HAVE_EPOLL 597 epoll_fd = epoll_create (1); 598#endif 599 if (epoll_fd >= 0) 600 my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, sock_fd, 0); 601 602#if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT) 603 sigset_t mask; 604 { 605 sigset_t block; 606 sigemptyset (&block); 607 sigaddset (&block, SIGTERM); 608 sigprocmask (SIG_BLOCK, &block, &mask); 609 } 610#endif 611 612#ifdef HAVE_EPOLL 613 const unsigned max_events = 20; 614 epoll_event events[max_events]; 615#endif 616#if defined (HAVE_PSELECT) || defined (HAVE_SELECT) 617 fd_set readers, writers; 618#endif 619 if (term_pipe) 620 pipe (term_pipe); 621 622 // We need stable references to servers, so this array can contain nulls 623 std::vector<Cody::Server *> connections; 624 unsigned live = 0; 625 while (sock_fd >= 0 || live) 626 { 627 /* Wait for one or more events. */ 628 bool eintr = false; 629 int event_count; 630 631 if (epoll_fd >= 0) 632 { 633#ifdef HAVE_EPOLL 634 event_count = epoll_pwait (epoll_fd, events, max_events, -1, &mask); 635#endif 636 } 637 else 638 { 639#if defined (HAVE_PSELECT) || defined (HAVE_SELECT) 640 FD_ZERO (&readers); 641 FD_ZERO (&writers); 642 643 unsigned limit = 0; 644 if (sock_fd >= 0 645 && !(term || (live && (flag_one || flag_sequential)))) 646 { 647 FD_SET (sock_fd, &readers); 648 limit = sock_fd + 1; 649 } 650 651 if (term_pipe && term_pipe[0] >= 0) 652 { 653 FD_SET (term_pipe[0], &readers); 654 if (unsigned (term_pipe[0]) >= limit) 655 limit = term_pipe[0] + 1; 656 } 657 658 for (auto iter = connections.begin (); 659 iter != connections.end (); ++iter) 660 if (auto *server = *iter) 661 { 662 int fd = -1; 663 switch (server->GetDirection ()) 664 { 665 case Cody::Server::READING: 666 fd = server->GetFDRead (); 667 FD_SET (fd, &readers); 668 break; 669 case Cody::Server::WRITING: 670 fd = server->GetFDWrite (); 671 FD_SET (fd, &writers); 672 break; 673 default: 674 break; 675 } 676 677 if (fd >= 0 && limit <= unsigned (fd)) 678 limit = fd + 1; 679 } 680 681#ifdef HAVE_PSELECT 682 event_count = pselect (limit, &readers, &writers, NULL, NULL, &mask); 683#else 684 event_count = select (limit, &readers, &writers, NULL, NULL); 685#endif 686 if (term_pipe && FD_ISSET (term_pipe[0], &readers)) 687 { 688 /* Fake up an interrupted system call. */ 689 event_count = -1; 690 errno = EINTR; 691 } 692#endif 693 } 694 695 if (event_count < 0) 696 { 697 // Error in waiting 698 if (errno == EINTR) 699 { 700 flag_noisy && noisy ("Interrupted wait"); 701 eintr = true; 702 } 703 else 704 error ("cannot %s: %s", epoll_fd >= 0 ? "epoll_wait" 705#ifdef HAVE_PSELECT 706 : "pselect", 707#else 708 : "select", 709#endif 710 xstrerror (errno)); 711 event_count = 0; 712 } 713 714 auto iter = connections.begin (); 715 while (event_count--) 716 { 717 // Process an event 718 int active = -2; 719 720 if (epoll_fd >= 0) 721 { 722#ifdef HAVE_EPOLL 723 /* See PR c++/88664 for why a temporary is used. */ 724 unsigned data = events[event_count].data.u32; 725 active = int (data) - 1; 726#endif 727 } 728 else 729 { 730 for (; iter != connections.end (); ++iter) 731 if (auto *server = *iter) 732 { 733 bool found = false; 734 switch (server->GetDirection ()) 735 { 736#if defined (HAVE_PSELECT) || defined (HAVE_SELECT) 737 case Cody::Server::READING: 738 found = FD_ISSET (server->GetFDRead (), &readers); 739 break; 740 case Cody::Server::WRITING: 741 found = FD_ISSET (server->GetFDWrite (), &writers); 742 break; 743#endif 744 default: 745 break; 746 } 747 748 if (found) 749 { 750 active = iter - connections.begin (); 751 ++iter; 752 break; 753 } 754 } 755 756 if (active < 0 && sock_fd >= 0 && FD_ISSET (sock_fd, &readers)) 757 active = -1; 758 } 759 760 if (active >= 0) 761 { 762 // Do the action 763 auto *server = connections[active]; 764 if (process_server (server, active, epoll_fd)) 765 { 766 connections[active] = nullptr; 767 close_server (server, epoll_fd); 768 live--; 769 if (flag_sequential) 770 my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, sock_fd, 0); 771 } 772 } 773 else if (active == -1 && !eintr) 774 { 775 // New connection 776 int fd = open_server (ipv6, sock_fd); 777 if (fd >= 0) 778 { 779#if !defined (HAVE_ACCEPT4) \ 780 && (defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)) 781 int flags = fcntl (fd, F_GETFL, 0); 782 fcntl (fd, F_SETFL, flags | O_NONBLOCK); 783#endif 784 auto *server = new Cody::Server (resolver, fd); 785 786 unsigned slot = connections.size (); 787 if (live == slot) 788 connections.push_back (server); 789 else 790 for (auto iter = connections.begin (); ; ++iter) 791 if (!*iter) 792 { 793 *iter = server; 794 slot = iter - connections.begin (); 795 break; 796 } 797 live++; 798 my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, fd, slot + 1); 799 } 800 } 801 802 if (sock_fd >= 0 803 && (term || (live && (flag_one || flag_sequential)))) 804 { 805 /* Stop paying attention to sock_fd. */ 806 my_epoll_ctl (epoll_fd, EPOLL_CTL_DEL, EPOLLIN, sock_fd, 0); 807 if (flag_one || term) 808 { 809 close (sock_fd); 810 sock_fd = -1; 811 } 812 } 813 } 814 } 815#if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT) 816 /* Restore the signal mask. */ 817 sigprocmask (SIG_SETMASK, &mask, NULL); 818#endif 819 820 gcc_assert (sock_fd < 0); 821 if (epoll_fd >= 0) 822 close (epoll_fd); 823 824 if (term_pipe && term_pipe[0] >= 0) 825 { 826 close (term_pipe[0]); 827 close (term_pipe[1]); 828 } 829} 830 831#endif 832 833static int maybe_parse_socket (std::string &option, module_resolver *r) 834{ 835 /* Local or ipv6 address. */ 836 auto last = option.find_last_of ('?'); 837 if (last != option.npos) 838 { 839 r->set_ident (option.c_str () + last + 1); 840 option.erase (last); 841 } 842 int fd = -2; 843 char const *errmsg = nullptr; 844 845 /* Does it look like a socket? */ 846 if (option[0] == '=') 847 { 848 /* A local socket. */ 849#if CODY_NETWORKING 850 fd = Cody::ListenLocal (&errmsg, option.c_str () + 1); 851#endif 852 } 853 else 854 { 855 auto colon = option.find_last_of (':'); 856 if (colon != option.npos) 857 { 858 /* Try a hostname:port address. */ 859 char const *cptr = option.c_str () + colon; 860 char *endp; 861 unsigned port = strtoul (cptr + 1, &endp, 10); 862 863 if (port && endp != cptr + 1 && !*endp) 864 { 865 /* Ends in ':number', treat as ipv6 domain socket. */ 866 option.erase (colon); 867#if CODY_NETWORKING 868 fd = Cody::ListenInet6 (&errmsg, option.c_str (), port); 869#endif 870 } 871 } 872 } 873 874 if (errmsg) 875 error ("failed to open socket: %s", errmsg); 876 877 return fd; 878} 879 880int 881main (int argc, char *argv[]) 882{ 883 const char *p = argv[0] + strlen (argv[0]); 884 while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1])) 885 --p; 886 progname = p; 887 888#ifdef SIGSEGV 889 signal (SIGSEGV, crash_signal); 890#endif 891#ifdef SIGILL 892 signal (SIGILL, crash_signal); 893#endif 894#ifdef SIGBUS 895 signal (SIGBUS, crash_signal); 896#endif 897#ifdef SIGABRT 898 signal (SIGABRT, crash_signal); 899#endif 900#ifdef SIGFPE 901 signal (SIGFPE, crash_signal); 902#endif 903#ifdef SIGPIPE 904 /* Ignore sigpipe, so read/write get an error. */ 905 signal (SIGPIPE, SIG_IGN); 906#endif 907#if NETWORKING 908#ifdef SIGINT 909 signal (SIGINT, kill_signal); 910#endif 911#endif 912 913 int argno = process_args (argc, argv); 914 915 std::string name; 916 int sock_fd = -1; /* Socket fd, otherwise stdin/stdout. */ 917 module_resolver r (flag_map, flag_xlate); 918 919 if (argno != argc) 920 { 921 name = argv[argno]; 922 sock_fd = maybe_parse_socket (name, &r); 923 if (!name.empty ()) 924 argno++; 925 } 926 927 if (argno != argc) 928 for (; argno != argc; argno++) 929 { 930 std::string option = argv[argno]; 931 char const *prefix = nullptr; 932 auto ident = option.find_last_of ('?'); 933 if (ident != option.npos) 934 { 935 prefix = option.c_str () + ident + 1; 936 option[ident] = 0; 937 } 938 int fd = open (option.c_str (), O_RDONLY | O_CLOEXEC); 939 int err = 0; 940 if (fd < 0) 941 err = errno; 942 else 943 { 944 err = r.read_tuple_file (fd, prefix, false); 945 close (fd); 946 } 947 948 if (err) 949 error ("failed reading '%s': %s", option.c_str (), xstrerror (err)); 950 } 951 else 952 r.set_default_map (true); 953 954 if (flag_root) 955 r.set_repo (flag_root); 956 957#ifdef HAVE_AF_INET6 958 netmask_set_t::iterator end = netmask_set.end (); 959 for (netmask_set_t::iterator iter = netmask_set.begin (); 960 iter != end; ++iter) 961 { 962 netmask_vec_t::iterator e = accept_addrs.end (); 963 for (netmask_vec_t::iterator i = accept_addrs.begin (); i != e; ++i) 964 if (i->includes (iter->addr)) 965 goto present; 966 accept_addrs.push_back (*iter); 967 present:; 968 } 969#endif 970 971#if NETWORKING 972 if (sock_fd >= 0) 973 { 974 server (name[0] != '=', sock_fd, &r); 975 if (name[0] == '=') 976 unlink (name.c_str () + 1); 977 } 978 else 979#endif 980 { 981 auto server = Cody::Server (&r, 0, 1); 982 983 int err = 0; 984 for (;;) 985 { 986 server.PrepareToRead (); 987 while ((err = server.Read ())) 988 { 989 if (err == EINTR || err == EAGAIN) 990 continue; 991 goto done; 992 } 993 994 server.ProcessRequests (); 995 996 server.PrepareToWrite (); 997 while ((err = server.Write ())) 998 { 999 if (err == EINTR || err == EAGAIN) 1000 continue; 1001 goto done; 1002 } 1003 } 1004 done:; 1005 if (err > 0) 1006 error ("communication error:%s", xstrerror (err)); 1007 } 1008 1009 return 0; 1010} 1011