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