diff options
author | 2020-08-03 14:33:06 +0000 | |
---|---|---|
committer | 2020-08-03 14:33:06 +0000 | |
commit | 061da546b983eb767bad15e67af1174fb0bcf31c (patch) | |
tree | 83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/source/Host/common/PseudoTerminal.cpp | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/source/Host/common/PseudoTerminal.cpp')
-rw-r--r-- | gnu/llvm/lldb/source/Host/common/PseudoTerminal.cpp | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/source/Host/common/PseudoTerminal.cpp b/gnu/llvm/lldb/source/Host/common/PseudoTerminal.cpp new file mode 100644 index 00000000000..85828283e21 --- /dev/null +++ b/gnu/llvm/lldb/source/Host/common/PseudoTerminal.cpp @@ -0,0 +1,287 @@ +//===-- PseudoTerminal.cpp --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/PseudoTerminal.h" +#include "lldb/Host/Config.h" + +#include "llvm/Support/Errno.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#if defined(TIOCSCTTY) +#include <sys/ioctl.h> +#endif + +#include "lldb/Host/PosixApi.h" + +#if defined(__ANDROID__) +int posix_openpt(int flags); +#endif + +using namespace lldb_private; + +// Write string describing error number +static void ErrnoToStr(char *error_str, size_t error_len) { + std::string strerror = llvm::sys::StrError(); + ::snprintf(error_str, error_len, "%s", strerror.c_str()); +} + +// PseudoTerminal constructor +PseudoTerminal::PseudoTerminal() + : m_master_fd(invalid_fd), m_slave_fd(invalid_fd) {} + +// Destructor +// +// The destructor will close the master and slave file descriptors if they are +// valid and ownership has not been released using the +// ReleaseMasterFileDescriptor() or the ReleaseSaveFileDescriptor() member +// functions. +PseudoTerminal::~PseudoTerminal() { + CloseMasterFileDescriptor(); + CloseSlaveFileDescriptor(); +} + +// Close the master file descriptor if it is valid. +void PseudoTerminal::CloseMasterFileDescriptor() { + if (m_master_fd >= 0) { + ::close(m_master_fd); + m_master_fd = invalid_fd; + } +} + +// Close the slave file descriptor if it is valid. +void PseudoTerminal::CloseSlaveFileDescriptor() { + if (m_slave_fd >= 0) { + ::close(m_slave_fd); + m_slave_fd = invalid_fd; + } +} + +// Open the first available pseudo terminal with OFLAG as the permissions. The +// file descriptor is stored in this object and can be accessed with the +// MasterFileDescriptor() accessor. The ownership of the master file descriptor +// can be released using the ReleaseMasterFileDescriptor() accessor. If this +// object has a valid master files descriptor when its destructor is called, it +// will close the master file descriptor, therefore clients must call +// ReleaseMasterFileDescriptor() if they wish to use the master file descriptor +// after this object is out of scope or destroyed. +// +// RETURNS: +// True when successful, false indicating an error occurred. +bool PseudoTerminal::OpenFirstAvailableMaster(int oflag, char *error_str, + size_t error_len) { + if (error_str) + error_str[0] = '\0'; + +#if LLDB_ENABLE_POSIX + // Open the master side of a pseudo terminal + m_master_fd = ::posix_openpt(oflag); + if (m_master_fd < 0) { + if (error_str) + ErrnoToStr(error_str, error_len); + return false; + } + + // Grant access to the slave pseudo terminal + if (::grantpt(m_master_fd) < 0) { + if (error_str) + ErrnoToStr(error_str, error_len); + CloseMasterFileDescriptor(); + return false; + } + + // Clear the lock flag on the slave pseudo terminal + if (::unlockpt(m_master_fd) < 0) { + if (error_str) + ErrnoToStr(error_str, error_len); + CloseMasterFileDescriptor(); + return false; + } + + return true; +#else + if (error_str) + ::snprintf(error_str, error_len, "%s", "pseudo terminal not supported"); + return false; +#endif +} + +// Open the slave pseudo terminal for the current master pseudo terminal. A +// master pseudo terminal should already be valid prior to calling this +// function (see OpenFirstAvailableMaster()). The file descriptor is stored +// this object's member variables and can be accessed via the +// GetSlaveFileDescriptor(), or released using the ReleaseSlaveFileDescriptor() +// member function. +// +// RETURNS: +// True when successful, false indicating an error occurred. +bool PseudoTerminal::OpenSlave(int oflag, char *error_str, size_t error_len) { + if (error_str) + error_str[0] = '\0'; + + CloseSlaveFileDescriptor(); + + // Open the master side of a pseudo terminal + const char *slave_name = GetSlaveName(error_str, error_len); + + if (slave_name == nullptr) + return false; + + m_slave_fd = llvm::sys::RetryAfterSignal(-1, ::open, slave_name, oflag); + + if (m_slave_fd < 0) { + if (error_str) + ErrnoToStr(error_str, error_len); + return false; + } + + return true; +} + +// Get the name of the slave pseudo terminal. A master pseudo terminal should +// already be valid prior to calling this function (see +// OpenFirstAvailableMaster()). +// +// RETURNS: +// NULL if no valid master pseudo terminal or if ptsname() fails. +// The name of the slave pseudo terminal as a NULL terminated C string +// that comes from static memory, so a copy of the string should be +// made as subsequent calls can change this value. +const char *PseudoTerminal::GetSlaveName(char *error_str, + size_t error_len) const { + if (error_str) + error_str[0] = '\0'; + + if (m_master_fd < 0) { + if (error_str) + ::snprintf(error_str, error_len, "%s", + "master file descriptor is invalid"); + return nullptr; + } + const char *slave_name = ::ptsname(m_master_fd); + + if (error_str && slave_name == nullptr) + ErrnoToStr(error_str, error_len); + + return slave_name; +} + +// Fork a child process and have its stdio routed to a pseudo terminal. +// +// In the parent process when a valid pid is returned, the master file +// descriptor can be used as a read/write access to stdio of the child process. +// +// In the child process the stdin/stdout/stderr will already be routed to the +// slave pseudo terminal and the master file descriptor will be closed as it is +// no longer needed by the child process. +// +// This class will close the file descriptors for the master/slave when the +// destructor is called, so be sure to call ReleaseMasterFileDescriptor() or +// ReleaseSlaveFileDescriptor() if any file descriptors are going to be used +// past the lifespan of this object. +// +// RETURNS: +// in the parent process: the pid of the child, or -1 if fork fails +// in the child process: zero +lldb::pid_t PseudoTerminal::Fork(char *error_str, size_t error_len) { + if (error_str) + error_str[0] = '\0'; + pid_t pid = LLDB_INVALID_PROCESS_ID; +#if LLDB_ENABLE_POSIX + int flags = O_RDWR; + flags |= O_CLOEXEC; + if (OpenFirstAvailableMaster(flags, error_str, error_len)) { + // Successfully opened our master pseudo terminal + + pid = ::fork(); + if (pid < 0) { + // Fork failed + if (error_str) + ErrnoToStr(error_str, error_len); + } else if (pid == 0) { + // Child Process + ::setsid(); + + if (OpenSlave(O_RDWR, error_str, error_len)) { + // Successfully opened slave + + // Master FD should have O_CLOEXEC set, but let's close it just in + // case... + CloseMasterFileDescriptor(); + +#if defined(TIOCSCTTY) + // Acquire the controlling terminal + if (::ioctl(m_slave_fd, TIOCSCTTY, (char *)0) < 0) { + if (error_str) + ErrnoToStr(error_str, error_len); + } +#endif + // Duplicate all stdio file descriptors to the slave pseudo terminal + if (::dup2(m_slave_fd, STDIN_FILENO) != STDIN_FILENO) { + if (error_str && !error_str[0]) + ErrnoToStr(error_str, error_len); + } + + if (::dup2(m_slave_fd, STDOUT_FILENO) != STDOUT_FILENO) { + if (error_str && !error_str[0]) + ErrnoToStr(error_str, error_len); + } + + if (::dup2(m_slave_fd, STDERR_FILENO) != STDERR_FILENO) { + if (error_str && !error_str[0]) + ErrnoToStr(error_str, error_len); + } + } + } else { + // Parent Process + // Do nothing and let the pid get returned! + } + } +#endif + return pid; +} + +// The master file descriptor accessor. This object retains ownership of the +// master file descriptor when this accessor is used. Use +// ReleaseMasterFileDescriptor() if you wish this object to release ownership +// of the master file descriptor. +// +// Returns the master file descriptor, or -1 if the master file descriptor is +// not currently valid. +int PseudoTerminal::GetMasterFileDescriptor() const { return m_master_fd; } + +// The slave file descriptor accessor. +// +// Returns the slave file descriptor, or -1 if the slave file descriptor is not +// currently valid. +int PseudoTerminal::GetSlaveFileDescriptor() const { return m_slave_fd; } + +// Release ownership of the master pseudo terminal file descriptor without +// closing it. The destructor for this class will close the master file +// descriptor if the ownership isn't released using this call and the master +// file descriptor has been opened. +int PseudoTerminal::ReleaseMasterFileDescriptor() { + // Release ownership of the master pseudo terminal file descriptor without + // closing it. (the destructor for this class will close it otherwise!) + int fd = m_master_fd; + m_master_fd = invalid_fd; + return fd; +} + +// Release ownership of the slave pseudo terminal file descriptor without +// closing it. The destructor for this class will close the slave file +// descriptor if the ownership isn't released using this call and the slave +// file descriptor has been opened. +int PseudoTerminal::ReleaseSlaveFileDescriptor() { + // Release ownership of the slave pseudo terminal file descriptor without + // closing it (the destructor for this class will close it otherwise!) + int fd = m_slave_fd; + m_slave_fd = invalid_fd; + return fd; +} |