Everything compiles; main.cpp still uses the removed placeholder agent API. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
81 lines
2.8 KiB
C++
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
|