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
signal_handler(int signo)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
display_usage(const char * progname,const char * subcommand)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
save_socket_id_to_file(const std::string & socket_id,const FileSpec & file_spec)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
main_platform(int argc,char * argv[])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