1 /* C++ modules. Experimental!
2 Copyright (C) 2018-2020 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/>. */
34 #include <sys/types.h>
39 /* Include network stuff first. Excitingly OSX10.14 uses bcmp here, which
41 #if defined (HAVE_AF_UNIX) || defined (HAVE_AF_INET6)
42 /* socket, bind, listen, accept{4} */
44 # include <sys/socket.h>
49 # include <netinet/in.h>
51 /* sockaddr_in6, getaddrinfo, freeaddrinfo, gai_sterror, ntohs, htons. */
56 #include <arpa/inet.h>
60 # define gai_strerror(X) ""
68 /* epoll_create, epoll_ctl, epoll_pwait */
69 #include <sys/epoll.h>
71 #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
72 /* pselect or select */
73 #include <sys/select.h>
80 #define HAVE_DECL_BASENAME 1 /* See comment in gcc/configure.ac. */
81 #include "libiberty.h"
83 #if !HOST_HAS_O_CLOEXEC
87 #ifndef IS_DIR_SEPARATOR
88 #define IS_DIR_SEPARATOR(C) ((C) == '/')
91 #define DIR_SEPARATOR '/'
99 netmask (const in6_addr
&a
, unsigned b
)
101 if (b
> sizeof (in6_addr
) * 8)
102 b
= sizeof (in6_addr
) * 8;
104 unsigned byte
= (b
+ 7) / 8;
106 for (ix
= 0; ix
< byte
; ix
++)
107 addr
.s6_addr
[ix
] = a
.s6_addr
[ix
];
108 for (; ix
!= sizeof (in6_addr
); ix
++)
109 addr
.s6_addr
[ix
] = 0;
111 addr
.s6_addr
[b
/7] &= (255 << 8) >> (b
& 3);
114 bool includes (const in6_addr
&a
) const
116 unsigned byte
= bits
/ 8;
117 for (unsigned ix
= 0; ix
!= byte
; ix
++)
118 if (addr
.s6_addr
[ix
] != a
.s6_addr
[ix
])
121 if ((addr
.s6_addr
[byte
] ^ a
.s6_addr
[byte
]) >> (8 - (bits
& 3)))
127 /* Netmask comparison. */
129 bool operator() (const netmask
&a
, const netmask
&b
) const
131 if (a
.bits
!= b
.bits
)
132 return a
.bits
< b
.bits
;
133 for (unsigned ix
= 0; ix
!= sizeof (in6_addr
); ix
++)
134 if (a
.addr
.s6_addr
[ix
] != b
.addr
.s6_addr
[ix
])
135 return a
.addr
.s6_addr
[ix
] < b
.addr
.s6_addr
[ix
];
140 typedef std::set
<netmask
, netmask_cmp
> netmask_set_t
;
141 typedef std::vector
<netmask
> netmask_vec_t
;
144 const char *progname
;
146 /* Speak thoughts out loud. */
147 static bool flag_noisy
= false;
150 static bool flag_one
= false;
152 /* Serialize connections. */
153 static bool flag_sequential
= false;
155 /* Fallback to default if map file is unrewarding. */
156 static bool flag_map
= false;
158 /* Fallback to xlate if map file is unrewarding. */
159 static bool flag_xlate
= false;
161 /* Root binary directory. */
162 static const char *flag_root
= "gcm.cache";
165 static netmask_set_t netmask_set
;
167 static netmask_vec_t accept_addrs
;
170 /* Strip out the source directory from FILE. */
173 trim_src_file (const char *file
)
175 static const char me
[] = __FILE__
;
178 while (file
[pos
] == me
[pos
] && me
[pos
])
180 while (pos
&& !IS_DIR_SEPARATOR (me
[pos
-1]))
188 void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD
189 internal_error (const char *fmt
, ...)
191 fprintf (stderr
, "%s:Internal error ", progname
);
194 va_start (args
, fmt
);
195 vfprintf (stderr
, fmt
, args
);
197 fprintf (stderr
, "\n");
202 /* Hooked to from gcc_assert & gcc_unreachable. */
204 void ATTRIBUTE_NORETURN ATTRIBUTE_COLD
205 fancy_abort (const char *file
, int line
, const char *func
)
207 internal_error ("in %s, at %s:%d", func
, trim_src_file (file
), line
);
210 /* Exploded on a signal. */
212 static void ATTRIBUTE_NORETURN ATTRIBUTE_COLD
213 crash_signal (int sig
)
215 signal (sig
, SIG_DFL
);
216 // strsignal is not portable :(
217 internal_error ("signal %d", sig
);
220 /* A fatal error of some kind. */
222 static void ATTRIBUTE_NORETURN ATTRIBUTE_COLD ATTRIBUTE_PRINTF_1
223 error (const char *msg
, ...)
225 fprintf (stderr
, "%s:error: ", progname
);
228 va_start (args
, msg
);
229 vfprintf (stderr
, msg
, args
);
231 fprintf (stderr
, "\n");
237 /* Progress messages to the user. */
238 static bool ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD
239 noisy (const char *fmt
, ...)
241 fprintf (stderr
, "%s:", progname
);
243 va_start (args
, fmt
);
244 vfprintf (stderr
, fmt
, args
);
246 fprintf (stderr
, "\n");
252 /* More messages to the user. */
254 static void ATTRIBUTE_PRINTF_2
255 fnotice (FILE *file
, const char *fmt
, ...)
259 va_start (args
, fmt
);
260 vfprintf (file
, fmt
, args
);
264 static void ATTRIBUTE_NORETURN
265 print_usage (int error_p
)
267 FILE *file
= error_p
? stderr
: stdout
;
268 int status
= error_p
? 1 : 0;
270 fnotice (file
, "Usage: %s [OPTION...] [CONNECTION] [MAPPINGS...] \n\n",
272 fnotice (file
, "C++ Module Mapper.\n\n");
273 fnotice (file
, " -a, --accept Netmask to accept from\n");
274 fnotice (file
, " -f, --fallback Use fallback for missing mappings\n");
275 fnotice (file
, " -h, --help Print this help, then exit\n");
276 fnotice (file
, " -n, --noisy Print progress messages\n");
277 fnotice (file
, " -1, --one One connection and then exit\n");
278 fnotice (file
, " -r, --root DIR Root compiled module directory\n");
279 fnotice (file
, " -s, --sequential Process connections sequentially\n");
280 fnotice (file
, " -v, --version Print version number, then exit\n");
281 fnotice (file
, "Send SIGTERM(%d) to terminate\n", SIGTERM
);
282 fnotice (file
, "\nFor bug reporting instructions, please see:\n%s.\n",
287 /* Print version information and exit. */
289 static void ATTRIBUTE_NORETURN
292 fnotice (stdout
, "%s %s%s\n", progname
, pkgversion_string
, version_string
);
293 fprintf (stdout
, "Copyright %s 2018-2020 Free Software Foundation, Inc.\n",
296 ("This is free software; see the source for copying conditions.\n"
297 "There is NO warranty; not even for MERCHANTABILITY or \n"
298 "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
302 /* ARG is a netmask to accept from. Add it to the table. Return
303 false if we fail to resolve it. */
306 accept_from (char *arg ATTRIBUTE_UNUSED
)
310 unsigned bits
= sizeof (in6_addr
) * 8;
311 char *slash
= strrchr (arg
, '/');
318 bits
= strtoul (slash
+ 1, &endp
, 0);
324 hints
.ai_flags
= AI_NUMERICSERV
;
325 hints
.ai_family
= AF_INET6
;
326 hints
.ai_socktype
= SOCK_STREAM
;
327 hints
.ai_protocol
= 0;
328 hints
.ai_addrlen
= 0;
329 hints
.ai_addr
= NULL
;
330 hints
.ai_canonname
= NULL
;
331 hints
.ai_next
= NULL
;
333 struct addrinfo
*addrs
= NULL
;
334 if (int e
= getaddrinfo (slash
== arg
? NULL
: arg
, "0", &hints
, &addrs
))
336 noisy ("cannot resolve '%s': %s", arg
, gai_strerror (e
));
340 for (addrinfo
*next
= addrs
; next
; next
= next
->ai_next
)
341 if (next
->ai_family
== AF_INET6
)
343 netmask
mask (((const sockaddr_in6
*)next
->ai_addr
)->sin6_addr
, bits
);
344 netmask_set
.insert (mask
);
346 freeaddrinfo (addrs
);
351 /* Process args, return index to first non-arg. */
354 process_args (int argc
, char **argv
)
356 static const struct option options
[] =
358 { "accept", required_argument
, NULL
, 'a' },
359 { "help", no_argument
, NULL
, 'h' },
360 { "map", no_argument
, NULL
, 'm' },
361 { "noisy", no_argument
, NULL
, 'n' },
362 { "one", no_argument
, NULL
, '1' },
363 { "root", required_argument
, NULL
, 'r' },
364 { "sequential", no_argument
, NULL
, 's' },
365 { "translate",no_argument
, NULL
, 't' },
366 { "version", no_argument
, NULL
, 'v' },
370 bool bad_accept
= false;
371 const char *opts
= "a:fhmn1r:stv";
372 while ((opt
= getopt_long (argc
, argv
, opts
, options
, NULL
)) != -1)
377 if (!accept_from (optarg
))
382 /* print_usage will exit. */
383 case 'f': // deprecated alias
397 flag_sequential
= true;
404 /* print_version will exit. */
407 /* print_usage will exit. */
412 error ("failed to resolve all accept addresses");
419 /* Manipulate the EPOLL state, or do nothing, if there is epoll. */
423 do_epoll_ctl (int epoll_fd
, int code
, int event
, int fd
, unsigned data
)
428 if (epoll_ctl (epoll_fd
, code
, fd
, &ev
))
430 noisy ("epoll_ctl error:%s", xstrerror (errno
));
434 #define my_epoll_ctl(EFD,C,EV,FD,CL) \
435 ((EFD) >= 0 ? do_epoll_ctl (EFD,C,EV,FD,CL) : (void)0)
437 #define my_epoll_ctl(EFD,C,EV,FD,CL) ((void)(EFD), (void)(FD), (void)(CL))
440 /* We increment this to tell the server to shut down. */
441 static volatile int term
= false;
442 static volatile int kill_sock_fd
= -1;
443 #if !defined (HAVE_PSELECT) && defined (HAVE_SELECT)
444 static int term_pipe
[2] = {-1, -1};
446 #define term_pipe ((int *)NULL)
449 /* A terminate signal. Shutdown gracefully. */
452 term_signal (int sig
)
454 signal (sig
, term_signal
);
456 if (term_pipe
&& term_pipe
[1] >= 0)
457 write (term_pipe
[1], &term_pipe
[1], 1);
460 /* A kill signal. Shutdown immediately. */
463 kill_signal (int sig
)
465 signal (sig
, SIG_DFL
);
466 int sock_fd
= kill_sock_fd
;
472 bool process_server (Cody::Server
*server
, unsigned slot
, int epoll_fd
)
474 switch (server
->GetDirection ())
476 case Cody::Server::READING
:
477 if (int err
= server
->Read ())
478 return !(err
== EINTR
|| err
== EAGAIN
);
479 server
->ProcessRequests ();
480 server
->PrepareToWrite ();
483 case Cody::Server::WRITING
:
484 if (int err
= server
->Write ())
485 return !(err
== EINTR
|| err
== EAGAIN
);
486 server
->PrepareToRead ();
490 // We should never get here
494 // We've changed direction, so update epoll
495 gcc_assert (server
->GetFDRead () == server
->GetFDWrite ());
496 my_epoll_ctl (epoll_fd
, EPOLL_CTL_MOD
,
497 server
->GetDirection () == Cody::Server::READING
498 ? EPOLLIN
: EPOLLOUT
, server
->GetFDRead (), slot
+ 1);
503 void close_server (Cody::Server
*server
, int epoll_fd
)
505 my_epoll_ctl (epoll_fd
, EPOLL_CTL_DEL
, EPOLLIN
, server
->GetFDRead (), 0);
507 close (server
->GetFDRead ());
512 int open_server (bool ip6
, int sock_fd
)
515 socklen_t addr_len
= sizeof (addr
);
518 int client_fd
= accept4 (sock_fd
, ip6
? (sockaddr
*)&addr
: nullptr,
519 &addr_len
, SOCK_NONBLOCK
);
521 int client_fd
= accept (sock_fd
, ip6
? (sockaddr
*)&addr
: nullptr, &addr_len
);
525 error ("cannot accept: %s", xstrerror (errno
));
530 const char *str
= NULL
;
532 char name
[INET6_ADDRSTRLEN
];
533 str
= inet_ntop (addr
.sin6_family
, &addr
.sin6_addr
, name
, sizeof (name
));
535 if (!accept_addrs
.empty ())
537 netmask_vec_t::iterator e
= accept_addrs
.end ();
538 for (netmask_vec_t::iterator i
= accept_addrs
.begin ();
540 if (i
->includes (addr
.sin6_addr
))
544 noisy ("Rejecting connection from disallowed source '%s'",
549 flag_noisy
&& noisy ("Accepting connection from '%s'", str
? str
: "");
555 /* A server listening on bound socket SOCK_FD. */
558 server (bool ipv6
, int sock_fd
, module_resolver
*resolver
)
562 signal (SIGTERM
, term_signal
);
564 epoll_fd
= epoll_create (1);
567 my_epoll_ctl (epoll_fd
, EPOLL_CTL_ADD
, EPOLLIN
, sock_fd
, 0);
569 #if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)
573 sigemptyset (&block
);
574 sigaddset (&block
, SIGTERM
);
575 sigprocmask (SIG_BLOCK
, &block
, &mask
);
580 const unsigned max_events
= 20;
581 epoll_event events
[max_events
];
583 #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
584 fd_set readers
, writers
;
589 // We need stable references to servers, so this array can contain nulls
590 std::vector
<Cody::Server
*> connections
;
592 while (sock_fd
>= 0 || live
)
594 /* Wait for one or more events. */
601 event_count
= epoll_pwait (epoll_fd
, events
, max_events
, -1, &mask
);
606 #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
612 && !(term
|| (live
&& (flag_one
|| flag_sequential
))))
614 FD_SET (sock_fd
, &readers
);
618 if (term_pipe
&& term_pipe
[0] >= 0)
620 FD_SET (term_pipe
[0], &readers
);
621 if (unsigned (term_pipe
[0]) >= limit
)
622 limit
= term_pipe
[0] + 1;
625 for (auto iter
= connections
.begin ();
626 iter
!= connections
.end (); ++iter
)
627 if (auto *server
= *iter
)
630 switch (server
->GetDirection ())
632 case Cody::Server::READING
:
633 fd
= server
->GetFDRead ();
634 FD_SET (fd
, &readers
);
636 case Cody::Server::WRITING
:
637 fd
= server
->GetFDWrite ();
638 FD_SET (fd
, &writers
);
644 if (fd
>= 0 && limit
<= unsigned (fd
))
649 event_count
= pselect (limit
, &readers
, &writers
, NULL
, NULL
, &mask
);
651 event_count
= select (limit
, &readers
, &writers
, NULL
, NULL
);
653 if (term_pipe
&& FD_ISSET (term_pipe
[0], &readers
))
655 /* Fake up an interrupted system call. */
667 flag_noisy
&& noisy ("Interrupted wait");
671 error ("cannot %s: %s", epoll_fd
>= 0 ? "epoll_wait"
681 auto iter
= connections
.begin ();
682 while (event_count
--)
690 /* See PR c++/88664 for why a temporary is used. */
691 unsigned data
= events
[event_count
].data
.u32
;
692 active
= int (data
) - 1;
697 for (; iter
!= connections
.end (); ++iter
)
698 if (auto *server
= *iter
)
701 switch (server
->GetDirection ())
703 #if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
704 case Cody::Server::READING
:
705 found
= FD_ISSET (server
->GetFDRead (), &readers
);
707 case Cody::Server::WRITING
:
708 found
= FD_ISSET (server
->GetFDWrite (), &writers
);
717 active
= iter
- connections
.begin ();
723 if (active
< 0 && sock_fd
>= 0 && FD_ISSET (sock_fd
, &readers
))
730 auto *server
= connections
[active
];
731 if (process_server (server
, active
, epoll_fd
))
733 connections
[active
] = nullptr;
734 close_server (server
, epoll_fd
);
737 my_epoll_ctl (epoll_fd
, EPOLL_CTL_ADD
, EPOLLIN
, sock_fd
, 0);
740 else if (active
== -1 && !eintr
)
743 int fd
= open_server (ipv6
, sock_fd
);
746 #if !defined (HAVE_ACCEPT4) \
747 && (defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT))
748 int flags
= fcntl (fd
, F_GETFL
, 0);
749 fcntl (fd
, F_SETFL
, flags
| O_NONBLOCK
);
751 auto *server
= new Cody::Server (resolver
, fd
);
753 unsigned slot
= connections
.size ();
755 connections
.push_back (server
);
757 for (auto iter
= connections
.begin (); ; ++iter
)
761 slot
= iter
- connections
.begin ();
765 my_epoll_ctl (epoll_fd
, EPOLL_CTL_ADD
, EPOLLIN
, fd
, slot
+ 1);
770 && (term
|| (live
&& (flag_one
|| flag_sequential
))))
772 /* Stop paying attention to sock_fd. */
773 my_epoll_ctl (epoll_fd
, EPOLL_CTL_DEL
, EPOLLIN
, sock_fd
, 0);
774 if (flag_one
|| term
)
782 #if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)
783 /* Restore the signal mask. */
784 sigprocmask (SIG_SETMASK
, &mask
, NULL
);
787 gcc_assert (sock_fd
< 0);
791 if (term_pipe
&& term_pipe
[0] >= 0)
793 close (term_pipe
[0]);
794 close (term_pipe
[1]);
800 static int maybe_parse_socket (std::string
&option
, module_resolver
*r
)
802 /* Local or ipv6 address. */
803 auto last
= option
.find_last_of ('?');
804 if (last
!= option
.npos
)
806 r
->set_ident (option
.c_str () + last
+ 1);
810 char const *errmsg
= nullptr;
812 /* Does it look like a socket? */
813 if (option
[0] == '=')
815 /* A local socket. */
817 fd
= Cody::ListenLocal (&errmsg
, option
.c_str () + 1);
822 auto colon
= option
.find_last_of (':');
823 if (colon
!= option
.npos
)
825 /* Try a hostname:port address. */
826 char const *cptr
= option
.c_str () + colon
;
828 unsigned port
= strtoul (cptr
+ 1, &endp
, 10);
830 if (port
&& endp
!= cptr
+ 1 && !*endp
)
832 /* Ends in ':number', treat as ipv6 domain socket. */
833 option
.erase (colon
);
835 fd
= Cody::ListenInet6 (&errmsg
, option
.c_str (), port
);
842 error ("failed to open socket: %s", errmsg
);
848 main (int argc
, char *argv
[])
850 const char *p
= argv
[0] + strlen (argv
[0]);
851 while (p
!= argv
[0] && !IS_DIR_SEPARATOR (p
[-1]))
856 signal (SIGSEGV
, crash_signal
);
859 signal (SIGILL
, crash_signal
);
862 signal (SIGBUS
, crash_signal
);
865 signal (SIGABRT
, crash_signal
);
868 signal (SIGFPE
, crash_signal
);
871 /* Ignore sigpipe, so read/write get an error. */
872 signal (SIGPIPE
, SIG_IGN
);
876 signal (SIGINT
, kill_signal
);
880 int argno
= process_args (argc
, argv
);
883 int sock_fd
= -1; /* Socket fd, otherwise stdin/stdout. */
884 module_resolver
r (flag_map
, flag_xlate
);
889 sock_fd
= maybe_parse_socket (name
, &r
);
895 for (; argno
!= argc
; argno
++)
897 std::string option
= argv
[argno
];
898 char const *prefix
= nullptr;
899 auto ident
= option
.find_last_of ('?');
900 if (ident
!= option
.npos
)
902 prefix
= option
.c_str () + ident
+ 1;
905 int fd
= open (option
.c_str (), O_RDONLY
| O_CLOEXEC
);
911 err
= r
.read_tuple_file (fd
, prefix
, false);
916 error ("failed reading '%s': %s", option
.c_str (), xstrerror (err
));
919 r
.set_default_map (true);
922 r
.set_repo (flag_root
);
925 netmask_set_t::iterator end
= netmask_set
.end ();
926 for (netmask_set_t::iterator iter
= netmask_set
.begin ();
929 netmask_vec_t::iterator e
= accept_addrs
.end ();
930 for (netmask_vec_t::iterator i
= accept_addrs
.begin (); i
!= e
; ++i
)
931 if (i
->includes (iter
->addr
))
933 accept_addrs
.push_back (*iter
);
941 server (name
[0] != '=', sock_fd
, &r
);
943 unlink (name
.c_str () + 1);
948 auto server
= Cody::Server (&r
, 0, 1);
953 server
.PrepareToRead ();
954 while ((err
= server
.Read ()))
956 if (err
== EINTR
|| err
== EAGAIN
)
961 server
.ProcessRequests ();
963 server
.PrepareToWrite ();
964 while ((err
= server
.Write ()))
966 if (err
== EINTR
|| err
== EAGAIN
)
973 error ("communication error:%s", xstrerror (err
));