1 | //===-- ConnectionFileDescriptorPosix.cpp ---------------------------------===// |
---|---|
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 | #if defined(__APPLE__) |
10 | // Enable this special support for Apple builds where we can have unlimited |
11 | // select bounds. We tried switching to poll() and kqueue and we were panicing |
12 | // the kernel, so we have to stick with select for now. |
13 | #define _DARWIN_UNLIMITED_SELECT |
14 | #endif |
15 | |
16 | #include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" |
17 | #include "lldb/Host/Config.h" |
18 | #include "lldb/Host/FileSystem.h" |
19 | #include "lldb/Host/Socket.h" |
20 | #include "lldb/Host/SocketAddress.h" |
21 | #include "lldb/Utility/LLDBLog.h" |
22 | #include "lldb/Utility/SelectHelper.h" |
23 | #include "lldb/Utility/Timeout.h" |
24 | |
25 | #include <cerrno> |
26 | #include <cstdlib> |
27 | #include <cstring> |
28 | #include <fcntl.h> |
29 | #include <sys/types.h> |
30 | |
31 | #if LLDB_ENABLE_POSIX |
32 | #include <termios.h> |
33 | #include <unistd.h> |
34 | #endif |
35 | |
36 | #include <memory> |
37 | #include <sstream> |
38 | |
39 | #include "llvm/Support/Errno.h" |
40 | #include "llvm/Support/ErrorHandling.h" |
41 | #if defined(__APPLE__) |
42 | #include "llvm/ADT/SmallVector.h" |
43 | #endif |
44 | #include "lldb/Host/Host.h" |
45 | #include "lldb/Host/Socket.h" |
46 | #include "lldb/Host/common/TCPSocket.h" |
47 | #include "lldb/Host/common/UDPSocket.h" |
48 | #include "lldb/Utility/Log.h" |
49 | #include "lldb/Utility/StreamString.h" |
50 | #include "lldb/Utility/Timer.h" |
51 | |
52 | using namespace lldb; |
53 | using namespace lldb_private; |
54 | |
55 | ConnectionFileDescriptor::ConnectionFileDescriptor() |
56 | : Connection(), m_pipe(), m_mutex(), m_shutting_down(false) { |
57 | Log *log(GetLog(mask: LLDBLog::Connection | LLDBLog::Object)); |
58 | LLDB_LOGF(log, "%p ConnectionFileDescriptor::ConnectionFileDescriptor ()", |
59 | static_cast<void *>(this)); |
60 | } |
61 | |
62 | ConnectionFileDescriptor::ConnectionFileDescriptor(int fd, bool owns_fd) |
63 | : Connection(), m_pipe(), m_mutex(), m_shutting_down(false) { |
64 | m_io_sp = |
65 | std::make_shared<NativeFile>(args&: fd, args: File::eOpenOptionReadWrite, args&: owns_fd); |
66 | |
67 | Log *log(GetLog(mask: LLDBLog::Connection | LLDBLog::Object)); |
68 | LLDB_LOGF(log, |
69 | "%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = " |
70 | "%i, owns_fd = %i)", |
71 | static_cast<void *>(this), fd, owns_fd); |
72 | OpenCommandPipe(); |
73 | } |
74 | |
75 | ConnectionFileDescriptor::ConnectionFileDescriptor(Socket *socket) |
76 | : Connection(), m_pipe(), m_mutex(), m_shutting_down(false) { |
77 | InitializeSocket(socket); |
78 | } |
79 | |
80 | ConnectionFileDescriptor::~ConnectionFileDescriptor() { |
81 | Log *log(GetLog(mask: LLDBLog::Connection | LLDBLog::Object)); |
82 | LLDB_LOGF(log, "%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()", |
83 | static_cast<void *>(this)); |
84 | Disconnect(error_ptr: nullptr); |
85 | CloseCommandPipe(); |
86 | } |
87 | |
88 | void ConnectionFileDescriptor::OpenCommandPipe() { |
89 | CloseCommandPipe(); |
90 | |
91 | Log *log = GetLog(mask: LLDBLog::Connection); |
92 | // Make the command file descriptor here: |
93 | Status result = m_pipe.CreateNew(/*child_processes_inherit=*/child_process_inherit: false); |
94 | if (!result.Success()) { |
95 | LLDB_LOGF(log, |
96 | "%p ConnectionFileDescriptor::OpenCommandPipe () - could not " |
97 | "make pipe: %s", |
98 | static_cast<void *>(this), result.AsCString()); |
99 | } else { |
100 | LLDB_LOGF(log, |
101 | "%p ConnectionFileDescriptor::OpenCommandPipe() - success " |
102 | "readfd=%d writefd=%d", |
103 | static_cast<void *>(this), m_pipe.GetReadFileDescriptor(), |
104 | m_pipe.GetWriteFileDescriptor()); |
105 | } |
106 | } |
107 | |
108 | void ConnectionFileDescriptor::CloseCommandPipe() { |
109 | Log *log = GetLog(mask: LLDBLog::Connection); |
110 | LLDB_LOGF(log, "%p ConnectionFileDescriptor::CloseCommandPipe()", |
111 | static_cast<void *>(this)); |
112 | |
113 | m_pipe.Close(); |
114 | } |
115 | |
116 | bool ConnectionFileDescriptor::IsConnected() const { |
117 | return m_io_sp && m_io_sp->IsValid(); |
118 | } |
119 | |
120 | ConnectionStatus ConnectionFileDescriptor::Connect(llvm::StringRef path, |
121 | Status *error_ptr) { |
122 | return Connect(url: path, socket_id_callback: [](llvm::StringRef) {}, error_ptr); |
123 | } |
124 | |
125 | ConnectionStatus |
126 | ConnectionFileDescriptor::Connect(llvm::StringRef path, |
127 | socket_id_callback_type socket_id_callback, |
128 | Status *error_ptr) { |
129 | std::lock_guard<std::recursive_mutex> guard(m_mutex); |
130 | Log *log = GetLog(mask: LLDBLog::Connection); |
131 | LLDB_LOGF(log, "%p ConnectionFileDescriptor::Connect (url = '%s')", |
132 | static_cast<void *>(this), path.str().c_str()); |
133 | |
134 | OpenCommandPipe(); |
135 | |
136 | if (path.empty()) { |
137 | if (error_ptr) |
138 | *error_ptr = Status::FromErrorString(str: "invalid connect arguments"); |
139 | return eConnectionStatusError; |
140 | } |
141 | |
142 | llvm::StringRef scheme; |
143 | std::tie(args&: scheme, args&: path) = path.split(Separator: "://"); |
144 | |
145 | if (!path.empty()) { |
146 | auto method = |
147 | llvm::StringSwitch<ConnectionStatus (ConnectionFileDescriptor::*)( |
148 | llvm::StringRef, socket_id_callback_type, Status *)>(scheme) |
149 | .Case(S: "listen", Value: &ConnectionFileDescriptor::AcceptTCP) |
150 | .Cases(S0: "accept", S1: "unix-accept", |
151 | Value: &ConnectionFileDescriptor::AcceptNamedSocket) |
152 | .Case(S: "unix-abstract-accept", |
153 | Value: &ConnectionFileDescriptor::AcceptAbstractSocket) |
154 | .Cases(S0: "connect", S1: "tcp-connect", |
155 | Value: &ConnectionFileDescriptor::ConnectTCP) |
156 | .Case(S: "udp", Value: &ConnectionFileDescriptor::ConnectUDP) |
157 | .Case(S: "unix-connect", Value: &ConnectionFileDescriptor::ConnectNamedSocket) |
158 | .Case(S: "unix-abstract-connect", |
159 | Value: &ConnectionFileDescriptor::ConnectAbstractSocket) |
160 | #if LLDB_ENABLE_POSIX |
161 | .Case(S: "fd", Value: &ConnectionFileDescriptor::ConnectFD) |
162 | .Case(S: "file", Value: &ConnectionFileDescriptor::ConnectFile) |
163 | .Case(S: "serial", Value: &ConnectionFileDescriptor::ConnectSerialPort) |
164 | #endif |
165 | .Default(Value: nullptr); |
166 | |
167 | if (method) { |
168 | if (error_ptr) |
169 | *error_ptr = Status(); |
170 | return (this->*method)(path, socket_id_callback, error_ptr); |
171 | } |
172 | } |
173 | |
174 | if (error_ptr) |
175 | *error_ptr = Status::FromErrorStringWithFormat( |
176 | format: "unsupported connection URL: '%s'", path.str().c_str()); |
177 | return eConnectionStatusError; |
178 | } |
179 | |
180 | bool ConnectionFileDescriptor::InterruptRead() { |
181 | return !errorToBool(Err: m_pipe.Write(buf: "i", size: 1).takeError()); |
182 | } |
183 | |
184 | ConnectionStatus ConnectionFileDescriptor::Disconnect(Status *error_ptr) { |
185 | Log *log = GetLog(mask: LLDBLog::Connection); |
186 | LLDB_LOGF(log, "%p ConnectionFileDescriptor::Disconnect ()", |
187 | static_cast<void *>(this)); |
188 | |
189 | ConnectionStatus status = eConnectionStatusSuccess; |
190 | |
191 | if (!IsConnected()) { |
192 | LLDB_LOGF( |
193 | log, "%p ConnectionFileDescriptor::Disconnect(): Nothing to disconnect", |
194 | static_cast<void *>(this)); |
195 | return eConnectionStatusSuccess; |
196 | } |
197 | |
198 | // Try to get the ConnectionFileDescriptor's mutex. If we fail, that is |
199 | // quite likely because somebody is doing a blocking read on our file |
200 | // descriptor. If that's the case, then send the "q" char to the command |
201 | // file channel so the read will wake up and the connection will then know to |
202 | // shut down. |
203 | std::unique_lock<std::recursive_mutex> locker(m_mutex, std::defer_lock); |
204 | if (!locker.try_lock()) { |
205 | if (m_pipe.CanWrite()) { |
206 | llvm::Error err = m_pipe.Write(buf: "q", size: 1).takeError(); |
207 | LLDB_LOG(log, |
208 | "{0}: Couldn't get the lock, sent 'q' to {1}, error = '{2}'.", |
209 | this, m_pipe.GetWriteFileDescriptor(), err); |
210 | consumeError(Err: std::move(err)); |
211 | } else if (log) { |
212 | LLDB_LOGF(log, |
213 | "%p ConnectionFileDescriptor::Disconnect(): Couldn't get the " |
214 | "lock, but no command pipe is available.", |
215 | static_cast<void *>(this)); |
216 | } |
217 | locker.lock(); |
218 | } |
219 | |
220 | // Prevents reads and writes during shutdown. |
221 | m_shutting_down = true; |
222 | |
223 | Status error = m_io_sp->Close(); |
224 | if (error.Fail()) |
225 | status = eConnectionStatusError; |
226 | if (error_ptr) |
227 | *error_ptr = std::move(error); |
228 | |
229 | // Close any pipes we were using for async interrupts |
230 | m_pipe.Close(); |
231 | |
232 | m_uri.clear(); |
233 | m_shutting_down = false; |
234 | return status; |
235 | } |
236 | |
237 | size_t ConnectionFileDescriptor::Read(void *dst, size_t dst_len, |
238 | const Timeout<std::micro> &timeout, |
239 | ConnectionStatus &status, |
240 | Status *error_ptr) { |
241 | Log *log = GetLog(mask: LLDBLog::Connection); |
242 | |
243 | std::unique_lock<std::recursive_mutex> locker(m_mutex, std::defer_lock); |
244 | if (!locker.try_lock()) { |
245 | LLDB_LOGF(log, |
246 | "%p ConnectionFileDescriptor::Read () failed to get the " |
247 | "connection lock.", |
248 | static_cast<void *>(this)); |
249 | if (error_ptr) |
250 | *error_ptr = Status::FromErrorString( |
251 | str: "failed to get the connection lock for read."); |
252 | |
253 | status = eConnectionStatusTimedOut; |
254 | return 0; |
255 | } |
256 | |
257 | if (m_shutting_down) { |
258 | if (error_ptr) |
259 | *error_ptr = Status::FromErrorString(str: "shutting down"); |
260 | status = eConnectionStatusError; |
261 | return 0; |
262 | } |
263 | |
264 | status = BytesAvailable(timeout, error_ptr); |
265 | if (status != eConnectionStatusSuccess) |
266 | return 0; |
267 | |
268 | Status error; |
269 | size_t bytes_read = dst_len; |
270 | error = m_io_sp->Read(buf: dst, num_bytes&: bytes_read); |
271 | |
272 | if (log) { |
273 | LLDB_LOGF(log, |
274 | "%p ConnectionFileDescriptor::Read() fd = %"PRIu64 |
275 | ", dst = %p, dst_len = %"PRIu64 ") => %"PRIu64 ", error = %s", |
276 | static_cast<void *>(this), |
277 | static_cast<uint64_t>(m_io_sp->GetWaitableHandle()), |
278 | static_cast<void *>(dst), static_cast<uint64_t>(dst_len), |
279 | static_cast<uint64_t>(bytes_read), error.AsCString()); |
280 | } |
281 | |
282 | if (bytes_read == 0) { |
283 | error.Clear(); // End-of-file. Do not automatically close; pass along for |
284 | // the end-of-file handlers. |
285 | status = eConnectionStatusEndOfFile; |
286 | } |
287 | |
288 | if (error_ptr) |
289 | *error_ptr = error.Clone(); |
290 | |
291 | if (error.Fail()) { |
292 | uint32_t error_value = error.GetError(); |
293 | switch (error_value) { |
294 | case EAGAIN: // The file was marked for non-blocking I/O, and no data were |
295 | // ready to be read. |
296 | if (m_io_sp->GetFdType() == IOObject::eFDTypeSocket) |
297 | status = eConnectionStatusTimedOut; |
298 | else |
299 | status = eConnectionStatusSuccess; |
300 | return 0; |
301 | |
302 | case EFAULT: // Buf points outside the allocated address space. |
303 | case EINTR: // A read from a slow device was interrupted before any data |
304 | // arrived by the delivery of a signal. |
305 | case EINVAL: // The pointer associated with fildes was negative. |
306 | case EIO: // An I/O error occurred while reading from the file system. |
307 | // The process group is orphaned. |
308 | // The file is a regular file, nbyte is greater than 0, the |
309 | // starting position is before the end-of-file, and the |
310 | // starting position is greater than or equal to the offset |
311 | // maximum established for the open file descriptor |
312 | // associated with fildes. |
313 | case EISDIR: // An attempt is made to read a directory. |
314 | case ENOBUFS: // An attempt to allocate a memory buffer fails. |
315 | case ENOMEM: // Insufficient memory is available. |
316 | status = eConnectionStatusError; |
317 | break; // Break to close.... |
318 | |
319 | case ENOENT: // no such file or directory |
320 | case EBADF: // fildes is not a valid file or socket descriptor open for |
321 | // reading. |
322 | case ENXIO: // An action is requested of a device that does not exist.. |
323 | // A requested action cannot be performed by the device. |
324 | case ECONNRESET: // The connection is closed by the peer during a read |
325 | // attempt on a socket. |
326 | case ENOTCONN: // A read is attempted on an unconnected socket. |
327 | status = eConnectionStatusLostConnection; |
328 | break; // Break to close.... |
329 | |
330 | case ETIMEDOUT: // A transmission timeout occurs during a read attempt on a |
331 | // socket. |
332 | status = eConnectionStatusTimedOut; |
333 | return 0; |
334 | |
335 | default: |
336 | LLDB_LOG(log, "this = {0}, unexpected error: {1}", this, |
337 | llvm::sys::StrError(error_value)); |
338 | status = eConnectionStatusError; |
339 | break; // Break to close.... |
340 | } |
341 | |
342 | return 0; |
343 | } |
344 | return bytes_read; |
345 | } |
346 | |
347 | size_t ConnectionFileDescriptor::Write(const void *src, size_t src_len, |
348 | ConnectionStatus &status, |
349 | Status *error_ptr) { |
350 | Log *log = GetLog(mask: LLDBLog::Connection); |
351 | LLDB_LOGF(log, |
352 | "%p ConnectionFileDescriptor::Write (src = %p, src_len = %"PRIu64 |
353 | ")", |
354 | static_cast<void *>(this), static_cast<const void *>(src), |
355 | static_cast<uint64_t>(src_len)); |
356 | |
357 | if (!IsConnected()) { |
358 | if (error_ptr) |
359 | *error_ptr = Status::FromErrorString(str: "not connected"); |
360 | status = eConnectionStatusNoConnection; |
361 | return 0; |
362 | } |
363 | |
364 | if (m_shutting_down) { |
365 | if (error_ptr) |
366 | *error_ptr = Status::FromErrorString(str: "shutting down"); |
367 | status = eConnectionStatusError; |
368 | return 0; |
369 | } |
370 | |
371 | Status error; |
372 | |
373 | size_t bytes_sent = src_len; |
374 | error = m_io_sp->Write(buf: src, num_bytes&: bytes_sent); |
375 | |
376 | if (log) { |
377 | LLDB_LOGF(log, |
378 | "%p ConnectionFileDescriptor::Write(fd = %"PRIu64 |
379 | ", src = %p, src_len = %"PRIu64 ") => %"PRIu64 " (error = %s)", |
380 | static_cast<void *>(this), |
381 | static_cast<uint64_t>(m_io_sp->GetWaitableHandle()), |
382 | static_cast<const void *>(src), static_cast<uint64_t>(src_len), |
383 | static_cast<uint64_t>(bytes_sent), error.AsCString()); |
384 | } |
385 | |
386 | if (error_ptr) |
387 | *error_ptr = error.Clone(); |
388 | |
389 | if (error.Fail()) { |
390 | switch (error.GetError()) { |
391 | case EAGAIN: |
392 | case EINTR: |
393 | status = eConnectionStatusSuccess; |
394 | return 0; |
395 | |
396 | case ECONNRESET: // The connection is closed by the peer during a read |
397 | // attempt on a socket. |
398 | case ENOTCONN: // A read is attempted on an unconnected socket. |
399 | status = eConnectionStatusLostConnection; |
400 | break; // Break to close.... |
401 | |
402 | default: |
403 | status = eConnectionStatusError; |
404 | break; // Break to close.... |
405 | } |
406 | |
407 | return 0; |
408 | } |
409 | |
410 | status = eConnectionStatusSuccess; |
411 | return bytes_sent; |
412 | } |
413 | |
414 | std::string ConnectionFileDescriptor::GetURI() { return m_uri; } |
415 | |
416 | // This ConnectionFileDescriptor::BytesAvailable() uses select() via |
417 | // SelectHelper |
418 | // |
419 | // PROS: |
420 | // - select is consistent across most unix platforms |
421 | // - The Apple specific version allows for unlimited fds in the fd_sets by |
422 | // setting the _DARWIN_UNLIMITED_SELECT define prior to including the |
423 | // required header files. |
424 | // CONS: |
425 | // - on non-Apple platforms, only supports file descriptors up to FD_SETSIZE. |
426 | // This implementation will assert if it runs into that hard limit to let |
427 | // users know that another ConnectionFileDescriptor::BytesAvailable() should |
428 | // be used or a new version of ConnectionFileDescriptor::BytesAvailable() |
429 | // should be written for the system that is running into the limitations. |
430 | |
431 | ConnectionStatus |
432 | ConnectionFileDescriptor::BytesAvailable(const Timeout<std::micro> &timeout, |
433 | Status *error_ptr) { |
434 | // Don't need to take the mutex here separately since we are only called from |
435 | // Read. If we ever get used more generally we will need to lock here as |
436 | // well. |
437 | |
438 | Log *log = GetLog(mask: LLDBLog::Connection); |
439 | LLDB_LOG(log, "this = {0}, timeout = {1}", this, timeout); |
440 | |
441 | // Make a copy of the file descriptors to make sure we don't have another |
442 | // thread change these values out from under us and cause problems in the |
443 | // loop below where like in FS_SET() |
444 | const IOObject::WaitableHandle handle = m_io_sp->GetWaitableHandle(); |
445 | const int pipe_fd = m_pipe.GetReadFileDescriptor(); |
446 | |
447 | if (handle != IOObject::kInvalidHandleValue) { |
448 | SelectHelper select_helper; |
449 | if (timeout) |
450 | select_helper.SetTimeout(*timeout); |
451 | |
452 | select_helper.FDSetRead(fd: handle); |
453 | #if defined(_WIN32) |
454 | // select() won't accept pipes on Windows. The entire Windows codepath |
455 | // needs to be converted over to using WaitForMultipleObjects and event |
456 | // HANDLEs, but for now at least this will allow ::select() to not return |
457 | // an error. |
458 | const bool have_pipe_fd = false; |
459 | #else |
460 | const bool have_pipe_fd = pipe_fd >= 0; |
461 | #endif |
462 | if (have_pipe_fd) |
463 | select_helper.FDSetRead(fd: pipe_fd); |
464 | |
465 | while (handle == m_io_sp->GetWaitableHandle()) { |
466 | |
467 | Status error = select_helper.Select(); |
468 | |
469 | if (error_ptr) |
470 | *error_ptr = error.Clone(); |
471 | |
472 | if (error.Fail()) { |
473 | switch (error.GetError()) { |
474 | case EBADF: // One of the descriptor sets specified an invalid |
475 | // descriptor. |
476 | return eConnectionStatusLostConnection; |
477 | |
478 | case EINVAL: // The specified time limit is invalid. One of its |
479 | // components is negative or too large. |
480 | default: // Other unknown error |
481 | return eConnectionStatusError; |
482 | |
483 | case ETIMEDOUT: |
484 | return eConnectionStatusTimedOut; |
485 | |
486 | case EAGAIN: // The kernel was (perhaps temporarily) unable to |
487 | // allocate the requested number of file descriptors, or |
488 | // we have non-blocking IO |
489 | case EINTR: // A signal was delivered before the time limit |
490 | // expired and before any of the selected events occurred. |
491 | break; // Lets keep reading to until we timeout |
492 | } |
493 | } else { |
494 | if (select_helper.FDIsSetRead(fd: handle)) |
495 | return eConnectionStatusSuccess; |
496 | |
497 | if (select_helper.FDIsSetRead(fd: pipe_fd)) { |
498 | // There is an interrupt or exit command in the command pipe Read the |
499 | // data from that pipe: |
500 | char c; |
501 | |
502 | ssize_t bytes_read = |
503 | llvm::sys::RetryAfterSignal(Fail: -1, F&: ::read, As: pipe_fd, As: &c, As: 1); |
504 | assert(bytes_read == 1); |
505 | UNUSED_IF_ASSERT_DISABLED(bytes_read); |
506 | switch (c) { |
507 | case 'q': |
508 | LLDB_LOGF(log, |
509 | "%p ConnectionFileDescriptor::BytesAvailable() " |
510 | "got data: %c from the command channel.", |
511 | static_cast<void *>(this), c); |
512 | return eConnectionStatusEndOfFile; |
513 | case 'i': |
514 | // Interrupt the current read |
515 | return eConnectionStatusInterrupted; |
516 | } |
517 | } |
518 | } |
519 | } |
520 | } |
521 | |
522 | if (error_ptr) |
523 | *error_ptr = Status::FromErrorString(str: "not connected"); |
524 | return eConnectionStatusLostConnection; |
525 | } |
526 | |
527 | lldb::ConnectionStatus ConnectionFileDescriptor::AcceptSocket( |
528 | Socket::SocketProtocol socket_protocol, llvm::StringRef socket_name, |
529 | llvm::function_ref<void(Socket &)> post_listen_callback, |
530 | Status *error_ptr) { |
531 | Status error; |
532 | std::unique_ptr<Socket> listening_socket = |
533 | Socket::Create(protocol: socket_protocol, error); |
534 | Socket *accepted_socket; |
535 | |
536 | if (!error.Fail()) |
537 | error = listening_socket->Listen(name: socket_name, backlog: 5); |
538 | |
539 | if (!error.Fail()) { |
540 | post_listen_callback(*listening_socket); |
541 | error = listening_socket->Accept(/*timeout=*/std::nullopt, socket&: accepted_socket); |
542 | } |
543 | |
544 | if (!error.Fail()) { |
545 | m_io_sp.reset(p: accepted_socket); |
546 | m_uri.assign(str: socket_name.str()); |
547 | return eConnectionStatusSuccess; |
548 | } |
549 | |
550 | if (error_ptr) |
551 | *error_ptr = error.Clone(); |
552 | return eConnectionStatusError; |
553 | } |
554 | |
555 | lldb::ConnectionStatus |
556 | ConnectionFileDescriptor::ConnectSocket(Socket::SocketProtocol socket_protocol, |
557 | llvm::StringRef socket_name, |
558 | Status *error_ptr) { |
559 | Status error; |
560 | std::unique_ptr<Socket> socket = Socket::Create(protocol: socket_protocol, error); |
561 | |
562 | if (!error.Fail()) |
563 | error = socket->Connect(name: socket_name); |
564 | |
565 | if (!error.Fail()) { |
566 | m_io_sp = std::move(socket); |
567 | m_uri.assign(str: socket_name.str()); |
568 | return eConnectionStatusSuccess; |
569 | } |
570 | |
571 | if (error_ptr) |
572 | *error_ptr = error.Clone(); |
573 | return eConnectionStatusError; |
574 | } |
575 | |
576 | ConnectionStatus ConnectionFileDescriptor::AcceptNamedSocket( |
577 | llvm::StringRef socket_name, socket_id_callback_type socket_id_callback, |
578 | Status *error_ptr) { |
579 | return AcceptSocket( |
580 | socket_protocol: Socket::ProtocolUnixDomain, socket_name, |
581 | post_listen_callback: [socket_id_callback, socket_name](Socket &listening_socket) { |
582 | socket_id_callback(socket_name); |
583 | }, |
584 | error_ptr); |
585 | } |
586 | |
587 | ConnectionStatus ConnectionFileDescriptor::ConnectNamedSocket( |
588 | llvm::StringRef socket_name, socket_id_callback_type socket_id_callback, |
589 | Status *error_ptr) { |
590 | return ConnectSocket(socket_protocol: Socket::ProtocolUnixDomain, socket_name, error_ptr); |
591 | } |
592 | |
593 | ConnectionStatus ConnectionFileDescriptor::AcceptAbstractSocket( |
594 | llvm::StringRef socket_name, socket_id_callback_type socket_id_callback, |
595 | Status *error_ptr) { |
596 | return AcceptSocket( |
597 | socket_protocol: Socket::ProtocolUnixAbstract, socket_name, |
598 | post_listen_callback: [socket_id_callback, socket_name](Socket &listening_socket) { |
599 | socket_id_callback(socket_name); |
600 | }, |
601 | error_ptr); |
602 | } |
603 | |
604 | lldb::ConnectionStatus ConnectionFileDescriptor::ConnectAbstractSocket( |
605 | llvm::StringRef socket_name, socket_id_callback_type socket_id_callback, |
606 | Status *error_ptr) { |
607 | return ConnectSocket(socket_protocol: Socket::ProtocolUnixAbstract, socket_name, error_ptr); |
608 | } |
609 | |
610 | ConnectionStatus |
611 | ConnectionFileDescriptor::AcceptTCP(llvm::StringRef socket_name, |
612 | socket_id_callback_type socket_id_callback, |
613 | Status *error_ptr) { |
614 | ConnectionStatus ret = AcceptSocket( |
615 | socket_protocol: Socket::ProtocolTcp, socket_name, |
616 | post_listen_callback: [socket_id_callback](Socket &listening_socket) { |
617 | uint16_t port = |
618 | static_cast<TCPSocket &>(listening_socket).GetLocalPortNumber(); |
619 | socket_id_callback(std::to_string(val: port)); |
620 | }, |
621 | error_ptr); |
622 | if (ret == eConnectionStatusSuccess) |
623 | m_uri.assign( |
624 | str: static_cast<TCPSocket *>(m_io_sp.get())->GetRemoteConnectionURI()); |
625 | return ret; |
626 | } |
627 | |
628 | ConnectionStatus |
629 | ConnectionFileDescriptor::ConnectTCP(llvm::StringRef socket_name, |
630 | socket_id_callback_type socket_id_callback, |
631 | Status *error_ptr) { |
632 | return ConnectSocket(socket_protocol: Socket::ProtocolTcp, socket_name, error_ptr); |
633 | } |
634 | |
635 | ConnectionStatus |
636 | ConnectionFileDescriptor::ConnectUDP(llvm::StringRef s, |
637 | socket_id_callback_type socket_id_callback, |
638 | Status *error_ptr) { |
639 | if (error_ptr) |
640 | *error_ptr = Status(); |
641 | llvm::Expected<std::unique_ptr<UDPSocket>> socket = Socket::UdpConnect(host_and_port: s); |
642 | if (!socket) { |
643 | if (error_ptr) |
644 | *error_ptr = Status::FromError(error: socket.takeError()); |
645 | else |
646 | LLDB_LOG_ERROR(GetLog(LLDBLog::Connection), socket.takeError(), |
647 | "tcp connect failed: {0}"); |
648 | return eConnectionStatusError; |
649 | } |
650 | m_io_sp = std::move(*socket); |
651 | m_uri.assign(str: std::string(s)); |
652 | return eConnectionStatusSuccess; |
653 | } |
654 | |
655 | ConnectionStatus |
656 | ConnectionFileDescriptor::ConnectFD(llvm::StringRef s, |
657 | socket_id_callback_type socket_id_callback, |
658 | Status *error_ptr) { |
659 | #if LLDB_ENABLE_POSIX |
660 | // Just passing a native file descriptor within this current process that |
661 | // is already opened (possibly from a service or other source). |
662 | int fd = -1; |
663 | |
664 | if (!s.getAsInteger(Radix: 0, Result&: fd)) { |
665 | // We have what looks to be a valid file descriptor, but we should make |
666 | // sure it is. We currently are doing this by trying to get the flags |
667 | // from the file descriptor and making sure it isn't a bad fd. |
668 | errno = 0; |
669 | int flags = ::fcntl(fd: fd, F_GETFL, 0); |
670 | if (flags == -1 || errno == EBADF) { |
671 | if (error_ptr) |
672 | *error_ptr = Status::FromErrorStringWithFormat( |
673 | format: "stale file descriptor: %s", s.str().c_str()); |
674 | m_io_sp.reset(); |
675 | return eConnectionStatusError; |
676 | } else { |
677 | // Don't take ownership of a file descriptor that gets passed to us |
678 | // since someone else opened the file descriptor and handed it to us. |
679 | // TODO: Since are using a URL to open connection we should |
680 | // eventually parse options using the web standard where we have |
681 | // "fd://123?opt1=value;opt2=value" and we can have an option be |
682 | // "owns=1" or "owns=0" or something like this to allow us to specify |
683 | // this. For now, we assume we must assume we don't own it. |
684 | |
685 | std::unique_ptr<TCPSocket> tcp_socket; |
686 | tcp_socket = std::make_unique<TCPSocket>(args&: fd, /*should_close=*/args: false); |
687 | // Try and get a socket option from this file descriptor to see if |
688 | // this is a socket and set m_is_socket accordingly. |
689 | int resuse; |
690 | bool is_socket = |
691 | !!tcp_socket->GetOption(SOL_SOCKET, SO_REUSEADDR, option_value&: resuse); |
692 | if (is_socket) |
693 | m_io_sp = std::move(tcp_socket); |
694 | else |
695 | m_io_sp = |
696 | std::make_shared<NativeFile>(args&: fd, args: File::eOpenOptionReadWrite, args: false); |
697 | m_uri = s.str(); |
698 | return eConnectionStatusSuccess; |
699 | } |
700 | } |
701 | |
702 | if (error_ptr) |
703 | *error_ptr = Status::FromErrorStringWithFormat( |
704 | format: "invalid file descriptor: \"%s\"", s.str().c_str()); |
705 | m_io_sp.reset(); |
706 | return eConnectionStatusError; |
707 | #endif // LLDB_ENABLE_POSIX |
708 | llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX"); |
709 | } |
710 | |
711 | ConnectionStatus ConnectionFileDescriptor::ConnectFile( |
712 | llvm::StringRef s, socket_id_callback_type socket_id_callback, |
713 | Status *error_ptr) { |
714 | #if LLDB_ENABLE_POSIX |
715 | std::string addr_str = s.str(); |
716 | // file:///PATH |
717 | int fd = FileSystem::Instance().Open(path: addr_str.c_str(), O_RDWR); |
718 | if (fd == -1) { |
719 | if (error_ptr) |
720 | *error_ptr = Status::FromErrno(); |
721 | return eConnectionStatusError; |
722 | } |
723 | |
724 | if (::isatty(fd: fd)) { |
725 | // Set up serial terminal emulation |
726 | struct termios options; |
727 | ::tcgetattr(fd: fd, termios_p: &options); |
728 | |
729 | // Set port speed to the available maximum |
730 | #ifdef B115200 |
731 | ::cfsetospeed(termios_p: &options, B115200); |
732 | ::cfsetispeed(termios_p: &options, B115200); |
733 | #elif B57600 |
734 | ::cfsetospeed(&options, B57600); |
735 | ::cfsetispeed(&options, B57600); |
736 | #elif B38400 |
737 | ::cfsetospeed(&options, B38400); |
738 | ::cfsetispeed(&options, B38400); |
739 | #else |
740 | #error "Maximum Baud rate is Unknown" |
741 | #endif |
742 | |
743 | // Raw input, disable echo and signals |
744 | options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); |
745 | |
746 | // Make sure only one character is needed to return from a read |
747 | options.c_cc[VMIN] = 1; |
748 | options.c_cc[VTIME] = 0; |
749 | |
750 | llvm::sys::RetryAfterSignal(Fail: -1, F&: ::tcsetattr, As: fd, TCSANOW, As: &options); |
751 | } |
752 | |
753 | m_io_sp = std::make_shared<NativeFile>(args&: fd, args: File::eOpenOptionReadWrite, args: true); |
754 | return eConnectionStatusSuccess; |
755 | #endif // LLDB_ENABLE_POSIX |
756 | llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX"); |
757 | } |
758 | |
759 | ConnectionStatus ConnectionFileDescriptor::ConnectSerialPort( |
760 | llvm::StringRef s, socket_id_callback_type socket_id_callback, |
761 | Status *error_ptr) { |
762 | #if LLDB_ENABLE_POSIX |
763 | llvm::StringRef path, qs; |
764 | // serial:///PATH?k1=v1&k2=v2... |
765 | std::tie(args&: path, args&: qs) = s.split(Separator: '?'); |
766 | |
767 | llvm::Expected<SerialPort::Options> serial_options = |
768 | SerialPort::OptionsFromURL(urlqs: qs); |
769 | if (!serial_options) { |
770 | if (error_ptr) |
771 | *error_ptr = Status::FromError(error: serial_options.takeError()); |
772 | else |
773 | llvm::consumeError(Err: serial_options.takeError()); |
774 | return eConnectionStatusError; |
775 | } |
776 | |
777 | int fd = FileSystem::Instance().Open(path: path.str().c_str(), O_RDWR); |
778 | if (fd == -1) { |
779 | if (error_ptr) |
780 | *error_ptr = Status::FromErrno(); |
781 | return eConnectionStatusError; |
782 | } |
783 | |
784 | llvm::Expected<std::unique_ptr<SerialPort>> serial_sp = SerialPort::Create( |
785 | fd, options: File::eOpenOptionReadWrite, serial_options: serial_options.get(), transfer_ownership: true); |
786 | if (!serial_sp) { |
787 | if (error_ptr) |
788 | *error_ptr = Status::FromError(error: serial_sp.takeError()); |
789 | else |
790 | llvm::consumeError(Err: serial_sp.takeError()); |
791 | return eConnectionStatusError; |
792 | } |
793 | m_io_sp = std::move(serial_sp.get()); |
794 | |
795 | return eConnectionStatusSuccess; |
796 | #endif // LLDB_ENABLE_POSIX |
797 | llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX"); |
798 | } |
799 | |
800 | void ConnectionFileDescriptor::InitializeSocket(Socket *socket) { |
801 | m_io_sp.reset(p: socket); |
802 | m_uri = socket->GetRemoteConnectionURI(); |
803 | } |
804 |
Definitions
- ConnectionFileDescriptor
- ConnectionFileDescriptor
- ConnectionFileDescriptor
- ~ConnectionFileDescriptor
- OpenCommandPipe
- CloseCommandPipe
- IsConnected
- Connect
- Connect
- InterruptRead
- Disconnect
- Read
- Write
- GetURI
- BytesAvailable
- AcceptSocket
- ConnectSocket
- AcceptNamedSocket
- ConnectNamedSocket
- AcceptAbstractSocket
- ConnectAbstractSocket
- AcceptTCP
- ConnectTCP
- ConnectUDP
- ConnectFD
- ConnectFile
- ConnectSerialPort
Learn to use CMake with our Intro Training
Find out more