1 /* C++ modules. Experimental!
2 Copyright (C) 2018-2024 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-2024 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 /* 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
));
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
);
384 /* Process args, return index to first non-arg. */
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' },
403 bool bad_accept
= false;
404 const char *opts
= "a:fhmn1r:stv";
405 while ((opt
= getopt_long (argc
, argv
, opts
, options
, NULL
)) != -1)
410 if (!accept_from (optarg
))
415 /* print_usage will exit. */
416 case 'f': // deprecated alias
430 flag_sequential
= true;
437 /* print_version will exit. */
440 /* print_usage will exit. */
445 error ("failed to resolve all accept addresses");
452 /* Manipulate the EPOLL state, or do nothing, if there is epoll. */
456 do_epoll_ctl (int epoll_fd
, int code
, int event
, int fd
, unsigned data
)
461 if (epoll_ctl (epoll_fd
, code
, fd
, &ev
))
463 noisy ("epoll_ctl error:%s", xstrerror (errno
));
467 #define my_epoll_ctl(EFD,C,EV,FD,CL) \
468 ((EFD) >= 0 ? do_epoll_ctl (EFD,C,EV,FD,CL) : (void)0)
470 #define my_epoll_ctl(EFD,C,EV,FD,CL) ((void)(EFD), (void)(FD), (void)(CL))
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};
479 #define term_pipe ((int *)NULL)
482 /* A terminate signal. Shutdown gracefully. */
485 term_signal (int sig
)
487 signal (sig
, term_signal
);
489 if (term_pipe
&& term_pipe
[1] >= 0)
490 write (term_pipe
[1], &term_pipe
[1], 1);
493 /* A kill signal. Shutdown immediately. */
496 kill_signal (int sig
)
498 signal (sig
, SIG_DFL
);
499 int sock_fd
= kill_sock_fd
;
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 ();
516 case Cody::Server::WRITING
:
517 if (int err
= server
->Write ())
518 return !(err
== EINTR
|| err
== EAGAIN
);
519 server
->PrepareToRead ();
523 // We should never get here
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);
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 ());
545 int open_server (bool ip6
, int sock_fd
)
548 socklen_t addr_len
= sizeof (addr
);
551 int client_fd
= accept4 (sock_fd
, ip6
? (sockaddr
*)&addr
: nullptr,
552 &addr_len
, SOCK_NONBLOCK
);
554 int client_fd
= accept (sock_fd
, ip6
? (sockaddr
*)&addr
: nullptr, &addr_len
);
558 error ("cannot accept: %s", xstrerror (errno
));
563 const char *str
= NULL
;
565 char name
[INET6_ADDRSTRLEN
];
566 str
= inet_ntop (addr
.sin6_family
, &addr
.sin6_addr
, name
, sizeof (name
));
568 if (!accept_addrs
.empty ())
570 netmask_vec_t::iterator e
= accept_addrs
.end ();
571 for (netmask_vec_t::iterator i
= accept_addrs
.begin ();
573 if (i
->includes (addr
.sin6_addr
))
577 noisy ("Rejecting connection from disallowed source '%s'",
582 flag_noisy
&& noisy ("Accepting connection from '%s'", str
? str
: "");
588 /* A server listening on bound socket SOCK_FD. */
591 server (bool ipv6
, int sock_fd
, module_resolver
*resolver
)
595 signal (SIGTERM
, term_signal
);
597 epoll_fd
= epoll_create (1);
600 my_epoll_ctl (epoll_fd
, EPOLL_CTL_ADD
, EPOLLIN
, sock_fd
, 0);
602 #if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)
606 sigemptyset (&block
);
607 sigaddset (&block
, SIGTERM
);
608 sigprocmask (SIG_BLOCK
, &block
, &mask
);
613 const unsigned max_events
= 20;
614 epoll_event events
[max_events
];
616 #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
617 fd_set readers
, writers
;
622 // We need stable references to servers, so this array can contain nulls
623 std::vector
<Cody::Server
*> connections
;
625 while (sock_fd
>= 0 || live
)
627 /* Wait for one or more events. */
634 event_count
= epoll_pwait (epoll_fd
, events
, max_events
, -1, &mask
);
639 #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
645 && !(term
|| (live
&& (flag_one
|| flag_sequential
))))
647 FD_SET (sock_fd
, &readers
);
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
)
663 switch (server
->GetDirection ())
665 case Cody::Server::READING
:
666 fd
= server
->GetFDRead ();
667 FD_SET (fd
, &readers
);
669 case Cody::Server::WRITING
:
670 fd
= server
->GetFDWrite ();
671 FD_SET (fd
, &writers
);
677 if (fd
>= 0 && limit
<= unsigned (fd
))
682 event_count
= pselect (limit
, &readers
, &writers
, NULL
, NULL
, &mask
);
684 event_count
= select (limit
, &readers
, &writers
, NULL
, NULL
);
686 if (term_pipe
&& FD_ISSET (term_pipe
[0], &readers
))
688 /* Fake up an interrupted system call. */
700 flag_noisy
&& noisy ("Interrupted wait");
704 error ("cannot %s: %s", epoll_fd
>= 0 ? "epoll_wait"
714 auto iter
= connections
.begin ();
715 while (event_count
--)
723 /* See PR c++/88664 for why a temporary is used. */
724 unsigned data
= events
[event_count
].data
.u32
;
725 active
= int (data
) - 1;
730 for (; iter
!= connections
.end (); ++iter
)
731 if (auto *server
= *iter
)
734 switch (server
->GetDirection ())
736 #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
737 case Cody::Server::READING
:
738 found
= FD_ISSET (server
->GetFDRead (), &readers
);
740 case Cody::Server::WRITING
:
741 found
= FD_ISSET (server
->GetFDWrite (), &writers
);
750 active
= iter
- connections
.begin ();
756 #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
757 if (active
< 0 && sock_fd
>= 0 && FD_ISSET (sock_fd
, &readers
))
765 auto *server
= connections
[active
];
766 if (process_server (server
, active
, epoll_fd
))
768 connections
[active
] = nullptr;
769 close_server (server
, epoll_fd
);
772 my_epoll_ctl (epoll_fd
, EPOLL_CTL_ADD
, EPOLLIN
, sock_fd
, 0);
775 else if (active
== -1 && !eintr
)
778 int fd
= open_server (ipv6
, sock_fd
);
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
);
786 auto *server
= new Cody::Server (resolver
, fd
);
788 unsigned slot
= connections
.size ();
790 connections
.push_back (server
);
792 for (auto iter
= connections
.begin (); ; ++iter
)
796 slot
= iter
- connections
.begin ();
800 my_epoll_ctl (epoll_fd
, EPOLL_CTL_ADD
, EPOLLIN
, fd
, slot
+ 1);
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
)
817 #if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)
818 /* Restore the signal mask. */
819 sigprocmask (SIG_SETMASK
, &mask
, NULL
);
822 gcc_assert (sock_fd
< 0);
826 if (term_pipe
&& term_pipe
[0] >= 0)
828 close (term_pipe
[0]);
829 close (term_pipe
[1]);
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);
845 char const *errmsg
= nullptr;
847 /* Does it look like a socket? */
848 if (option
[0] == '=')
850 /* A local socket. */
852 fd
= Cody::ListenLocal (&errmsg
, option
.c_str () + 1);
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
;
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
);
870 fd
= Cody::ListenInet6 (&errmsg
, option
.c_str (), port
);
877 error ("failed to open socket: %s", errmsg
);
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]))
891 signal (SIGSEGV
, crash_signal
);
894 signal (SIGILL
, crash_signal
);
897 signal (SIGBUS
, crash_signal
);
900 signal (SIGABRT
, crash_signal
);
903 signal (SIGFPE
, crash_signal
);
906 /* Ignore sigpipe, so read/write get an error. */
907 signal (SIGPIPE
, SIG_IGN
);
911 signal (SIGINT
, kill_signal
);
915 int argno
= process_args (argc
, argv
);
918 int sock_fd
= -1; /* Socket fd, otherwise stdin/stdout. */
919 module_resolver
r (flag_map
, flag_xlate
);
924 sock_fd
= maybe_parse_socket (name
, &r
);
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;
940 int fd
= open (option
.c_str (), O_RDONLY
| O_CLOEXEC
);
946 err
= r
.read_tuple_file (fd
, prefix
, false);
951 error ("failed reading '%s': %s", option
.c_str (), xstrerror (err
));
954 r
.set_default_map (true);
957 r
.set_repo (flag_root
);
960 netmask_set_t::iterator end
= netmask_set
.end ();
961 for (netmask_set_t::iterator iter
= netmask_set
.begin ();
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
))
968 accept_addrs
.push_back (*iter
);
976 server (name
[0] != '=', sock_fd
, &r
);
978 unlink (name
.c_str () + 1);
983 auto server
= Cody::Server (&r
, 0, 1);
988 server
.PrepareToRead ();
989 while ((err
= server
.Read ()))
991 if (err
== EINTR
|| err
== EAGAIN
)
996 server
.ProcessRequests ();
998 server
.PrepareToWrite ();
999 while ((err
= server
.Write ()))
1001 if (err
== EINTR
|| err
== EAGAIN
)
1008 error ("communication error:%s", xstrerror (err
));