#include "lldb/Host/PseudoTerminal.h"
#include "lldb/Host/Config.h"
#include "lldb/Host/FileSystem.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Errno.h"
#include <cassert>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <mutex>
#if defined(TIOCSCTTY)
#include <sys/ioctl.h>
#endif
#include "lldb/Host/PosixApi.h"
#if defined(__APPLE__)
#include <Availability.h>
#endif
#if defined(__ANDROID__)
int posix_openpt(int flags);
#endif
using namespace lldb_private;
PseudoTerminal::PseudoTerminal() = default;
PseudoTerminal::~PseudoTerminal() {
ClosePrimaryFileDescriptor();
CloseSecondaryFileDescriptor();
}
void PseudoTerminal::ClosePrimaryFileDescriptor() {
if (m_primary_fd >= 0) {
::close(m_primary_fd);
m_primary_fd = invalid_fd;
}
}
void PseudoTerminal::CloseSecondaryFileDescriptor() {
if (m_secondary_fd >= 0) {
::close(m_secondary_fd);
m_secondary_fd = invalid_fd;
}
}
llvm::Error PseudoTerminal::OpenFirstAvailablePrimary(int oflag) {
#if LLDB_ENABLE_POSIX
m_primary_fd = ::posix_openpt(oflag);
if (m_primary_fd < 0) {
return llvm::errorCodeToError(
std::error_code(errno, std::generic_category()));
}
if (::grantpt(m_primary_fd) < 0) {
std::error_code EC(errno, std::generic_category());
ClosePrimaryFileDescriptor();
return llvm::errorCodeToError(EC);
}
if (::unlockpt(m_primary_fd) < 0) {
std::error_code EC(errno, std::generic_category());
ClosePrimaryFileDescriptor();
return llvm::errorCodeToError(EC);
}
return llvm::Error::success();
#else
return llvm::errorCodeToError(llvm::errc::not_supported);
#endif
}
llvm::Error PseudoTerminal::OpenSecondary(int oflag) {
CloseSecondaryFileDescriptor();
std::string name = GetSecondaryName();
m_secondary_fd = FileSystem::Instance().Open(name.c_str(), oflag);
if (m_secondary_fd >= 0)
return llvm::Error::success();
return llvm::errorCodeToError(
std::error_code(errno, std::generic_category()));
}
#if !HAVE_PTSNAME_R || defined(__APPLE__)
static std::string use_ptsname(int fd) {
static std::mutex mutex;
std::lock_guard<std::mutex> guard(mutex);
const char *r = ptsname(fd);
assert(r != nullptr);
return r;
}
#endif
std::string PseudoTerminal::GetSecondaryName() const {
assert(m_primary_fd >= 0);
#if HAVE_PTSNAME_R
#if defined(__APPLE__)
if (__builtin_available(macos 10.13.4, iOS 11.3, tvOS 11.3, watchOS 4.4, *)) {
#endif
char buf[PATH_MAX];
buf[0] = '\0';
int r = ptsname_r(m_primary_fd, buf, sizeof(buf));
UNUSED_IF_ASSERT_DISABLED(r);
assert(r == 0);
return buf;
#if defined(__APPLE__)
} else {
return use_ptsname(m_primary_fd);
}
#endif
#else
return use_ptsname(m_primary_fd);
#endif
}
llvm::Expected<lldb::pid_t> PseudoTerminal::Fork() {
#if LLDB_ENABLE_POSIX
if (llvm::Error Err = OpenFirstAvailablePrimary(O_RDWR | O_CLOEXEC))
return std::move(Err);
pid_t pid = ::fork();
if (pid < 0) {
return llvm::errorCodeToError(
std::error_code(errno, std::generic_category()));
}
if (pid > 0) {
return pid;
}
::setsid();
if (llvm::Error Err = OpenSecondary(O_RDWR))
return std::move(Err);
ClosePrimaryFileDescriptor();
#if defined(TIOCSCTTY)
if (::ioctl(m_secondary_fd, TIOCSCTTY, (char *)0) < 0) {
return llvm::errorCodeToError(
std::error_code(errno, std::generic_category()));
}
#endif
for (int fd : {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}) {
if (::dup2(m_secondary_fd, fd) != fd) {
return llvm::errorCodeToError(
std::error_code(errno, std::generic_category()));
}
}
#endif
return 0;
}
int PseudoTerminal::GetPrimaryFileDescriptor() const { return m_primary_fd; }
int PseudoTerminal::GetSecondaryFileDescriptor() const {
return m_secondary_fd;
}
int PseudoTerminal::ReleasePrimaryFileDescriptor() {
int fd = m_primary_fd;
m_primary_fd = invalid_fd;
return fd;
}
int PseudoTerminal::ReleaseSecondaryFileDescriptor() {
int fd = m_secondary_fd;
m_secondary_fd = invalid_fd;
return fd;
}