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