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