xref: /openbsd-src/gnu/llvm/lldb/tools/lldb-server/lldb-platform.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1061da546Spatrick //===-- lldb-platform.cpp ---------------------------------------*- C++ -*-===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9be691f3bSpatrick #include <cerrno>
10061da546Spatrick #if defined(__APPLE__)
11061da546Spatrick #include <netinet/in.h>
12061da546Spatrick #endif
13be691f3bSpatrick #include <csignal>
14be691f3bSpatrick #include <cstdint>
15be691f3bSpatrick #include <cstdio>
16be691f3bSpatrick #include <cstdlib>
17be691f3bSpatrick #include <cstring>
18061da546Spatrick #if !defined(_WIN32)
19061da546Spatrick #include <sys/wait.h>
20061da546Spatrick #endif
21061da546Spatrick #include <fstream>
22*f6aab3d8Srobert #include <optional>
23061da546Spatrick 
24061da546Spatrick #include "llvm/Support/FileSystem.h"
25061da546Spatrick #include "llvm/Support/FileUtilities.h"
26be691f3bSpatrick #include "llvm/Support/WithColor.h"
27061da546Spatrick #include "llvm/Support/raw_ostream.h"
28061da546Spatrick 
29061da546Spatrick #include "Acceptor.h"
30061da546Spatrick #include "LLDBServerUtilities.h"
31061da546Spatrick #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h"
32061da546Spatrick #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
33061da546Spatrick #include "lldb/Host/ConnectionFileDescriptor.h"
34061da546Spatrick #include "lldb/Host/HostGetOpt.h"
35061da546Spatrick #include "lldb/Host/OptionParser.h"
36061da546Spatrick #include "lldb/Host/common/TCPSocket.h"
37061da546Spatrick #include "lldb/Utility/FileSpec.h"
38061da546Spatrick #include "lldb/Utility/Status.h"
39061da546Spatrick 
40061da546Spatrick using namespace lldb;
41061da546Spatrick using namespace lldb_private;
42061da546Spatrick using namespace lldb_private::lldb_server;
43061da546Spatrick using namespace lldb_private::process_gdb_remote;
44061da546Spatrick using namespace llvm;
45061da546Spatrick 
46061da546Spatrick // option descriptors for getopt_long_only()
47061da546Spatrick 
48061da546Spatrick static int g_debug = 0;
49061da546Spatrick static int g_verbose = 0;
50061da546Spatrick static int g_server = 0;
51061da546Spatrick 
52061da546Spatrick static struct option g_long_options[] = {
53061da546Spatrick     {"debug", no_argument, &g_debug, 1},
54061da546Spatrick     {"verbose", no_argument, &g_verbose, 1},
55061da546Spatrick     {"log-file", required_argument, nullptr, 'l'},
56061da546Spatrick     {"log-channels", required_argument, nullptr, 'c'},
57061da546Spatrick     {"listen", required_argument, nullptr, 'L'},
58061da546Spatrick     {"port-offset", required_argument, nullptr, 'p'},
59061da546Spatrick     {"gdbserver-port", required_argument, nullptr, 'P'},
60061da546Spatrick     {"min-gdbserver-port", required_argument, nullptr, 'm'},
61061da546Spatrick     {"max-gdbserver-port", required_argument, nullptr, 'M'},
62061da546Spatrick     {"socket-file", required_argument, nullptr, 'f'},
63061da546Spatrick     {"server", no_argument, &g_server, 1},
64061da546Spatrick     {nullptr, 0, nullptr, 0}};
65061da546Spatrick 
66061da546Spatrick #if defined(__APPLE__)
67061da546Spatrick #define LOW_PORT (IPPORT_RESERVED)
68061da546Spatrick #define HIGH_PORT (IPPORT_HIFIRSTAUTO)
69061da546Spatrick #else
70061da546Spatrick #define LOW_PORT (1024u)
71061da546Spatrick #define HIGH_PORT (49151u)
72061da546Spatrick #endif
73061da546Spatrick 
74061da546Spatrick #if !defined(_WIN32)
75061da546Spatrick // Watch for signals
signal_handler(int signo)76061da546Spatrick static void signal_handler(int signo) {
77061da546Spatrick   switch (signo) {
78061da546Spatrick   case SIGHUP:
79061da546Spatrick     // Use SIGINT first, if that does not work, use SIGHUP as a last resort.
80061da546Spatrick     // And we should not call exit() here because it results in the global
81*f6aab3d8Srobert     // destructors to be invoked and wreaking havoc on the threads still
82*f6aab3d8Srobert     // running.
83*f6aab3d8Srobert     llvm::errs() << "SIGHUP received, exiting lldb-server...\n";
84061da546Spatrick     abort();
85061da546Spatrick     break;
86061da546Spatrick   }
87061da546Spatrick }
88061da546Spatrick #endif
89061da546Spatrick 
display_usage(const char * progname,const char * subcommand)90061da546Spatrick static void display_usage(const char *progname, const char *subcommand) {
91061da546Spatrick   fprintf(stderr, "Usage:\n  %s %s [--log-file log-file-name] [--log-channels "
92061da546Spatrick                   "log-channel-list] [--port-file port-file-path] --server "
93061da546Spatrick                   "--listen port\n",
94061da546Spatrick           progname, subcommand);
95061da546Spatrick   exit(0);
96061da546Spatrick }
97061da546Spatrick 
save_socket_id_to_file(const std::string & socket_id,const FileSpec & file_spec)98061da546Spatrick static Status save_socket_id_to_file(const std::string &socket_id,
99061da546Spatrick                                      const FileSpec &file_spec) {
100dda28197Spatrick   FileSpec temp_file_spec(file_spec.GetDirectory().GetStringRef());
101061da546Spatrick   Status error(llvm::sys::fs::create_directory(temp_file_spec.GetPath()));
102061da546Spatrick   if (error.Fail())
103061da546Spatrick     return Status("Failed to create directory %s: %s",
104*f6aab3d8Srobert                   temp_file_spec.GetPath().c_str(), error.AsCString());
105061da546Spatrick 
106061da546Spatrick   llvm::SmallString<64> temp_file_path;
107061da546Spatrick   temp_file_spec.AppendPathComponent("port-file.%%%%%%");
108be691f3bSpatrick   temp_file_path = temp_file_spec.GetPath();
109061da546Spatrick 
110061da546Spatrick   Status status;
111061da546Spatrick   if (auto Err =
112061da546Spatrick           handleErrors(llvm::writeFileAtomically(
113be691f3bSpatrick                            temp_file_path, file_spec.GetPath(), socket_id),
114061da546Spatrick                        [&status, &file_spec](const AtomicFileWriteError &E) {
115061da546Spatrick                          std::string ErrorMsgBuffer;
116061da546Spatrick                          llvm::raw_string_ostream S(ErrorMsgBuffer);
117061da546Spatrick                          E.log(S);
118061da546Spatrick 
119061da546Spatrick                          switch (E.Error) {
120061da546Spatrick                          case atomic_write_error::failed_to_create_uniq_file:
121061da546Spatrick                            status = Status("Failed to create temp file: %s",
122061da546Spatrick                                            ErrorMsgBuffer.c_str());
123061da546Spatrick                            break;
124061da546Spatrick                          case atomic_write_error::output_stream_error:
125061da546Spatrick                            status = Status("Failed to write to port file.");
126061da546Spatrick                            break;
127061da546Spatrick                          case atomic_write_error::failed_to_rename_temp_file:
128061da546Spatrick                            status = Status("Failed to rename file %s to %s: %s",
129061da546Spatrick                                            ErrorMsgBuffer.c_str(),
130061da546Spatrick                                            file_spec.GetPath().c_str(),
131061da546Spatrick                                            ErrorMsgBuffer.c_str());
132061da546Spatrick                            break;
133061da546Spatrick                          }
134061da546Spatrick                        })) {
135061da546Spatrick     return Status("Failed to atomically write file %s",
136061da546Spatrick                   file_spec.GetPath().c_str());
137061da546Spatrick   }
138061da546Spatrick   return status;
139061da546Spatrick }
140061da546Spatrick 
141061da546Spatrick // main
main_platform(int argc,char * argv[])142061da546Spatrick int main_platform(int argc, char *argv[]) {
143061da546Spatrick   const char *progname = argv[0];
144061da546Spatrick   const char *subcommand = argv[1];
145061da546Spatrick   argc--;
146061da546Spatrick   argv++;
147061da546Spatrick #if !defined(_WIN32)
148061da546Spatrick   signal(SIGPIPE, SIG_IGN);
149061da546Spatrick   signal(SIGHUP, signal_handler);
150061da546Spatrick #endif
151061da546Spatrick   int long_option_index = 0;
152061da546Spatrick   Status error;
153061da546Spatrick   std::string listen_host_port;
154061da546Spatrick   int ch;
155061da546Spatrick 
156061da546Spatrick   std::string log_file;
157061da546Spatrick   StringRef
158061da546Spatrick       log_channels; // e.g. "lldb process threads:gdb-remote default:linux all"
159061da546Spatrick 
160061da546Spatrick   GDBRemoteCommunicationServerPlatform::PortMap gdbserver_portmap;
161061da546Spatrick   int min_gdbserver_port = 0;
162061da546Spatrick   int max_gdbserver_port = 0;
163061da546Spatrick   uint16_t port_offset = 0;
164061da546Spatrick 
165061da546Spatrick   FileSpec socket_file;
166061da546Spatrick   bool show_usage = false;
167061da546Spatrick   int option_error = 0;
168061da546Spatrick   int socket_error = -1;
169061da546Spatrick 
170061da546Spatrick   std::string short_options(OptionParser::GetShortOptionString(g_long_options));
171061da546Spatrick 
172061da546Spatrick #if __GLIBC__
173061da546Spatrick   optind = 0;
174061da546Spatrick #else
175061da546Spatrick   optreset = 1;
176061da546Spatrick   optind = 1;
177061da546Spatrick #endif
178061da546Spatrick 
179061da546Spatrick   while ((ch = getopt_long_only(argc, argv, short_options.c_str(),
180061da546Spatrick                                 g_long_options, &long_option_index)) != -1) {
181061da546Spatrick     switch (ch) {
182061da546Spatrick     case 0: // Any optional that auto set themselves will return 0
183061da546Spatrick       break;
184061da546Spatrick 
185061da546Spatrick     case 'L':
186061da546Spatrick       listen_host_port.append(optarg);
187061da546Spatrick       break;
188061da546Spatrick 
189061da546Spatrick     case 'l': // Set Log File
190061da546Spatrick       if (optarg && optarg[0])
191061da546Spatrick         log_file.assign(optarg);
192061da546Spatrick       break;
193061da546Spatrick 
194061da546Spatrick     case 'c': // Log Channels
195061da546Spatrick       if (optarg && optarg[0])
196061da546Spatrick         log_channels = StringRef(optarg);
197061da546Spatrick       break;
198061da546Spatrick 
199061da546Spatrick     case 'f': // Socket file
200061da546Spatrick       if (optarg && optarg[0])
201061da546Spatrick         socket_file.SetFile(optarg, FileSpec::Style::native);
202061da546Spatrick       break;
203061da546Spatrick 
204061da546Spatrick     case 'p': {
205061da546Spatrick       if (!llvm::to_integer(optarg, port_offset)) {
206be691f3bSpatrick         WithColor::error() << "invalid port offset string " << optarg << "\n";
207061da546Spatrick         option_error = 4;
208061da546Spatrick         break;
209061da546Spatrick       }
210061da546Spatrick       if (port_offset < LOW_PORT || port_offset > HIGH_PORT) {
211be691f3bSpatrick         WithColor::error() << llvm::formatv(
212be691f3bSpatrick             "port offset {0} is not in the "
213061da546Spatrick             "valid user port range of {1} - {2}\n",
214061da546Spatrick             port_offset, LOW_PORT, HIGH_PORT);
215061da546Spatrick         option_error = 5;
216061da546Spatrick       }
217061da546Spatrick     } break;
218061da546Spatrick 
219061da546Spatrick     case 'P':
220061da546Spatrick     case 'm':
221061da546Spatrick     case 'M': {
222061da546Spatrick       uint16_t portnum;
223061da546Spatrick       if (!llvm::to_integer(optarg, portnum)) {
224be691f3bSpatrick         WithColor::error() << "invalid port number string " << optarg << "\n";
225061da546Spatrick         option_error = 2;
226061da546Spatrick         break;
227061da546Spatrick       }
228061da546Spatrick       if (portnum < LOW_PORT || portnum > HIGH_PORT) {
229be691f3bSpatrick         WithColor::error() << llvm::formatv(
230be691f3bSpatrick             "port number {0} is not in the "
231061da546Spatrick             "valid user port range of {1} - {2}\n",
232061da546Spatrick             portnum, LOW_PORT, HIGH_PORT);
233061da546Spatrick         option_error = 1;
234061da546Spatrick         break;
235061da546Spatrick       }
236061da546Spatrick       if (ch == 'P')
237be691f3bSpatrick         gdbserver_portmap.AllowPort(portnum);
238061da546Spatrick       else if (ch == 'm')
239061da546Spatrick         min_gdbserver_port = portnum;
240061da546Spatrick       else
241061da546Spatrick         max_gdbserver_port = portnum;
242061da546Spatrick     } break;
243061da546Spatrick 
244061da546Spatrick     case 'h': /* fall-through is intentional */
245061da546Spatrick     case '?':
246061da546Spatrick       show_usage = true;
247061da546Spatrick       break;
248061da546Spatrick     }
249061da546Spatrick   }
250061da546Spatrick 
251061da546Spatrick   if (!LLDBServerUtilities::SetupLogging(log_file, log_channels, 0))
252061da546Spatrick     return -1;
253061da546Spatrick 
254061da546Spatrick   // Make a port map for a port range that was specified.
255061da546Spatrick   if (min_gdbserver_port && min_gdbserver_port < max_gdbserver_port) {
256be691f3bSpatrick     gdbserver_portmap = GDBRemoteCommunicationServerPlatform::PortMap(
257be691f3bSpatrick         min_gdbserver_port, max_gdbserver_port);
258061da546Spatrick   } else if (min_gdbserver_port || max_gdbserver_port) {
259be691f3bSpatrick     WithColor::error() << llvm::formatv(
260be691f3bSpatrick         "--min-gdbserver-port ({0}) is not lower than "
261be691f3bSpatrick         "--max-gdbserver-port ({1})\n",
262061da546Spatrick         min_gdbserver_port, max_gdbserver_port);
263061da546Spatrick     option_error = 3;
264061da546Spatrick   }
265061da546Spatrick 
266061da546Spatrick   // Print usage and exit if no listening port is specified.
267061da546Spatrick   if (listen_host_port.empty())
268061da546Spatrick     show_usage = true;
269061da546Spatrick 
270061da546Spatrick   if (show_usage || option_error) {
271061da546Spatrick     display_usage(progname, subcommand);
272061da546Spatrick     exit(option_error);
273061da546Spatrick   }
274061da546Spatrick 
275061da546Spatrick   // Skip any options we consumed with getopt_long_only.
276061da546Spatrick   argc -= optind;
277061da546Spatrick   argv += optind;
278061da546Spatrick   lldb_private::Args inferior_arguments;
279061da546Spatrick   inferior_arguments.SetArguments(argc, const_cast<const char **>(argv));
280061da546Spatrick 
281061da546Spatrick   const bool children_inherit_listen_socket = false;
282061da546Spatrick   // the test suite makes many connections in parallel, let's not miss any.
283061da546Spatrick   // The highest this should get reasonably is a function of the number
284061da546Spatrick   // of target CPUs. For now, let's just use 100.
285061da546Spatrick   const int backlog = 100;
286061da546Spatrick 
287061da546Spatrick   std::unique_ptr<Acceptor> acceptor_up(Acceptor::Create(
288061da546Spatrick       listen_host_port, children_inherit_listen_socket, error));
289061da546Spatrick   if (error.Fail()) {
290061da546Spatrick     fprintf(stderr, "failed to create acceptor: %s", error.AsCString());
291061da546Spatrick     exit(socket_error);
292061da546Spatrick   }
293061da546Spatrick 
294061da546Spatrick   error = acceptor_up->Listen(backlog);
295061da546Spatrick   if (error.Fail()) {
296061da546Spatrick     printf("failed to listen: %s\n", error.AsCString());
297061da546Spatrick     exit(socket_error);
298061da546Spatrick   }
299061da546Spatrick   if (socket_file) {
300061da546Spatrick     error =
301061da546Spatrick         save_socket_id_to_file(acceptor_up->GetLocalSocketId(), socket_file);
302061da546Spatrick     if (error.Fail()) {
303061da546Spatrick       fprintf(stderr, "failed to write socket id to %s: %s\n",
304061da546Spatrick               socket_file.GetPath().c_str(), error.AsCString());
305061da546Spatrick       return 1;
306061da546Spatrick     }
307061da546Spatrick   }
308061da546Spatrick 
309061da546Spatrick   do {
310061da546Spatrick     GDBRemoteCommunicationServerPlatform platform(
311061da546Spatrick         acceptor_up->GetSocketProtocol(), acceptor_up->GetSocketScheme());
312061da546Spatrick 
313061da546Spatrick     if (port_offset > 0)
314061da546Spatrick       platform.SetPortOffset(port_offset);
315061da546Spatrick 
316061da546Spatrick     if (!gdbserver_portmap.empty()) {
317061da546Spatrick       platform.SetPortMap(std::move(gdbserver_portmap));
318061da546Spatrick     }
319061da546Spatrick 
320061da546Spatrick     const bool children_inherit_accept_socket = true;
321061da546Spatrick     Connection *conn = nullptr;
322061da546Spatrick     error = acceptor_up->Accept(children_inherit_accept_socket, conn);
323061da546Spatrick     if (error.Fail()) {
324be691f3bSpatrick       WithColor::error() << error.AsCString() << '\n';
325061da546Spatrick       exit(socket_error);
326061da546Spatrick     }
327061da546Spatrick     printf("Connection established.\n");
328061da546Spatrick     if (g_server) {
329061da546Spatrick       // Collect child zombie processes.
330061da546Spatrick #if !defined(_WIN32)
331061da546Spatrick       while (waitpid(-1, nullptr, WNOHANG) > 0)
332061da546Spatrick         ;
333061da546Spatrick #endif
334061da546Spatrick       if (fork()) {
335061da546Spatrick         // Parent doesn't need a connection to the lldb client
336061da546Spatrick         delete conn;
337061da546Spatrick 
338061da546Spatrick         // Parent will continue to listen for new connections.
339061da546Spatrick         continue;
340061da546Spatrick       } else {
341061da546Spatrick         // Child process will handle the connection and exit.
342061da546Spatrick         g_server = 0;
343061da546Spatrick         // Listening socket is owned by parent process.
344061da546Spatrick         acceptor_up.release();
345061da546Spatrick       }
346061da546Spatrick     } else {
347061da546Spatrick       // If not running as a server, this process will not accept
348061da546Spatrick       // connections while a connection is active.
349061da546Spatrick       acceptor_up.reset();
350061da546Spatrick     }
351dda28197Spatrick     platform.SetConnection(std::unique_ptr<Connection>(conn));
352061da546Spatrick 
353061da546Spatrick     if (platform.IsConnected()) {
354061da546Spatrick       if (inferior_arguments.GetArgumentCount() > 0) {
355061da546Spatrick         lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
356*f6aab3d8Srobert         std::optional<uint16_t> port = 0;
357061da546Spatrick         std::string socket_name;
358061da546Spatrick         Status error = platform.LaunchGDBServer(inferior_arguments,
359061da546Spatrick                                                 "", // hostname
360061da546Spatrick                                                 pid, port, socket_name);
361061da546Spatrick         if (error.Success())
362be691f3bSpatrick           platform.SetPendingGdbServer(pid, *port, socket_name);
363061da546Spatrick         else
364061da546Spatrick           fprintf(stderr, "failed to start gdbserver: %s\n", error.AsCString());
365061da546Spatrick       }
366061da546Spatrick 
367061da546Spatrick       bool interrupt = false;
368061da546Spatrick       bool done = false;
369061da546Spatrick       while (!interrupt && !done) {
370*f6aab3d8Srobert         if (platform.GetPacketAndSendResponse(std::nullopt, error, interrupt,
371061da546Spatrick                                               done) !=
372061da546Spatrick             GDBRemoteCommunication::PacketResult::Success)
373061da546Spatrick           break;
374061da546Spatrick       }
375061da546Spatrick 
376*f6aab3d8Srobert       if (error.Fail())
377be691f3bSpatrick         WithColor::error() << error.AsCString() << '\n';
378061da546Spatrick     }
379061da546Spatrick   } while (g_server);
380061da546Spatrick 
381061da546Spatrick   fprintf(stderr, "lldb-server exiting...\n");
382061da546Spatrick 
383061da546Spatrick   return 0;
384061da546Spatrick }
385