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