1 /* C++ modules. Experimental!
2 Copyright (C) 2018-2021 Free Software Foundation, Inc.
3 Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
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/>. */
35 #include <sys/types.h>
40 /* Include network stuff first. Excitingly OSX10.14 uses bcmp here, which
42 #if defined (HAVE_AF_UNIX) || defined (HAVE_AF_INET6)
43 /* socket, bind, listen, accept{4} */
45 # include <sys/socket.h>
50 # include <netinet/in.h>
52 /* sockaddr_in6, getaddrinfo, freeaddrinfo, gai_sterror, ntohs, htons. */
57 #include <arpa/inet.h>
61 # define gai_strerror(X) ""
64 #ifndef AI_NUMERICSERV
65 #define AI_NUMERICSERV 0
73 /* epoll_create, epoll_ctl, epoll_pwait */
74 #include <sys/epoll.h>
76 #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
77 /* pselect or select */
78 #include <sys/select.h>
85 #define HAVE_DECL_BASENAME 1 /* See comment in gcc/configure.ac. */
86 #include "libiberty.h"
88 #if !HOST_HAS_O_CLOEXEC
92 #ifndef IS_DIR_SEPARATOR
93 #define IS_DIR_SEPARATOR(C) ((C) == '/')
96 #define DIR_SEPARATOR '/'
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))
108 /* Include EXPR, so that unused variable warnings do not occur. */
109 #define gcc_assert(EXPR) ((void)(0 && (EXPR)))
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 ()
117 #define gcc_unreachable() (fancy_abort (__FILE__, __LINE__, __FUNCTION__))
126 netmask (const in6_addr
&a
, unsigned b
)
128 if (b
> sizeof (in6_addr
) * 8)
129 b
= sizeof (in6_addr
) * 8;
131 unsigned byte
= (b
+ 7) / 8;
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;
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
])
148 if ((addr
.s6_addr
[byte
] ^ a
.s6_addr
[byte
]) >> (8 - (bits
& 3)))
154 /* Netmask comparison. */
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
];
167 typedef std::set
<netmask
, netmask_cmp
> netmask_set_t
;
168 typedef std::vector
<netmask
> netmask_vec_t
;
171 const char *progname
;
173 /* Speak thoughts out loud. */
174 static bool flag_noisy
= false;
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";
192 static netmask_set_t netmask_set
;
194 static netmask_vec_t accept_addrs
;
197 /* Strip out the source directory from FILE. */
200 trim_src_file (const char *file
)
202 static const char me
[] = __FILE__
;
205 while (file
[pos
] == me
[pos
] && me
[pos
])
207 while (pos
&& !IS_DIR_SEPARATOR (me
[pos
-1]))
215 void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD
216 internal_error (const char *fmt
, ...)
218 fprintf (stderr
, "%s:Internal error ", progname
);
221 va_start (args
, fmt
);
222 vfprintf (stderr
, fmt
, args
);
224 fprintf (stderr
, "\n");
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
);
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
);
257 va_start (args
, msg
);
258 vfprintf (stderr
, msg
, args
);
260 fprintf (stderr
, "\n");
266 /* Progress messages to the user. */
267 static bool ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD
268 noisy (const char *fmt
, ...)
270 fprintf (stderr
, "%s:", progname
);
272 va_start (args
, fmt
);
273 vfprintf (stderr
, fmt
, args
);
275 fprintf (stderr
, "\n");
281 /* More messages to the user. */
283 static void ATTRIBUTE_PRINTF_2
284 fnotice (FILE *file
, const char *fmt
, ...)
288 va_start (args
, fmt
);
289 vfprintf (file
, fmt
, 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",
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",
316 /* Print version information and exit. */
318 static void ATTRIBUTE_NORETURN
321 fnotice (stdout
, "%s %s%s\n", progname
, pkgversion_string
, version_string
);
322 fprintf (stdout
, "Copyright %s 2018-2021 Free Software Foundation, Inc.\n",
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"));
331 /* ARG is a netmask to accept from. Add it to the table. Return
332 false if we fail to resolve it. */
335 accept_from (char *arg ATTRIBUTE_UNUSED
)
339 unsigned bits
= sizeof (in6_addr
) * 8;
340 char *slash
= strrchr (arg
, '/');
347 bits
= strtoul (slash
+ 1, &endp
, 0);
353 hints
.ai_flags
= AI_NUMERICSERV
;
354 hints
.ai_family
= AF_INET6
;
355 hints
.ai_socktype
= SOCK_STREAM
;
356 hints
.ai_protocol
= 0;
357 hints
.ai_addrlen
= 0;
358 hints
.ai_addr
= NULL
;
359 hints
.ai_canonname
= NULL
;
360 hints
.ai_next
= NULL
;
362 struct addrinfo
*addrs
= NULL
;
363 if (int e
= getaddrinfo (slash
== arg
? NULL
: arg
, "0", &hints
, &addrs
))
365 noisy ("cannot resolve '%s': %s", arg
, gai_strerror (e
));
369 for (addrinfo
*next
= addrs
; next
; next
= next
->ai_next
)
370 if (next
->ai_family
== AF_INET6
)
372 netmask
mask (((const sockaddr_in6
*)next
->ai_addr
)->sin6_addr
, bits
);
373 netmask_set
.insert (mask
);
375 freeaddrinfo (addrs
);
380 /* Process args, return index to first non-arg. */
383 process_args (int argc
, char **argv
)
385 static const struct option options
[] =
387 { "accept", required_argument
, NULL
, 'a' },
388 { "help", no_argument
, NULL
, 'h' },
389 { "map", no_argument
, NULL
, 'm' },
390 { "noisy", no_argument
, NULL
, 'n' },
391 { "one", no_argument
, NULL
, '1' },
392 { "root", required_argument
, NULL
, 'r' },
393 { "sequential", no_argument
, NULL
, 's' },
394 { "translate",no_argument
, NULL
, 't' },
395 { "version", no_argument
, NULL
, 'v' },
399 bool bad_accept
= false;
400 const char *opts
= "a:fhmn1r:stv";
401 while ((opt
= getopt_long (argc
, argv
, opts
, options
, NULL
)) != -1)
406 if (!accept_from (optarg
))
411 /* print_usage will exit. */
412 case 'f': // deprecated alias
426 flag_sequential
= true;
433 /* print_version will exit. */
436 /* print_usage will exit. */
441 error ("failed to resolve all accept addresses");
448 /* Manipulate the EPOLL state, or do nothing, if there is epoll. */
452 do_epoll_ctl (int epoll_fd
, int code
, int event
, int fd
, unsigned data
)
457 if (epoll_ctl (epoll_fd
, code
, fd
, &ev
))
459 noisy ("epoll_ctl error:%s", xstrerror (errno
));
463 #define my_epoll_ctl(EFD,C,EV,FD,CL) \
464 ((EFD) >= 0 ? do_epoll_ctl (EFD,C,EV,FD,CL) : (void)0)
466 #define my_epoll_ctl(EFD,C,EV,FD,CL) ((void)(EFD), (void)(FD), (void)(CL))
469 /* We increment this to tell the server to shut down. */
470 static volatile int term
= false;
471 static volatile int kill_sock_fd
= -1;
472 #if !defined (HAVE_PSELECT) && defined (HAVE_SELECT)
473 static int term_pipe
[2] = {-1, -1};
475 #define term_pipe ((int *)NULL)
478 /* A terminate signal. Shutdown gracefully. */
481 term_signal (int sig
)
483 signal (sig
, term_signal
);
485 if (term_pipe
&& term_pipe
[1] >= 0)
486 write (term_pipe
[1], &term_pipe
[1], 1);
489 /* A kill signal. Shutdown immediately. */
492 kill_signal (int sig
)
494 signal (sig
, SIG_DFL
);
495 int sock_fd
= kill_sock_fd
;
501 bool process_server (Cody::Server
*server
, unsigned slot
, int epoll_fd
)
503 switch (server
->GetDirection ())
505 case Cody::Server::READING
:
506 if (int err
= server
->Read ())
507 return !(err
== EINTR
|| err
== EAGAIN
);
508 server
->ProcessRequests ();
509 server
->PrepareToWrite ();
512 case Cody::Server::WRITING
:
513 if (int err
= server
->Write ())
514 return !(err
== EINTR
|| err
== EAGAIN
);
515 server
->PrepareToRead ();
519 // We should never get here
523 // We've changed direction, so update epoll
524 gcc_assert (server
->GetFDRead () == server
->GetFDWrite ());
525 my_epoll_ctl (epoll_fd
, EPOLL_CTL_MOD
,
526 server
->GetDirection () == Cody::Server::READING
527 ? EPOLLIN
: EPOLLOUT
, server
->GetFDRead (), slot
+ 1);
532 void close_server (Cody::Server
*server
, int epoll_fd
)
534 my_epoll_ctl (epoll_fd
, EPOLL_CTL_DEL
, EPOLLIN
, server
->GetFDRead (), 0);
536 close (server
->GetFDRead ());
541 int open_server (bool ip6
, int sock_fd
)
544 socklen_t addr_len
= sizeof (addr
);
547 int client_fd
= accept4 (sock_fd
, ip6
? (sockaddr
*)&addr
: nullptr,
548 &addr_len
, SOCK_NONBLOCK
);
550 int client_fd
= accept (sock_fd
, ip6
? (sockaddr
*)&addr
: nullptr, &addr_len
);
554 error ("cannot accept: %s", xstrerror (errno
));
559 const char *str
= NULL
;
561 char name
[INET6_ADDRSTRLEN
];
562 str
= inet_ntop (addr
.sin6_family
, &addr
.sin6_addr
, name
, sizeof (name
));
564 if (!accept_addrs
.empty ())
566 netmask_vec_t::iterator e
= accept_addrs
.end ();
567 for (netmask_vec_t::iterator i
= accept_addrs
.begin ();
569 if (i
->includes (addr
.sin6_addr
))
573 noisy ("Rejecting connection from disallowed source '%s'",
578 flag_noisy
&& noisy ("Accepting connection from '%s'", str
? str
: "");
584 /* A server listening on bound socket SOCK_FD. */
587 server (bool ipv6
, int sock_fd
, module_resolver
*resolver
)
591 signal (SIGTERM
, term_signal
);
593 epoll_fd
= epoll_create (1);
596 my_epoll_ctl (epoll_fd
, EPOLL_CTL_ADD
, EPOLLIN
, sock_fd
, 0);
598 #if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)
602 sigemptyset (&block
);
603 sigaddset (&block
, SIGTERM
);
604 sigprocmask (SIG_BLOCK
, &block
, &mask
);
609 const unsigned max_events
= 20;
610 epoll_event events
[max_events
];
612 #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
613 fd_set readers
, writers
;
618 // We need stable references to servers, so this array can contain nulls
619 std::vector
<Cody::Server
*> connections
;
621 while (sock_fd
>= 0 || live
)
623 /* Wait for one or more events. */
630 event_count
= epoll_pwait (epoll_fd
, events
, max_events
, -1, &mask
);
635 #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
641 && !(term
|| (live
&& (flag_one
|| flag_sequential
))))
643 FD_SET (sock_fd
, &readers
);
647 if (term_pipe
&& term_pipe
[0] >= 0)
649 FD_SET (term_pipe
[0], &readers
);
650 if (unsigned (term_pipe
[0]) >= limit
)
651 limit
= term_pipe
[0] + 1;
654 for (auto iter
= connections
.begin ();
655 iter
!= connections
.end (); ++iter
)
656 if (auto *server
= *iter
)
659 switch (server
->GetDirection ())
661 case Cody::Server::READING
:
662 fd
= server
->GetFDRead ();
663 FD_SET (fd
, &readers
);
665 case Cody::Server::WRITING
:
666 fd
= server
->GetFDWrite ();
667 FD_SET (fd
, &writers
);
673 if (fd
>= 0 && limit
<= unsigned (fd
))
678 event_count
= pselect (limit
, &readers
, &writers
, NULL
, NULL
, &mask
);
680 event_count
= select (limit
, &readers
, &writers
, NULL
, NULL
);
682 if (term_pipe
&& FD_ISSET (term_pipe
[0], &readers
))
684 /* Fake up an interrupted system call. */
696 flag_noisy
&& noisy ("Interrupted wait");
700 error ("cannot %s: %s", epoll_fd
>= 0 ? "epoll_wait"
710 auto iter
= connections
.begin ();
711 while (event_count
--)
719 /* See PR c++/88664 for why a temporary is used. */
720 unsigned data
= events
[event_count
].data
.u32
;
721 active
= int (data
) - 1;
726 for (; iter
!= connections
.end (); ++iter
)
727 if (auto *server
= *iter
)
730 switch (server
->GetDirection ())
732 #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
733 case Cody::Server::READING
:
734 found
= FD_ISSET (server
->GetFDRead (), &readers
);
736 case Cody::Server::WRITING
:
737 found
= FD_ISSET (server
->GetFDWrite (), &writers
);
746 active
= iter
- connections
.begin ();
752 if (active
< 0 && sock_fd
>= 0 && FD_ISSET (sock_fd
, &readers
))
759 auto *server
= connections
[active
];
760 if (process_server (server
, active
, epoll_fd
))
762 connections
[active
] = nullptr;
763 close_server (server
, epoll_fd
);
766 my_epoll_ctl (epoll_fd
, EPOLL_CTL_ADD
, EPOLLIN
, sock_fd
, 0);
769 else if (active
== -1 && !eintr
)
772 int fd
= open_server (ipv6
, sock_fd
);
775 #if !defined (HAVE_ACCEPT4) \
776 && (defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT))
777 int flags
= fcntl (fd
, F_GETFL
, 0);
778 fcntl (fd
, F_SETFL
, flags
| O_NONBLOCK
);
780 auto *server
= new Cody::Server (resolver
, fd
);
782 unsigned slot
= connections
.size ();
784 connections
.push_back (server
);
786 for (auto iter
= connections
.begin (); ; ++iter
)
790 slot
= iter
- connections
.begin ();
794 my_epoll_ctl (epoll_fd
, EPOLL_CTL_ADD
, EPOLLIN
, fd
, slot
+ 1);
799 && (term
|| (live
&& (flag_one
|| flag_sequential
))))
801 /* Stop paying attention to sock_fd. */
802 my_epoll_ctl (epoll_fd
, EPOLL_CTL_DEL
, EPOLLIN
, sock_fd
, 0);
803 if (flag_one
|| term
)
811 #if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)
812 /* Restore the signal mask. */
813 sigprocmask (SIG_SETMASK
, &mask
, NULL
);
816 gcc_assert (sock_fd
< 0);
820 if (term_pipe
&& term_pipe
[0] >= 0)
822 close (term_pipe
[0]);
823 close (term_pipe
[1]);
829 static int maybe_parse_socket (std::string
&option
, module_resolver
*r
)
831 /* Local or ipv6 address. */
832 auto last
= option
.find_last_of ('?');
833 if (last
!= option
.npos
)
835 r
->set_ident (option
.c_str () + last
+ 1);
839 char const *errmsg
= nullptr;
841 /* Does it look like a socket? */
842 if (option
[0] == '=')
844 /* A local socket. */
846 fd
= Cody::ListenLocal (&errmsg
, option
.c_str () + 1);
851 auto colon
= option
.find_last_of (':');
852 if (colon
!= option
.npos
)
854 /* Try a hostname:port address. */
855 char const *cptr
= option
.c_str () + colon
;
857 unsigned port
= strtoul (cptr
+ 1, &endp
, 10);
859 if (port
&& endp
!= cptr
+ 1 && !*endp
)
861 /* Ends in ':number', treat as ipv6 domain socket. */
862 option
.erase (colon
);
864 fd
= Cody::ListenInet6 (&errmsg
, option
.c_str (), port
);
871 error ("failed to open socket: %s", errmsg
);
877 main (int argc
, char *argv
[])
879 const char *p
= argv
[0] + strlen (argv
[0]);
880 while (p
!= argv
[0] && !IS_DIR_SEPARATOR (p
[-1]))
885 signal (SIGSEGV
, crash_signal
);
888 signal (SIGILL
, crash_signal
);
891 signal (SIGBUS
, crash_signal
);
894 signal (SIGABRT
, crash_signal
);
897 signal (SIGFPE
, crash_signal
);
900 /* Ignore sigpipe, so read/write get an error. */
901 signal (SIGPIPE
, SIG_IGN
);
905 signal (SIGINT
, kill_signal
);
909 int argno
= process_args (argc
, argv
);
912 int sock_fd
= -1; /* Socket fd, otherwise stdin/stdout. */
913 module_resolver
r (flag_map
, flag_xlate
);
918 sock_fd
= maybe_parse_socket (name
, &r
);
924 for (; argno
!= argc
; argno
++)
926 std::string option
= argv
[argno
];
927 char const *prefix
= nullptr;
928 auto ident
= option
.find_last_of ('?');
929 if (ident
!= option
.npos
)
931 prefix
= option
.c_str () + ident
+ 1;
934 int fd
= open (option
.c_str (), O_RDONLY
| O_CLOEXEC
);
940 err
= r
.read_tuple_file (fd
, prefix
, false);
945 error ("failed reading '%s': %s", option
.c_str (), xstrerror (err
));
948 r
.set_default_map (true);
951 r
.set_repo (flag_root
);
954 netmask_set_t::iterator end
= netmask_set
.end ();
955 for (netmask_set_t::iterator iter
= netmask_set
.begin ();
958 netmask_vec_t::iterator e
= accept_addrs
.end ();
959 for (netmask_vec_t::iterator i
= accept_addrs
.begin (); i
!= e
; ++i
)
960 if (i
->includes (iter
->addr
))
962 accept_addrs
.push_back (*iter
);
970 server (name
[0] != '=', sock_fd
, &r
);
972 unlink (name
.c_str () + 1);
977 auto server
= Cody::Server (&r
, 0, 1);
982 server
.PrepareToRead ();
983 while ((err
= server
.Read ()))
985 if (err
== EINTR
|| err
== EAGAIN
)
990 server
.ProcessRequests ();
992 server
.PrepareToWrite ();
993 while ((err
= server
.Write ()))
995 if (err
== EINTR
|| err
== EAGAIN
)
1002 error ("communication error:%s", xstrerror (err
));