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