xref: /llvm-project/lldb/tools/lldb-server/lldb-platform.cpp (revision 0642cd768b80665585c8500bed2933a3b99123dc)
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/ScopedPrinter.h"
26 #include "llvm/Support/WithColor.h"
27 #include "llvm/Support/raw_ostream.h"
28 
29 #include "Acceptor.h"
30 #include "LLDBServerUtilities.h"
31 #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h"
32 #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
33 #include "lldb/Host/ConnectionFileDescriptor.h"
34 #include "lldb/Host/HostGetOpt.h"
35 #include "lldb/Host/OptionParser.h"
36 #include "lldb/Host/Socket.h"
37 #include "lldb/Host/common/TCPSocket.h"
38 #include "lldb/Utility/FileSpec.h"
39 #include "lldb/Utility/LLDBLog.h"
40 #include "lldb/Utility/Status.h"
41 
42 using namespace lldb;
43 using namespace lldb_private;
44 using namespace lldb_private::lldb_server;
45 using namespace lldb_private::process_gdb_remote;
46 using namespace llvm;
47 
48 // option descriptors for getopt_long_only()
49 
50 static int g_debug = 0;
51 static int g_verbose = 0;
52 static int g_server = 0;
53 
54 static struct option g_long_options[] = {
55     {"debug", no_argument, &g_debug, 1},
56     {"verbose", no_argument, &g_verbose, 1},
57     {"log-file", required_argument, nullptr, 'l'},
58     {"log-channels", required_argument, nullptr, 'c'},
59     {"listen", required_argument, nullptr, 'L'},
60     {"port-offset", required_argument, nullptr, 'p'},
61     {"gdbserver-port", required_argument, nullptr, 'P'},
62     {"min-gdbserver-port", required_argument, nullptr, 'm'},
63     {"max-gdbserver-port", required_argument, nullptr, 'M'},
64     {"socket-file", required_argument, nullptr, 'f'},
65     {"server", no_argument, &g_server, 1},
66     {"child-platform-fd", required_argument, nullptr, 2},
67     {nullptr, 0, nullptr, 0}};
68 
69 #if defined(__APPLE__)
70 #define LOW_PORT (IPPORT_RESERVED)
71 #define HIGH_PORT (IPPORT_HIFIRSTAUTO)
72 #else
73 #define LOW_PORT (1024u)
74 #define HIGH_PORT (49151u)
75 #endif
76 
77 #if !defined(_WIN32)
78 // Watch for signals
79 static void signal_handler(int signo) {
80   switch (signo) {
81   case SIGHUP:
82     // Use SIGINT first, if that does not work, use SIGHUP as a last resort.
83     // And we should not call exit() here because it results in the global
84     // destructors to be invoked and wreaking havoc on the threads still
85     // running.
86     llvm::errs() << "SIGHUP received, exiting lldb-server...\n";
87     abort();
88     break;
89   }
90 }
91 #endif
92 
93 static void display_usage(const char *progname, const char *subcommand) {
94   fprintf(stderr, "Usage:\n  %s %s [--log-file log-file-name] [--log-channels "
95                   "log-channel-list] [--port-file port-file-path] --server "
96                   "--listen port\n",
97           progname, subcommand);
98   exit(0);
99 }
100 
101 static Status save_socket_id_to_file(const std::string &socket_id,
102                                      const FileSpec &file_spec) {
103   FileSpec temp_file_spec(file_spec.GetDirectory().GetStringRef());
104   Status error(llvm::sys::fs::create_directory(temp_file_spec.GetPath()));
105   if (error.Fail())
106     return Status::FromErrorStringWithFormat(
107         "Failed to create directory %s: %s", temp_file_spec.GetPath().c_str(),
108         error.AsCString());
109 
110   Status status;
111   if (auto Err = llvm::writeToOutput(file_spec.GetPath(),
112                                      [&socket_id](llvm::raw_ostream &OS) {
113                                        OS << socket_id;
114                                        return llvm::Error::success();
115                                      }))
116     return Status::FromErrorStringWithFormat(
117         "Failed to atomically write file %s: %s", file_spec.GetPath().c_str(),
118         llvm::toString(std::move(Err)).c_str());
119   return status;
120 }
121 
122 static void client_handle(GDBRemoteCommunicationServerPlatform &platform,
123                           const lldb_private::Args &args) {
124   if (!platform.IsConnected())
125     return;
126 
127   if (args.GetArgumentCount() > 0) {
128     lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
129     std::optional<uint16_t> port;
130     std::string socket_name;
131     Status error = platform.LaunchGDBServer(args,
132                                             "", // hostname
133                                             pid, port, socket_name);
134     if (error.Success())
135       platform.SetPendingGdbServer(pid, *port, socket_name);
136     else
137       fprintf(stderr, "failed to start gdbserver: %s\n", error.AsCString());
138   }
139 
140   bool interrupt = false;
141   bool done = false;
142   Status error;
143   while (!interrupt && !done) {
144     if (platform.GetPacketAndSendResponse(std::nullopt, error, interrupt,
145                                           done) !=
146         GDBRemoteCommunication::PacketResult::Success)
147       break;
148   }
149 
150   printf("Disconnected.\n");
151 }
152 
153 static GDBRemoteCommunicationServerPlatform::PortMap gdbserver_portmap;
154 static std::mutex gdbserver_portmap_mutex;
155 
156 static void spawn_process_reaped(lldb::pid_t pid, int signal, int status) {
157   std::lock_guard<std::mutex> guard(gdbserver_portmap_mutex);
158   gdbserver_portmap.FreePortForProcess(pid);
159 }
160 
161 static Status spawn_process(const char *progname, const Socket *conn_socket,
162                             uint16_t gdb_port, uint16_t port_offset,
163                             const lldb_private::Args &args,
164                             const std::string &log_file,
165                             const StringRef log_channels) {
166   Status error;
167   SharedSocket shared_socket(conn_socket, error);
168   if (error.Fail())
169     return error;
170 
171   ProcessLaunchInfo launch_info;
172 
173   FileSpec self_spec(progname, FileSpec::Style::native);
174   launch_info.SetExecutableFile(self_spec, true);
175   Args &self_args = launch_info.GetArguments();
176   self_args.AppendArgument(llvm::StringRef("platform"));
177   self_args.AppendArgument(llvm::StringRef("--child-platform-fd"));
178   self_args.AppendArgument(llvm::to_string(shared_socket.GetSendableFD()));
179   if (gdb_port) {
180     self_args.AppendArgument(llvm::StringRef("--gdbserver-port"));
181     self_args.AppendArgument(llvm::to_string(gdb_port));
182   }
183   if (port_offset > 0) {
184     self_args.AppendArgument(llvm::StringRef("--port-offset"));
185     self_args.AppendArgument(llvm::to_string(port_offset));
186   }
187   if (!log_file.empty()) {
188     self_args.AppendArgument(llvm::StringRef("--log-file"));
189     self_args.AppendArgument(log_file);
190   }
191   if (!log_channels.empty()) {
192     self_args.AppendArgument(llvm::StringRef("--log-channels"));
193     self_args.AppendArgument(log_channels);
194   }
195   if (args.GetArgumentCount() > 0) {
196     self_args.AppendArgument("--");
197     self_args.AppendArguments(args);
198   }
199 
200   launch_info.SetLaunchInSeparateProcessGroup(false);
201   launch_info.SetMonitorProcessCallback(&spawn_process_reaped);
202 
203   // Copy the current environment.
204   launch_info.GetEnvironment() = Host::GetEnvironment();
205 
206   launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO);
207 
208   // Close STDIN, STDOUT and STDERR.
209   launch_info.AppendCloseFileAction(STDIN_FILENO);
210   launch_info.AppendCloseFileAction(STDOUT_FILENO);
211   launch_info.AppendCloseFileAction(STDERR_FILENO);
212 
213   // Redirect STDIN, STDOUT and STDERR to "/dev/null".
214   launch_info.AppendSuppressFileAction(STDIN_FILENO, true, false);
215   launch_info.AppendSuppressFileAction(STDOUT_FILENO, false, true);
216   launch_info.AppendSuppressFileAction(STDERR_FILENO, false, true);
217 
218   std::string cmd;
219   self_args.GetCommandString(cmd);
220 
221   error = Host::LaunchProcess(launch_info);
222   if (error.Fail())
223     return error;
224 
225   lldb::pid_t child_pid = launch_info.GetProcessID();
226   if (child_pid == LLDB_INVALID_PROCESS_ID)
227     return Status::FromErrorString("invalid pid");
228 
229   LLDB_LOG(GetLog(LLDBLog::Platform), "lldb-platform launched '{0}', pid={1}",
230            cmd, child_pid);
231 
232   {
233     std::lock_guard<std::mutex> guard(gdbserver_portmap_mutex);
234     gdbserver_portmap.AssociatePortWithProcess(gdb_port, child_pid);
235   }
236 
237   error = shared_socket.CompleteSending(child_pid);
238   if (error.Fail()) {
239     Host::Kill(child_pid, SIGTERM);
240     return error;
241   }
242 
243   return Status();
244 }
245 
246 // main
247 int main_platform(int argc, char *argv[]) {
248   const char *progname = argv[0];
249   const char *subcommand = argv[1];
250   argc--;
251   argv++;
252 #if !defined(_WIN32)
253   signal(SIGPIPE, SIG_IGN);
254   signal(SIGHUP, signal_handler);
255 #endif
256   int long_option_index = 0;
257   Status error;
258   std::string listen_host_port;
259   int ch;
260 
261   std::string log_file;
262   StringRef
263       log_channels; // e.g. "lldb process threads:gdb-remote default:linux all"
264 
265   shared_fd_t fd = SharedSocket::kInvalidFD;
266 
267   int min_gdbserver_port = 0;
268   int max_gdbserver_port = 0;
269   uint16_t port_offset = 0;
270 
271   FileSpec socket_file;
272   bool show_usage = false;
273   int option_error = 0;
274   int socket_error = -1;
275 
276   std::string short_options(OptionParser::GetShortOptionString(g_long_options));
277 
278 #if __GLIBC__
279   optind = 0;
280 #else
281   optreset = 1;
282   optind = 1;
283 #endif
284 
285   while ((ch = getopt_long_only(argc, argv, short_options.c_str(),
286                                 g_long_options, &long_option_index)) != -1) {
287     switch (ch) {
288     case 0: // Any optional that auto set themselves will return 0
289       break;
290 
291     case 'L':
292       listen_host_port.append(optarg);
293       break;
294 
295     case 'l': // Set Log File
296       if (optarg && optarg[0])
297         log_file.assign(optarg);
298       break;
299 
300     case 'c': // Log Channels
301       if (optarg && optarg[0])
302         log_channels = StringRef(optarg);
303       break;
304 
305     case 'f': // Socket file
306       if (optarg && optarg[0])
307         socket_file.SetFile(optarg, FileSpec::Style::native);
308       break;
309 
310     case 'p': {
311       if (!llvm::to_integer(optarg, port_offset)) {
312         WithColor::error() << "invalid port offset string " << optarg << "\n";
313         option_error = 4;
314         break;
315       }
316       if (port_offset < LOW_PORT || port_offset > HIGH_PORT) {
317         WithColor::error() << llvm::formatv(
318             "port offset {0} is not in the "
319             "valid user port range of {1} - {2}\n",
320             port_offset, LOW_PORT, HIGH_PORT);
321         option_error = 5;
322       }
323     } break;
324 
325     case 'P':
326     case 'm':
327     case 'M': {
328       uint16_t portnum;
329       if (!llvm::to_integer(optarg, portnum)) {
330         WithColor::error() << "invalid port number string " << optarg << "\n";
331         option_error = 2;
332         break;
333       }
334       if (portnum < LOW_PORT || portnum > HIGH_PORT) {
335         WithColor::error() << llvm::formatv(
336             "port number {0} is not in the "
337             "valid user port range of {1} - {2}\n",
338             portnum, LOW_PORT, HIGH_PORT);
339         option_error = 1;
340         break;
341       }
342       if (ch == 'P')
343         gdbserver_portmap.AllowPort(portnum);
344       else if (ch == 'm')
345         min_gdbserver_port = portnum;
346       else
347         max_gdbserver_port = portnum;
348     } break;
349 
350     case 2: {
351       uint64_t _fd;
352       if (!llvm::to_integer(optarg, _fd)) {
353         WithColor::error() << "invalid fd " << optarg << "\n";
354         option_error = 6;
355       } else
356         fd = (shared_fd_t)_fd;
357     } break;
358 
359     case 'h': /* fall-through is intentional */
360     case '?':
361       show_usage = true;
362       break;
363     }
364   }
365 
366   if (!LLDBServerUtilities::SetupLogging(log_file, log_channels, 0))
367     return -1;
368 
369   // Make a port map for a port range that was specified.
370   if (min_gdbserver_port && min_gdbserver_port < max_gdbserver_port) {
371     gdbserver_portmap = GDBRemoteCommunicationServerPlatform::PortMap(
372         min_gdbserver_port, max_gdbserver_port);
373   } else if (min_gdbserver_port || max_gdbserver_port) {
374     WithColor::error() << llvm::formatv(
375         "--min-gdbserver-port ({0}) is not lower than "
376         "--max-gdbserver-port ({1})\n",
377         min_gdbserver_port, max_gdbserver_port);
378     option_error = 3;
379   }
380 
381   // Print usage and exit if no listening port is specified.
382   if (listen_host_port.empty() && fd == SharedSocket::kInvalidFD)
383     show_usage = true;
384 
385   if (show_usage || option_error) {
386     display_usage(progname, subcommand);
387     exit(option_error);
388   }
389 
390   // Skip any options we consumed with getopt_long_only.
391   argc -= optind;
392   argv += optind;
393   lldb_private::Args inferior_arguments;
394   inferior_arguments.SetArguments(argc, const_cast<const char **>(argv));
395 
396   if (fd != SharedSocket::kInvalidFD) {
397     // Child process will handle the connection and exit.
398     Log *log = GetLog(LLDBLog::Platform);
399     if (!listen_host_port.empty()) {
400       LLDB_LOGF(log, "lldb-platform child: "
401                      "ambiguous parameters --listen and --child-platform-fd");
402       return socket_error;
403     }
404 
405     NativeSocket socket;
406     error = SharedSocket::GetNativeSocket(fd, socket);
407     if (error.Fail()) {
408       LLDB_LOGF(log, "lldb-platform child: %s", error.AsCString());
409       return socket_error;
410     }
411 
412     GDBRemoteCommunicationServerPlatform platform(Socket::ProtocolTcp, "tcp");
413     if (port_offset > 0)
414       platform.SetPortOffset(port_offset);
415     platform.SetPortMap(std::move(gdbserver_portmap));
416     platform.SetConnection(
417         std::unique_ptr<Connection>(new ConnectionFileDescriptor(
418             new TCPSocket(socket, /*should_close=*/true,
419                           /*child_processes_inherit=*/false))));
420     client_handle(platform, inferior_arguments);
421     return 0;
422   }
423 
424   const bool children_inherit_listen_socket = false;
425   // the test suite makes many connections in parallel, let's not miss any.
426   // The highest this should get reasonably is a function of the number
427   // of target CPUs. For now, let's just use 100.
428   const int backlog = 100;
429 
430   std::unique_ptr<Acceptor> acceptor_up(Acceptor::Create(
431       listen_host_port, children_inherit_listen_socket, error));
432   if (error.Fail()) {
433     fprintf(stderr, "failed to create acceptor: %s", error.AsCString());
434     exit(socket_error);
435   }
436 
437   error = acceptor_up->Listen(backlog);
438   if (error.Fail()) {
439     printf("failed to listen: %s\n", error.AsCString());
440     exit(socket_error);
441   }
442   if (socket_file) {
443     error =
444         save_socket_id_to_file(acceptor_up->GetLocalSocketId(), socket_file);
445     if (error.Fail()) {
446       fprintf(stderr, "failed to write socket id to %s: %s\n",
447               socket_file.GetPath().c_str(), error.AsCString());
448       return 1;
449     }
450   }
451 
452   GDBRemoteCommunicationServerPlatform platform(
453       acceptor_up->GetSocketProtocol(), acceptor_up->GetSocketScheme());
454   if (port_offset > 0)
455     platform.SetPortOffset(port_offset);
456 
457   do {
458     const bool children_inherit_accept_socket = true;
459     Connection *conn = nullptr;
460     error = acceptor_up->Accept(children_inherit_accept_socket, conn);
461     if (error.Fail()) {
462       WithColor::error() << error.AsCString() << '\n';
463       exit(socket_error);
464     }
465     printf("Connection established.\n");
466 
467     if (g_server) {
468       std::optional<uint16_t> available_port;
469       {
470         std::lock_guard<std::mutex> guard(gdbserver_portmap_mutex);
471         auto port = gdbserver_portmap.GetNextAvailablePort();
472         if (port)
473           available_port = *port;
474         else
475           llvm::consumeError(port.takeError());
476       }
477       if (!available_port) {
478         fprintf(stderr,
479                 "no available gdbserver port for connection - dropping...\n");
480       } else {
481         const Socket *conn_socket =
482             static_cast<const Socket *>(conn->GetReadObject().get());
483         error =
484             spawn_process(progname, conn_socket, *available_port, port_offset,
485                           inferior_arguments, log_file, log_channels);
486         if (error.Fail()) {
487           {
488 
489             std::lock_guard<std::mutex> guard(gdbserver_portmap_mutex);
490             gdbserver_portmap.FreePort(*available_port);
491           }
492           LLDB_LOGF(GetLog(LLDBLog::Platform), "spawn_process failed: %s",
493                     error.AsCString());
494           WithColor::error()
495               << "spawn_process failed: " << error.AsCString() << "\n";
496         }
497       }
498       // Parent doesn't need a connection to the lldb client
499       delete conn;
500 
501       // Parent will continue to listen for new connections.
502       continue;
503     } else {
504       // If not running as a server, this process will not accept
505       // connections while a connection is active.
506       acceptor_up.reset();
507 
508       // When not running in server mode, use all available ports
509       platform.SetPortMap(std::move(gdbserver_portmap));
510     }
511 
512     platform.SetConnection(std::unique_ptr<Connection>(conn));
513     client_handle(platform, inferior_arguments);
514   } while (g_server);
515 
516   fprintf(stderr, "lldb-server exiting...\n");
517 
518   return 0;
519 }
520