10b57cec5SDimitry Andric //===-- lldb-platform.cpp ---------------------------------------*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
9fe6060f1SDimitry Andric #include <cerrno>
100b57cec5SDimitry Andric #if defined(__APPLE__)
110b57cec5SDimitry Andric #include <netinet/in.h>
120b57cec5SDimitry Andric #endif
13fe6060f1SDimitry Andric #include <csignal>
14fe6060f1SDimitry Andric #include <cstdint>
15fe6060f1SDimitry Andric #include <cstdio>
16fe6060f1SDimitry Andric #include <cstdlib>
17fe6060f1SDimitry Andric #include <cstring>
189dba64beSDimitry Andric #if !defined(_WIN32)
190b57cec5SDimitry Andric #include <sys/wait.h>
209dba64beSDimitry Andric #endif
210b57cec5SDimitry Andric #include <fstream>
22bdd1243dSDimitry Andric #include <optional>
230b57cec5SDimitry Andric 
240b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
25fe6060f1SDimitry Andric #include "llvm/Support/WithColor.h"
269dba64beSDimitry Andric #include "llvm/Support/raw_ostream.h"
270b57cec5SDimitry Andric 
280b57cec5SDimitry Andric #include "Acceptor.h"
290b57cec5SDimitry Andric #include "LLDBServerUtilities.h"
300b57cec5SDimitry Andric #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h"
310b57cec5SDimitry Andric #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
320b57cec5SDimitry Andric #include "lldb/Host/ConnectionFileDescriptor.h"
330b57cec5SDimitry Andric #include "lldb/Host/HostGetOpt.h"
340b57cec5SDimitry Andric #include "lldb/Host/OptionParser.h"
350b57cec5SDimitry Andric #include "lldb/Host/common/TCPSocket.h"
360b57cec5SDimitry Andric #include "lldb/Utility/FileSpec.h"
370b57cec5SDimitry Andric #include "lldb/Utility/Status.h"
380b57cec5SDimitry Andric 
390b57cec5SDimitry Andric using namespace lldb;
400b57cec5SDimitry Andric using namespace lldb_private;
410b57cec5SDimitry Andric using namespace lldb_private::lldb_server;
420b57cec5SDimitry Andric using namespace lldb_private::process_gdb_remote;
430b57cec5SDimitry Andric using namespace llvm;
440b57cec5SDimitry Andric 
450b57cec5SDimitry Andric // option descriptors for getopt_long_only()
460b57cec5SDimitry Andric 
470b57cec5SDimitry Andric static int g_debug = 0;
480b57cec5SDimitry Andric static int g_verbose = 0;
490b57cec5SDimitry Andric static int g_server = 0;
500b57cec5SDimitry Andric 
510b57cec5SDimitry Andric static struct option g_long_options[] = {
520b57cec5SDimitry Andric     {"debug", no_argument, &g_debug, 1},
530b57cec5SDimitry Andric     {"verbose", no_argument, &g_verbose, 1},
540b57cec5SDimitry Andric     {"log-file", required_argument, nullptr, 'l'},
550b57cec5SDimitry Andric     {"log-channels", required_argument, nullptr, 'c'},
560b57cec5SDimitry Andric     {"listen", required_argument, nullptr, 'L'},
570b57cec5SDimitry Andric     {"port-offset", required_argument, nullptr, 'p'},
580b57cec5SDimitry Andric     {"gdbserver-port", required_argument, nullptr, 'P'},
590b57cec5SDimitry Andric     {"min-gdbserver-port", required_argument, nullptr, 'm'},
600b57cec5SDimitry Andric     {"max-gdbserver-port", required_argument, nullptr, 'M'},
610b57cec5SDimitry Andric     {"socket-file", required_argument, nullptr, 'f'},
620b57cec5SDimitry Andric     {"server", no_argument, &g_server, 1},
630b57cec5SDimitry Andric     {nullptr, 0, nullptr, 0}};
640b57cec5SDimitry Andric 
650b57cec5SDimitry Andric #if defined(__APPLE__)
660b57cec5SDimitry Andric #define LOW_PORT (IPPORT_RESERVED)
670b57cec5SDimitry Andric #define HIGH_PORT (IPPORT_HIFIRSTAUTO)
680b57cec5SDimitry Andric #else
690b57cec5SDimitry Andric #define LOW_PORT (1024u)
700b57cec5SDimitry Andric #define HIGH_PORT (49151u)
710b57cec5SDimitry Andric #endif
720b57cec5SDimitry Andric 
739dba64beSDimitry Andric #if !defined(_WIN32)
740b57cec5SDimitry Andric // Watch for signals
signal_handler(int signo)750b57cec5SDimitry Andric static void signal_handler(int signo) {
760b57cec5SDimitry Andric   switch (signo) {
770b57cec5SDimitry Andric   case SIGHUP:
780b57cec5SDimitry Andric     // Use SIGINT first, if that does not work, use SIGHUP as a last resort.
790b57cec5SDimitry Andric     // And we should not call exit() here because it results in the global
8081ad6265SDimitry Andric     // destructors to be invoked and wreaking havoc on the threads still
8181ad6265SDimitry Andric     // running.
8281ad6265SDimitry Andric     llvm::errs() << "SIGHUP received, exiting lldb-server...\n";
830b57cec5SDimitry Andric     abort();
840b57cec5SDimitry Andric     break;
850b57cec5SDimitry Andric   }
860b57cec5SDimitry Andric }
879dba64beSDimitry Andric #endif
880b57cec5SDimitry Andric 
display_usage(const char * progname,const char * subcommand)890b57cec5SDimitry Andric static void display_usage(const char *progname, const char *subcommand) {
900b57cec5SDimitry Andric   fprintf(stderr, "Usage:\n  %s %s [--log-file log-file-name] [--log-channels "
910b57cec5SDimitry Andric                   "log-channel-list] [--port-file port-file-path] --server "
920b57cec5SDimitry Andric                   "--listen port\n",
930b57cec5SDimitry Andric           progname, subcommand);
940b57cec5SDimitry Andric   exit(0);
950b57cec5SDimitry Andric }
960b57cec5SDimitry Andric 
save_socket_id_to_file(const std::string & socket_id,const FileSpec & file_spec)970b57cec5SDimitry Andric static Status save_socket_id_to_file(const std::string &socket_id,
980b57cec5SDimitry Andric                                      const FileSpec &file_spec) {
995ffd83dbSDimitry Andric   FileSpec temp_file_spec(file_spec.GetDirectory().GetStringRef());
1000b57cec5SDimitry Andric   Status error(llvm::sys::fs::create_directory(temp_file_spec.GetPath()));
1010b57cec5SDimitry Andric   if (error.Fail())
1020b57cec5SDimitry Andric     return Status("Failed to create directory %s: %s",
103bdd1243dSDimitry Andric                   temp_file_spec.GetPath().c_str(), error.AsCString());
1040b57cec5SDimitry Andric 
1059dba64beSDimitry Andric   Status status;
10606c3fb27SDimitry Andric   if (auto Err = llvm::writeToOutput(file_spec.GetPath(),
10706c3fb27SDimitry Andric                                      [&socket_id](llvm::raw_ostream &OS) {
10806c3fb27SDimitry Andric                                        OS << socket_id;
10906c3fb27SDimitry Andric                                        return llvm::Error::success();
11006c3fb27SDimitry Andric                                      }))
11106c3fb27SDimitry Andric     return Status("Failed to atomically write file %s: %s",
1129dba64beSDimitry Andric                   file_spec.GetPath().c_str(),
11306c3fb27SDimitry Andric                   llvm::toString(std::move(Err)).c_str());
1149dba64beSDimitry Andric   return status;
1150b57cec5SDimitry Andric }
1160b57cec5SDimitry Andric 
1170b57cec5SDimitry Andric // main
main_platform(int argc,char * argv[])1180b57cec5SDimitry Andric int main_platform(int argc, char *argv[]) {
1190b57cec5SDimitry Andric   const char *progname = argv[0];
1200b57cec5SDimitry Andric   const char *subcommand = argv[1];
1210b57cec5SDimitry Andric   argc--;
1220b57cec5SDimitry Andric   argv++;
1239dba64beSDimitry Andric #if !defined(_WIN32)
1240b57cec5SDimitry Andric   signal(SIGPIPE, SIG_IGN);
1250b57cec5SDimitry Andric   signal(SIGHUP, signal_handler);
1269dba64beSDimitry Andric #endif
1270b57cec5SDimitry Andric   int long_option_index = 0;
1280b57cec5SDimitry Andric   Status error;
1290b57cec5SDimitry Andric   std::string listen_host_port;
1300b57cec5SDimitry Andric   int ch;
1310b57cec5SDimitry Andric 
1320b57cec5SDimitry Andric   std::string log_file;
1330b57cec5SDimitry Andric   StringRef
1340b57cec5SDimitry Andric       log_channels; // e.g. "lldb process threads:gdb-remote default:linux all"
1350b57cec5SDimitry Andric 
1360b57cec5SDimitry Andric   GDBRemoteCommunicationServerPlatform::PortMap gdbserver_portmap;
1370b57cec5SDimitry Andric   int min_gdbserver_port = 0;
1380b57cec5SDimitry Andric   int max_gdbserver_port = 0;
1390b57cec5SDimitry Andric   uint16_t port_offset = 0;
1400b57cec5SDimitry Andric 
1410b57cec5SDimitry Andric   FileSpec socket_file;
1420b57cec5SDimitry Andric   bool show_usage = false;
1430b57cec5SDimitry Andric   int option_error = 0;
1440b57cec5SDimitry Andric   int socket_error = -1;
1450b57cec5SDimitry Andric 
1460b57cec5SDimitry Andric   std::string short_options(OptionParser::GetShortOptionString(g_long_options));
1470b57cec5SDimitry Andric 
1480b57cec5SDimitry Andric #if __GLIBC__
1490b57cec5SDimitry Andric   optind = 0;
1500b57cec5SDimitry Andric #else
1510b57cec5SDimitry Andric   optreset = 1;
1520b57cec5SDimitry Andric   optind = 1;
1530b57cec5SDimitry Andric #endif
1540b57cec5SDimitry Andric 
1550b57cec5SDimitry Andric   while ((ch = getopt_long_only(argc, argv, short_options.c_str(),
1560b57cec5SDimitry Andric                                 g_long_options, &long_option_index)) != -1) {
1570b57cec5SDimitry Andric     switch (ch) {
1580b57cec5SDimitry Andric     case 0: // Any optional that auto set themselves will return 0
1590b57cec5SDimitry Andric       break;
1600b57cec5SDimitry Andric 
1610b57cec5SDimitry Andric     case 'L':
1620b57cec5SDimitry Andric       listen_host_port.append(optarg);
1630b57cec5SDimitry Andric       break;
1640b57cec5SDimitry Andric 
1650b57cec5SDimitry Andric     case 'l': // Set Log File
1660b57cec5SDimitry Andric       if (optarg && optarg[0])
1670b57cec5SDimitry Andric         log_file.assign(optarg);
1680b57cec5SDimitry Andric       break;
1690b57cec5SDimitry Andric 
1700b57cec5SDimitry Andric     case 'c': // Log Channels
1710b57cec5SDimitry Andric       if (optarg && optarg[0])
1720b57cec5SDimitry Andric         log_channels = StringRef(optarg);
1730b57cec5SDimitry Andric       break;
1740b57cec5SDimitry Andric 
1750b57cec5SDimitry Andric     case 'f': // Socket file
1760b57cec5SDimitry Andric       if (optarg && optarg[0])
1770b57cec5SDimitry Andric         socket_file.SetFile(optarg, FileSpec::Style::native);
1780b57cec5SDimitry Andric       break;
1790b57cec5SDimitry Andric 
1800b57cec5SDimitry Andric     case 'p': {
1810b57cec5SDimitry Andric       if (!llvm::to_integer(optarg, port_offset)) {
182fe6060f1SDimitry Andric         WithColor::error() << "invalid port offset string " << optarg << "\n";
1830b57cec5SDimitry Andric         option_error = 4;
1840b57cec5SDimitry Andric         break;
1850b57cec5SDimitry Andric       }
1860b57cec5SDimitry Andric       if (port_offset < LOW_PORT || port_offset > HIGH_PORT) {
187fe6060f1SDimitry Andric         WithColor::error() << llvm::formatv(
188fe6060f1SDimitry Andric             "port offset {0} is not in the "
1890b57cec5SDimitry Andric             "valid user port range of {1} - {2}\n",
1900b57cec5SDimitry Andric             port_offset, LOW_PORT, HIGH_PORT);
1910b57cec5SDimitry Andric         option_error = 5;
1920b57cec5SDimitry Andric       }
1930b57cec5SDimitry Andric     } break;
1940b57cec5SDimitry Andric 
1950b57cec5SDimitry Andric     case 'P':
1960b57cec5SDimitry Andric     case 'm':
1970b57cec5SDimitry Andric     case 'M': {
1980b57cec5SDimitry Andric       uint16_t portnum;
1990b57cec5SDimitry Andric       if (!llvm::to_integer(optarg, portnum)) {
200fe6060f1SDimitry Andric         WithColor::error() << "invalid port number string " << optarg << "\n";
2010b57cec5SDimitry Andric         option_error = 2;
2020b57cec5SDimitry Andric         break;
2030b57cec5SDimitry Andric       }
2040b57cec5SDimitry Andric       if (portnum < LOW_PORT || portnum > HIGH_PORT) {
205fe6060f1SDimitry Andric         WithColor::error() << llvm::formatv(
206fe6060f1SDimitry Andric             "port number {0} is not in the "
2070b57cec5SDimitry Andric             "valid user port range of {1} - {2}\n",
2080b57cec5SDimitry Andric             portnum, LOW_PORT, HIGH_PORT);
2090b57cec5SDimitry Andric         option_error = 1;
2100b57cec5SDimitry Andric         break;
2110b57cec5SDimitry Andric       }
2120b57cec5SDimitry Andric       if (ch == 'P')
213e8d8bef9SDimitry Andric         gdbserver_portmap.AllowPort(portnum);
2140b57cec5SDimitry Andric       else if (ch == 'm')
2150b57cec5SDimitry Andric         min_gdbserver_port = portnum;
2160b57cec5SDimitry Andric       else
2170b57cec5SDimitry Andric         max_gdbserver_port = portnum;
2180b57cec5SDimitry Andric     } break;
2190b57cec5SDimitry Andric 
2200b57cec5SDimitry Andric     case 'h': /* fall-through is intentional */
2210b57cec5SDimitry Andric     case '?':
2220b57cec5SDimitry Andric       show_usage = true;
2230b57cec5SDimitry Andric       break;
2240b57cec5SDimitry Andric     }
2250b57cec5SDimitry Andric   }
2260b57cec5SDimitry Andric 
2270b57cec5SDimitry Andric   if (!LLDBServerUtilities::SetupLogging(log_file, log_channels, 0))
2280b57cec5SDimitry Andric     return -1;
2290b57cec5SDimitry Andric 
2300b57cec5SDimitry Andric   // Make a port map for a port range that was specified.
2310b57cec5SDimitry Andric   if (min_gdbserver_port && min_gdbserver_port < max_gdbserver_port) {
232e8d8bef9SDimitry Andric     gdbserver_portmap = GDBRemoteCommunicationServerPlatform::PortMap(
233e8d8bef9SDimitry Andric         min_gdbserver_port, max_gdbserver_port);
2340b57cec5SDimitry Andric   } else if (min_gdbserver_port || max_gdbserver_port) {
235fe6060f1SDimitry Andric     WithColor::error() << llvm::formatv(
236fe6060f1SDimitry Andric         "--min-gdbserver-port ({0}) is not lower than "
237fe6060f1SDimitry Andric         "--max-gdbserver-port ({1})\n",
2380b57cec5SDimitry Andric         min_gdbserver_port, max_gdbserver_port);
2390b57cec5SDimitry Andric     option_error = 3;
2400b57cec5SDimitry Andric   }
2410b57cec5SDimitry Andric 
2420b57cec5SDimitry Andric   // Print usage and exit if no listening port is specified.
2430b57cec5SDimitry Andric   if (listen_host_port.empty())
2440b57cec5SDimitry Andric     show_usage = true;
2450b57cec5SDimitry Andric 
2460b57cec5SDimitry Andric   if (show_usage || option_error) {
2470b57cec5SDimitry Andric     display_usage(progname, subcommand);
2480b57cec5SDimitry Andric     exit(option_error);
2490b57cec5SDimitry Andric   }
2500b57cec5SDimitry Andric 
2510b57cec5SDimitry Andric   // Skip any options we consumed with getopt_long_only.
2520b57cec5SDimitry Andric   argc -= optind;
2530b57cec5SDimitry Andric   argv += optind;
2540b57cec5SDimitry Andric   lldb_private::Args inferior_arguments;
2550b57cec5SDimitry Andric   inferior_arguments.SetArguments(argc, const_cast<const char **>(argv));
2560b57cec5SDimitry Andric 
2570b57cec5SDimitry Andric   const bool children_inherit_listen_socket = false;
2580b57cec5SDimitry Andric   // the test suite makes many connections in parallel, let's not miss any.
2590b57cec5SDimitry Andric   // The highest this should get reasonably is a function of the number
2600b57cec5SDimitry Andric   // of target CPUs. For now, let's just use 100.
2610b57cec5SDimitry Andric   const int backlog = 100;
2620b57cec5SDimitry Andric 
2630b57cec5SDimitry Andric   std::unique_ptr<Acceptor> acceptor_up(Acceptor::Create(
2640b57cec5SDimitry Andric       listen_host_port, children_inherit_listen_socket, error));
2650b57cec5SDimitry Andric   if (error.Fail()) {
2660b57cec5SDimitry Andric     fprintf(stderr, "failed to create acceptor: %s", error.AsCString());
2670b57cec5SDimitry Andric     exit(socket_error);
2680b57cec5SDimitry Andric   }
2690b57cec5SDimitry Andric 
2700b57cec5SDimitry Andric   error = acceptor_up->Listen(backlog);
2710b57cec5SDimitry Andric   if (error.Fail()) {
2720b57cec5SDimitry Andric     printf("failed to listen: %s\n", error.AsCString());
2730b57cec5SDimitry Andric     exit(socket_error);
2740b57cec5SDimitry Andric   }
2750b57cec5SDimitry Andric   if (socket_file) {
2760b57cec5SDimitry Andric     error =
2770b57cec5SDimitry Andric         save_socket_id_to_file(acceptor_up->GetLocalSocketId(), socket_file);
2780b57cec5SDimitry Andric     if (error.Fail()) {
2790b57cec5SDimitry Andric       fprintf(stderr, "failed to write socket id to %s: %s\n",
2800b57cec5SDimitry Andric               socket_file.GetPath().c_str(), error.AsCString());
2810b57cec5SDimitry Andric       return 1;
2820b57cec5SDimitry Andric     }
2830b57cec5SDimitry Andric   }
2840b57cec5SDimitry Andric 
2850b57cec5SDimitry Andric   do {
2860b57cec5SDimitry Andric     GDBRemoteCommunicationServerPlatform platform(
2870b57cec5SDimitry Andric         acceptor_up->GetSocketProtocol(), acceptor_up->GetSocketScheme());
2880b57cec5SDimitry Andric 
2890b57cec5SDimitry Andric     if (port_offset > 0)
2900b57cec5SDimitry Andric       platform.SetPortOffset(port_offset);
2910b57cec5SDimitry Andric 
2920b57cec5SDimitry Andric     if (!gdbserver_portmap.empty()) {
2930b57cec5SDimitry Andric       platform.SetPortMap(std::move(gdbserver_portmap));
2940b57cec5SDimitry Andric     }
2950b57cec5SDimitry Andric 
2960b57cec5SDimitry Andric     const bool children_inherit_accept_socket = true;
2970b57cec5SDimitry Andric     Connection *conn = nullptr;
2980b57cec5SDimitry Andric     error = acceptor_up->Accept(children_inherit_accept_socket, conn);
2990b57cec5SDimitry Andric     if (error.Fail()) {
300fe6060f1SDimitry Andric       WithColor::error() << error.AsCString() << '\n';
3010b57cec5SDimitry Andric       exit(socket_error);
3020b57cec5SDimitry Andric     }
3030b57cec5SDimitry Andric     printf("Connection established.\n");
3040b57cec5SDimitry Andric     if (g_server) {
3050b57cec5SDimitry Andric       // Collect child zombie processes.
3069dba64beSDimitry Andric #if !defined(_WIN32)
3070b57cec5SDimitry Andric       while (waitpid(-1, nullptr, WNOHANG) > 0)
3080b57cec5SDimitry Andric         ;
3099dba64beSDimitry Andric #endif
3100b57cec5SDimitry Andric       if (fork()) {
3110b57cec5SDimitry Andric         // Parent doesn't need a connection to the lldb client
3120b57cec5SDimitry Andric         delete conn;
3130b57cec5SDimitry Andric 
3140b57cec5SDimitry Andric         // Parent will continue to listen for new connections.
3150b57cec5SDimitry Andric         continue;
3160b57cec5SDimitry Andric       } else {
3170b57cec5SDimitry Andric         // Child process will handle the connection and exit.
3180b57cec5SDimitry Andric         g_server = 0;
3190b57cec5SDimitry Andric         // Listening socket is owned by parent process.
3200b57cec5SDimitry Andric         acceptor_up.release();
3210b57cec5SDimitry Andric       }
3220b57cec5SDimitry Andric     } else {
3230b57cec5SDimitry Andric       // If not running as a server, this process will not accept
3240b57cec5SDimitry Andric       // connections while a connection is active.
3250b57cec5SDimitry Andric       acceptor_up.reset();
3260b57cec5SDimitry Andric     }
3275ffd83dbSDimitry Andric     platform.SetConnection(std::unique_ptr<Connection>(conn));
3280b57cec5SDimitry Andric 
3290b57cec5SDimitry Andric     if (platform.IsConnected()) {
3300b57cec5SDimitry Andric       if (inferior_arguments.GetArgumentCount() > 0) {
3310b57cec5SDimitry Andric         lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
332bdd1243dSDimitry Andric         std::optional<uint16_t> port = 0;
3330b57cec5SDimitry Andric         std::string socket_name;
3340b57cec5SDimitry Andric         Status error = platform.LaunchGDBServer(inferior_arguments,
3350b57cec5SDimitry Andric                                                 "", // hostname
3360b57cec5SDimitry Andric                                                 pid, port, socket_name);
3370b57cec5SDimitry Andric         if (error.Success())
338e8d8bef9SDimitry Andric           platform.SetPendingGdbServer(pid, *port, socket_name);
3390b57cec5SDimitry Andric         else
3400b57cec5SDimitry Andric           fprintf(stderr, "failed to start gdbserver: %s\n", error.AsCString());
3410b57cec5SDimitry Andric       }
3420b57cec5SDimitry Andric 
3430b57cec5SDimitry Andric       bool interrupt = false;
3440b57cec5SDimitry Andric       bool done = false;
3450b57cec5SDimitry Andric       while (!interrupt && !done) {
346bdd1243dSDimitry Andric         if (platform.GetPacketAndSendResponse(std::nullopt, error, interrupt,
3470b57cec5SDimitry Andric                                               done) !=
3480b57cec5SDimitry Andric             GDBRemoteCommunication::PacketResult::Success)
3490b57cec5SDimitry Andric           break;
3500b57cec5SDimitry Andric       }
3510b57cec5SDimitry Andric 
3524824e7fdSDimitry Andric       if (error.Fail())
353fe6060f1SDimitry Andric         WithColor::error() << error.AsCString() << '\n';
3540b57cec5SDimitry Andric     }
3550b57cec5SDimitry Andric   } while (g_server);
3560b57cec5SDimitry Andric 
3570b57cec5SDimitry Andric   fprintf(stderr, "lldb-server exiting...\n");
3580b57cec5SDimitry Andric 
3590b57cec5SDimitry Andric   return 0;
3600b57cec5SDimitry Andric }
361