1 //===-- lldb-platform.cpp ---------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include <cerrno> 10 #if defined(__APPLE__) 11 #include <netinet/in.h> 12 #endif 13 #include <csignal> 14 #include <cstdint> 15 #include <cstdio> 16 #include <cstdlib> 17 #include <cstring> 18 #if !defined(_WIN32) 19 #include <sys/wait.h> 20 #endif 21 #include <fstream> 22 #include <optional> 23 24 #include "llvm/Support/FileSystem.h" 25 #include "llvm/Support/WithColor.h" 26 #include "llvm/Support/raw_ostream.h" 27 28 #include "Acceptor.h" 29 #include "LLDBServerUtilities.h" 30 #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h" 31 #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h" 32 #include "lldb/Host/ConnectionFileDescriptor.h" 33 #include "lldb/Host/HostGetOpt.h" 34 #include "lldb/Host/OptionParser.h" 35 #include "lldb/Host/common/TCPSocket.h" 36 #include "lldb/Utility/FileSpec.h" 37 #include "lldb/Utility/Status.h" 38 39 using namespace lldb; 40 using namespace lldb_private; 41 using namespace lldb_private::lldb_server; 42 using namespace lldb_private::process_gdb_remote; 43 using namespace llvm; 44 45 // option descriptors for getopt_long_only() 46 47 static int g_debug = 0; 48 static int g_verbose = 0; 49 static int g_server = 0; 50 51 static struct option g_long_options[] = { 52 {"debug", no_argument, &g_debug, 1}, 53 {"verbose", no_argument, &g_verbose, 1}, 54 {"log-file", required_argument, nullptr, 'l'}, 55 {"log-channels", required_argument, nullptr, 'c'}, 56 {"listen", required_argument, nullptr, 'L'}, 57 {"port-offset", required_argument, nullptr, 'p'}, 58 {"gdbserver-port", required_argument, nullptr, 'P'}, 59 {"min-gdbserver-port", required_argument, nullptr, 'm'}, 60 {"max-gdbserver-port", required_argument, nullptr, 'M'}, 61 {"socket-file", required_argument, nullptr, 'f'}, 62 {"server", no_argument, &g_server, 1}, 63 {nullptr, 0, nullptr, 0}}; 64 65 #if defined(__APPLE__) 66 #define LOW_PORT (IPPORT_RESERVED) 67 #define HIGH_PORT (IPPORT_HIFIRSTAUTO) 68 #else 69 #define LOW_PORT (1024u) 70 #define HIGH_PORT (49151u) 71 #endif 72 73 #if !defined(_WIN32) 74 // Watch for signals 75 static void signal_handler(int signo) { 76 switch (signo) { 77 case SIGHUP: 78 // Use SIGINT first, if that does not work, use SIGHUP as a last resort. 79 // And we should not call exit() here because it results in the global 80 // destructors to be invoked and wreaking havoc on the threads still 81 // running. 82 llvm::errs() << "SIGHUP received, exiting lldb-server...\n"; 83 abort(); 84 break; 85 } 86 } 87 #endif 88 89 static void display_usage(const char *progname, const char *subcommand) { 90 fprintf(stderr, "Usage:\n %s %s [--log-file log-file-name] [--log-channels " 91 "log-channel-list] [--port-file port-file-path] --server " 92 "--listen port\n", 93 progname, subcommand); 94 exit(0); 95 } 96 97 static Status save_socket_id_to_file(const std::string &socket_id, 98 const FileSpec &file_spec) { 99 FileSpec temp_file_spec(file_spec.GetDirectory().GetStringRef()); 100 Status error(llvm::sys::fs::create_directory(temp_file_spec.GetPath())); 101 if (error.Fail()) 102 return Status("Failed to create directory %s: %s", 103 temp_file_spec.GetPath().c_str(), error.AsCString()); 104 105 Status status; 106 if (auto Err = llvm::writeToOutput(file_spec.GetPath(), 107 [&socket_id](llvm::raw_ostream &OS) { 108 OS << socket_id; 109 return llvm::Error::success(); 110 })) 111 return Status("Failed to atomically write file %s: %s", 112 file_spec.GetPath().c_str(), 113 llvm::toString(std::move(Err)).c_str()); 114 return status; 115 } 116 117 // main 118 int main_platform(int argc, char *argv[]) { 119 const char *progname = argv[0]; 120 const char *subcommand = argv[1]; 121 argc--; 122 argv++; 123 #if !defined(_WIN32) 124 signal(SIGPIPE, SIG_IGN); 125 signal(SIGHUP, signal_handler); 126 #endif 127 int long_option_index = 0; 128 Status error; 129 std::string listen_host_port; 130 int ch; 131 132 std::string log_file; 133 StringRef 134 log_channels; // e.g. "lldb process threads:gdb-remote default:linux all" 135 136 GDBRemoteCommunicationServerPlatform::PortMap gdbserver_portmap; 137 int min_gdbserver_port = 0; 138 int max_gdbserver_port = 0; 139 uint16_t port_offset = 0; 140 141 FileSpec socket_file; 142 bool show_usage = false; 143 int option_error = 0; 144 int socket_error = -1; 145 146 std::string short_options(OptionParser::GetShortOptionString(g_long_options)); 147 148 #if __GLIBC__ 149 optind = 0; 150 #else 151 optreset = 1; 152 optind = 1; 153 #endif 154 155 while ((ch = getopt_long_only(argc, argv, short_options.c_str(), 156 g_long_options, &long_option_index)) != -1) { 157 switch (ch) { 158 case 0: // Any optional that auto set themselves will return 0 159 break; 160 161 case 'L': 162 listen_host_port.append(optarg); 163 break; 164 165 case 'l': // Set Log File 166 if (optarg && optarg[0]) 167 log_file.assign(optarg); 168 break; 169 170 case 'c': // Log Channels 171 if (optarg && optarg[0]) 172 log_channels = StringRef(optarg); 173 break; 174 175 case 'f': // Socket file 176 if (optarg && optarg[0]) 177 socket_file.SetFile(optarg, FileSpec::Style::native); 178 break; 179 180 case 'p': { 181 if (!llvm::to_integer(optarg, port_offset)) { 182 WithColor::error() << "invalid port offset string " << optarg << "\n"; 183 option_error = 4; 184 break; 185 } 186 if (port_offset < LOW_PORT || port_offset > HIGH_PORT) { 187 WithColor::error() << llvm::formatv( 188 "port offset {0} is not in the " 189 "valid user port range of {1} - {2}\n", 190 port_offset, LOW_PORT, HIGH_PORT); 191 option_error = 5; 192 } 193 } break; 194 195 case 'P': 196 case 'm': 197 case 'M': { 198 uint16_t portnum; 199 if (!llvm::to_integer(optarg, portnum)) { 200 WithColor::error() << "invalid port number string " << optarg << "\n"; 201 option_error = 2; 202 break; 203 } 204 if (portnum < LOW_PORT || portnum > HIGH_PORT) { 205 WithColor::error() << llvm::formatv( 206 "port number {0} is not in the " 207 "valid user port range of {1} - {2}\n", 208 portnum, LOW_PORT, HIGH_PORT); 209 option_error = 1; 210 break; 211 } 212 if (ch == 'P') 213 gdbserver_portmap.AllowPort(portnum); 214 else if (ch == 'm') 215 min_gdbserver_port = portnum; 216 else 217 max_gdbserver_port = portnum; 218 } break; 219 220 case 'h': /* fall-through is intentional */ 221 case '?': 222 show_usage = true; 223 break; 224 } 225 } 226 227 if (!LLDBServerUtilities::SetupLogging(log_file, log_channels, 0)) 228 return -1; 229 230 // Make a port map for a port range that was specified. 231 if (min_gdbserver_port && min_gdbserver_port < max_gdbserver_port) { 232 gdbserver_portmap = GDBRemoteCommunicationServerPlatform::PortMap( 233 min_gdbserver_port, max_gdbserver_port); 234 } else if (min_gdbserver_port || max_gdbserver_port) { 235 WithColor::error() << llvm::formatv( 236 "--min-gdbserver-port ({0}) is not lower than " 237 "--max-gdbserver-port ({1})\n", 238 min_gdbserver_port, max_gdbserver_port); 239 option_error = 3; 240 } 241 242 // Print usage and exit if no listening port is specified. 243 if (listen_host_port.empty()) 244 show_usage = true; 245 246 if (show_usage || option_error) { 247 display_usage(progname, subcommand); 248 exit(option_error); 249 } 250 251 // Skip any options we consumed with getopt_long_only. 252 argc -= optind; 253 argv += optind; 254 lldb_private::Args inferior_arguments; 255 inferior_arguments.SetArguments(argc, const_cast<const char **>(argv)); 256 257 const bool children_inherit_listen_socket = false; 258 // the test suite makes many connections in parallel, let's not miss any. 259 // The highest this should get reasonably is a function of the number 260 // of target CPUs. For now, let's just use 100. 261 const int backlog = 100; 262 263 std::unique_ptr<Acceptor> acceptor_up(Acceptor::Create( 264 listen_host_port, children_inherit_listen_socket, error)); 265 if (error.Fail()) { 266 fprintf(stderr, "failed to create acceptor: %s", error.AsCString()); 267 exit(socket_error); 268 } 269 270 error = acceptor_up->Listen(backlog); 271 if (error.Fail()) { 272 printf("failed to listen: %s\n", error.AsCString()); 273 exit(socket_error); 274 } 275 if (socket_file) { 276 error = 277 save_socket_id_to_file(acceptor_up->GetLocalSocketId(), socket_file); 278 if (error.Fail()) { 279 fprintf(stderr, "failed to write socket id to %s: %s\n", 280 socket_file.GetPath().c_str(), error.AsCString()); 281 return 1; 282 } 283 } 284 285 do { 286 GDBRemoteCommunicationServerPlatform platform( 287 acceptor_up->GetSocketProtocol(), acceptor_up->GetSocketScheme()); 288 289 if (port_offset > 0) 290 platform.SetPortOffset(port_offset); 291 292 if (!gdbserver_portmap.empty()) { 293 platform.SetPortMap(std::move(gdbserver_portmap)); 294 } 295 296 const bool children_inherit_accept_socket = true; 297 Connection *conn = nullptr; 298 error = acceptor_up->Accept(children_inherit_accept_socket, conn); 299 if (error.Fail()) { 300 WithColor::error() << error.AsCString() << '\n'; 301 exit(socket_error); 302 } 303 printf("Connection established.\n"); 304 if (g_server) { 305 // Collect child zombie processes. 306 #if !defined(_WIN32) 307 while (waitpid(-1, nullptr, WNOHANG) > 0) 308 ; 309 #endif 310 if (fork()) { 311 // Parent doesn't need a connection to the lldb client 312 delete conn; 313 314 // Parent will continue to listen for new connections. 315 continue; 316 } else { 317 // Child process will handle the connection and exit. 318 g_server = 0; 319 // Listening socket is owned by parent process. 320 acceptor_up.release(); 321 } 322 } else { 323 // If not running as a server, this process will not accept 324 // connections while a connection is active. 325 acceptor_up.reset(); 326 } 327 platform.SetConnection(std::unique_ptr<Connection>(conn)); 328 329 if (platform.IsConnected()) { 330 if (inferior_arguments.GetArgumentCount() > 0) { 331 lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; 332 std::optional<uint16_t> port = 0; 333 std::string socket_name; 334 Status error = platform.LaunchGDBServer(inferior_arguments, 335 "", // hostname 336 pid, port, socket_name); 337 if (error.Success()) 338 platform.SetPendingGdbServer(pid, *port, socket_name); 339 else 340 fprintf(stderr, "failed to start gdbserver: %s\n", error.AsCString()); 341 } 342 343 bool interrupt = false; 344 bool done = false; 345 while (!interrupt && !done) { 346 if (platform.GetPacketAndSendResponse(std::nullopt, error, interrupt, 347 done) != 348 GDBRemoteCommunication::PacketResult::Success) 349 break; 350 } 351 352 if (error.Fail()) 353 WithColor::error() << error.AsCString() << '\n'; 354 } 355 } while (g_server); 356 357 fprintf(stderr, "lldb-server exiting...\n"); 358 359 return 0; 360 } 361