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