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