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/FileUtilities.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/common/TCPSocket.h"
37 #include "lldb/Utility/FileSpec.h"
38 #include "lldb/Utility/Status.h"
39 
40 using namespace lldb;
41 using namespace lldb_private;
42 using namespace lldb_private::lldb_server;
43 using namespace lldb_private::process_gdb_remote;
44 using namespace llvm;
45 
46 // option descriptors for getopt_long_only()
47 
48 static int g_debug = 0;
49 static int g_verbose = 0;
50 static int g_server = 0;
51 
52 static struct option g_long_options[] = {
53     {"debug", no_argument, &g_debug, 1},
54     {"verbose", no_argument, &g_verbose, 1},
55     {"log-file", required_argument, nullptr, 'l'},
56     {"log-channels", required_argument, nullptr, 'c'},
57     {"listen", required_argument, nullptr, 'L'},
58     {"port-offset", required_argument, nullptr, 'p'},
59     {"gdbserver-port", required_argument, nullptr, 'P'},
60     {"min-gdbserver-port", required_argument, nullptr, 'm'},
61     {"max-gdbserver-port", required_argument, nullptr, 'M'},
62     {"socket-file", required_argument, nullptr, 'f'},
63     {"server", no_argument, &g_server, 1},
64     {nullptr, 0, nullptr, 0}};
65 
66 #if defined(__APPLE__)
67 #define LOW_PORT (IPPORT_RESERVED)
68 #define HIGH_PORT (IPPORT_HIFIRSTAUTO)
69 #else
70 #define LOW_PORT (1024u)
71 #define HIGH_PORT (49151u)
72 #endif
73 
74 #if !defined(_WIN32)
75 // Watch for signals
76 static void signal_handler(int signo) {
77   switch (signo) {
78   case SIGHUP:
79     // Use SIGINT first, if that does not work, use SIGHUP as a last resort.
80     // And we should not call exit() here because it results in the global
81     // destructors to be invoked and wreaking havoc on the threads still
82     // running.
83     llvm::errs() << "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.GetPath().c_str(), 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         std::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(std::nullopt, 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