Files
tai/taicpp/src/shell/ptyproc.cpp
T
2026-10-25 06:48:34 +01:00

81 lines
2.8 KiB
C++

#include "ptyproc.h"
#include <cerrno>
#include <cstdlib>
#include <fcntl.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
namespace detz::shell {
bool PtyProc::Available() { return true; }
PtyProc::PtyProc(const std::vector<std::string>& argv, const std::optional<std::string>& shellCommand, const std::optional<std::string>& cwd, const EnvMap* env, int rows, int cols) {
int master = ::posix_openpt(O_RDWR | O_NOCTTY);
if (master < 0) throw OsError(errno, "");
if (::grantpt(master) != 0 || ::unlockpt(master) != 0) { int e = errno; ::close(master); throw OsError(e, ""); }
char name[128];
if (::ptsname_r(master, name, sizeof name) != 0) { int e = errno; ::close(master); throw OsError(e, ""); }
int slave = ::open(name, O_RDWR | O_NOCTTY);
if (slave < 0) { int e = errno; ::close(master); throw OsError(e, ""); }
::fcntl(master, F_SETFD, FD_CLOEXEC);
try {
struct winsize ws = {};
ws.ws_row = (unsigned short)rows; ws.ws_col = (unsigned short)cols;
::ioctl(master, TIOCSWINSZ, &ws);
PopenOptions o;
o.argv = argv;
o.shellCommand = shellCommand;
o.cwd = cwd;
o.env = env;
o.in = o.out = o.err = StdMode::Fd;
o.inFd = o.outFd = o.errFd = slave;
o.adoptCtty = true;
proc_ = std::make_unique<Popen>(o);
} catch (...) {
::close(slave);
::close(master);
throw;
}
::close(slave);
master_ = master;
}
PtyProc::~PtyProc() { Close(); }
PtyProc::ReadResult PtyProc::Read(double timeoutSeconds) {
ReadResult r;
struct pollfd pfd = {master_, POLLIN, 0};
int ms = (int)(std::max(0.0, timeoutSeconds) * 1000.0);
int rc;
do { rc = ::poll(&pfd, 1, ms); } while (rc < 0 && errno == EINTR);
if (rc <= 0) return r; // select() reported nothing readable
char buf[65536];
ssize_t n;
do { n = ::read(master_, buf, sizeof buf); } while (n < 0 && errno == EINTR);
if (n <= 0) { r.kind = ReadKind::End; return r; } // EOF, or OSError (EIO) once the slave closed
r.kind = ReadKind::Data;
r.data.assign(buf, (size_t)n);
return r;
}
void PtyProc::Write(const std::string& text) { WriteBytes(text); }
void PtyProc::WriteBytes(const std::string& data) {
size_t off = 0;
while (off < data.size()) {
ssize_t n = ::write(master_, data.data() + off, data.size() - off);
if (n < 0) { if (errno == EINTR) continue; throw OsError(errno, ""); }
off += (size_t)n;
}
}
void PtyProc::Resize(int rows, int cols) {
struct winsize ws = {};
ws.ws_row = (unsigned short)rows; ws.ws_col = (unsigned short)cols;
if (::ioctl(master_, TIOCSWINSZ, &ws) != 0) throw OsError(errno, "");
}
bool PtyProc::EchoOff() const {
struct termios t;
if (::tcgetattr(master_, &t) != 0) return false;
return !(t.c_lflag & ECHO);
}
void PtyProc::Kill() { if (proc_) proc_->KillGroup(); }
void PtyProc::Close() { if (master_ >= 0) { ::close(master_); master_ = -1; } }
} // namespace detz::shell