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