15#include <kpty_debug.h>
19#ifdef __INTEL_COMPILER
27#include <sys/resource.h>
54#define UTEMPTER_ADD "add"
55#define UTEMPTER_DEL "del"
57#define UTEMPTER_ADD "login"
58#define UTEMPTER_DEL "logout"
80#if !defined(_PATH_UTMPX) && defined(_UTMPX_FILE)
81#define _PATH_UTMPX _UTMPX_FILE
83#if !defined(_PATH_WTMPX) && defined(_WTMPX_FILE)
84#define _PATH_WTMPX _WTMPX_FILE
97#if defined(_HPUX_SOURCE)
98#define _TERMIOS_INCLUDED
102#if HAVE_SYS_STROPTS_H
103#include <sys/stropts.h>
108#define _tcgetattr(fd, ttmode) tcgetattr(fd, ttmode)
109#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__APPLE__) || defined(__DragonFly__)
110#define _tcgetattr(fd, ttmode) ioctl(fd, TIOCGETA, (char *)ttmode)
112#define _tcgetattr(fd, ttmode) ioctl(fd, TCGETS, (char *)ttmode)
116#define _tcsetattr(fd, ttmode) tcsetattr(fd, TCSANOW, ttmode)
117#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__APPLE__) || defined(__DragonFly__)
118#define _tcsetattr(fd, ttmode) ioctl(fd, TIOCSETA, (char *)ttmode)
120#define _tcsetattr(fd, ttmode) ioctl(fd, TCSETS, (char *)ttmode)
123#include <qplatformdefs.h>
125#define TTY_GROUP "tty"
129#define PATH_MAX MAXPATHLEN
143KPtyPrivate::KPtyPrivate(
KPty *parent)
150 utempterPath = QStringLiteral(UTEMPTER_PATH);
154KPtyPrivate::~KPtyPrivate()
159bool KPtyPrivate::chownpty(
bool grant)
171 : d_ptr(new KPtyPrivate(this))
190 if (d->masterFd >= 0) {
208 if (::openpty(&d->masterFd, &d->slaveFd, ptsn,
nullptr,
nullptr)) {
211 qCWarning(KPTY_LOG) <<
"Can't open a pseudo teletype";
218#if HAVE_PTSNAME || defined(TIOCGPTN)
221 d->masterFd = ::posix_openpt(O_RDWR | O_NOCTTY);
223 d->masterFd = ::getpt();
224#elif defined(PTM_DEVICE)
225 d->masterFd = QT_OPEN(PTM_DEVICE, QT_OPEN_RDWR | O_NOCTTY);
227#error No method to open a PTY master detected.
229 if (d->masterFd >= 0) {
231 char *ptsn = ptsname(d->masterFd);
236 if (!ioctl(d->masterFd, TIOCGPTN, &ptyno)) {
238 sprintf(buf,
"/dev/pts/%d", ptyno);
242 if (!grantpt(d->masterFd)) {
255 for (
const char *s3 =
"pqrstuvwxyzabcde"; *s3; s3++) {
256 for (
const char *s4 =
"0123456789abcdef"; *s4; s4++) {
260 d->masterFd = QT_OPEN(ptyName.
data(), QT_OPEN_RDWR);
261 if (d->masterFd >= 0) {
262 if (!access(d->ttyName.data(), R_OK | W_OK)) {
264 struct group *p = getgrnam(TTY_GROUP);
266 p = getgrnam(
"wheel");
268 gid_t gid = p ? p->gr_gid : getgid();
270 chown(d->ttyName.data(), getuid(), gid);
271 chmod(d->ttyName.data(), S_IRUSR | S_IWUSR | S_IWGRP);
281 qCWarning(KPTY_LOG) <<
"Can't open a pseudo teletype";
293 && !d->chownpty(
true)) {
294 qCWarning(KPTY_LOG) <<
"chownpty failed for device " << ptyName <<
"::" << d->ttyName <<
"\nThis means the communication can be eavesdropped." << endl;
300 revoke(d->ttyName.data());
304 unlockpt(d->masterFd);
305#elif defined(TIOCSPTLCK)
307 ioctl(d->masterFd, TIOCSPTLCK, &flag);
310 d->slaveFd = QT_OPEN(d->ttyName.data(), QT_OPEN_RDWR | O_NOCTTY);
311 if (d->slaveFd < 0) {
312 qCWarning(KPTY_LOG) <<
"Can't open slave pseudo teletype";
320 fcntl(d->masterFd, F_SETFD, FD_CLOEXEC);
321 fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC);
328#if !HAVE_PTSNAME && !defined(TIOCGPTN)
329 qCWarning(KPTY_LOG) <<
"Unsupported attempt to open pty with fd" << fd;
334 if (d->masterFd >= 0) {
335 qCWarning(KPTY_LOG) <<
"Attempting to open an already open pty";
339 d->ownMaster =
false;
342 char *ptsn = ptsname(fd);
347 if (!ioctl(fd, TIOCGPTN, &ptyno)) {
349 sprintf(buf,
"/dev/pts/%d", ptyno);
353 qCWarning(KPTY_LOG) <<
"Failed to determine pty slave device for fd" << fd;
371 if (d->slaveFd < 0) {
382 if (d->slaveFd >= 0) {
385 if (d->masterFd < 0) {
386 qCWarning(KPTY_LOG) <<
"Attempting to open pty slave while master is closed";
389 d->slaveFd = QT_OPEN(d->ttyName.data(), QT_OPEN_RDWR | O_NOCTTY);
390 if (d->slaveFd < 0) {
391 qCWarning(KPTY_LOG) <<
"Can't open slave pseudo teletype";
394 fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC);
402 if (d->masterFd < 0) {
409 if (memcmp(d->ttyName.data(),
"/dev/pts/", 9)) {
412 if (!stat(d->ttyName.data(), &st)) {
413 chown(d->ttyName.data(), 0, st.st_gid == getgid() ? 0 : -1);
414 chmod(d->ttyName.data(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
417 fcntl(d->masterFd, F_SETFD, 0);
442 ioctl(d->slaveFd, TIOCSCTTY, 0);
446 tcsetpgrp(d->slaveFd, pgrp);
457 if (!d->utempterPath.isEmpty()) {
458 UtemptProcess utemptProcess;
459 utemptProcess.cmdFd = d->masterFd;
460 utemptProcess.setProgram(d->utempterPath);
463 utemptProcess.start();
464 utemptProcess.waitForFinished();
469 struct utmpx l_struct;
471 struct utmp l_struct;
473 memset(&l_struct, 0,
sizeof(l_struct));
477 strncpy(l_struct.ut_name, user,
sizeof(l_struct.ut_name));
481 strncpy(l_struct.ut_host, remotehost,
sizeof(l_struct.ut_host));
482#if HAVE_STRUCT_UTMP_UT_SYSLEN
483 l_struct.ut_syslen = qMin(strlen(remotehost),
sizeof(l_struct.ut_host));
489 const char *str_ptr = d->ttyName.data();
490 if (!memcmp(str_ptr,
"/dev/", 5)) {
493 strncpy(l_struct.ut_line, str_ptr,
sizeof(l_struct.ut_line));
494#if HAVE_STRUCT_UTMP_UT_ID
495 strncpy(l_struct.ut_id, str_ptr + strlen(str_ptr) -
sizeof(l_struct.ut_id),
sizeof(l_struct.ut_id));
500 gettimeofday(&l_struct.ut_tv, 0);
502 l_struct.ut_time = time(0);
512#if HAVE_STRUCT_UTMP_UT_TYPE
513 l_struct.ut_type = USER_PROCESS;
515#if HAVE_STRUCT_UTMP_UT_PID
516 l_struct.ut_pid = getpid();
517#if HAVE_STRUCT_UTMP_UT_SESSION
518 l_struct.ut_session = getsid(0);
522 utmpxname(_PATH_UTMPX);
524 pututxline(&l_struct);
526 updwtmpx(_PATH_WTMPX, &l_struct);
528 utmpname(_PATH_UTMP);
530 pututline(&l_struct);
532 updwtmp(_PATH_WTMP, &l_struct);
544 if (!d->utempterPath.isEmpty()) {
545 UtemptProcess utemptProcess;
546 utemptProcess.cmdFd = d->masterFd;
547 utemptProcess.setProgram(d->utempterPath);
548 utemptProcess.setArguments(
QStringList(QStringLiteral(UTEMPTER_DEL)));
550 utemptProcess.start();
551 utemptProcess.waitForFinished();
557 const char *str_ptr = d->ttyName.data();
558 if (!memcmp(str_ptr,
"/dev/", 5)) {
563 const char *sl_ptr = strrchr(str_ptr,
'/');
565 str_ptr = sl_ptr + 1;
571 ::logoutx(str_ptr, 0, DEAD_PROCESS);
577 struct utmpx l_struct, *ut;
579 struct utmp l_struct, *ut;
581 memset(&l_struct, 0,
sizeof(l_struct));
583 strncpy(l_struct.ut_line, str_ptr,
sizeof(l_struct.ut_line));
586 utmpxname(_PATH_UTMPX);
588 if ((ut = getutxline(&l_struct))) {
590 utmpname(_PATH_UTMP);
592 if ((ut = getutline(&l_struct))) {
594 memset(ut->ut_name, 0,
sizeof(*ut->ut_name));
595 memset(ut->ut_host, 0,
sizeof(*ut->ut_host));
596#if HAVE_STRUCT_UTMP_UT_SYSLEN
599#if HAVE_STRUCT_UTMP_UT_TYPE
600 ut->ut_type = DEAD_PROCESS;
603 gettimeofday(&(ut->ut_tv), 0);
608 ut->ut_time = time(0);
621 return _tcgetattr(d->masterFd, ttmode) == 0;
628 return _tcsetattr(d->masterFd, ttmode) == 0;
635 struct winsize winSize;
636 winSize.ws_row = (
unsigned short)lines;
637 winSize.ws_col = (
unsigned short)columns;
638 winSize.ws_ypixel = (
unsigned short)height;
639 winSize.ws_xpixel = (
unsigned short)width;
640 return ioctl(d->masterFd, TIOCSWINSZ, (
char *)&winSize) == 0;
650 struct ::termios ttmode;
655 ttmode.c_lflag &= ~ECHO;
657 ttmode.c_lflag |= ECHO;
666 return d->ttyName.data();
687 d->withCTty = enable;
Provides primitives for opening & closing a pseudo TTY pair, assigning the controlling TTY,...
void setCTtyEnabled(bool enable)
Whether this will be a controlling terminal.
void setCTty()
Creates a new session and process group and makes this pty the controlling tty.
std::unique_ptr< KPtyPrivate > const d_ptr
void login(const char *user=nullptr, const char *remotehost=nullptr)
Creates an utmp entry for the tty.
bool setWinSize(int lines, int columns, int height, int width)
Change the logical (screen) size of the pty.
bool setEcho(bool echo)
Set whether the pty should echo input.
bool open()
Create a pty master/slave pair.
bool tcGetAttr(struct ::termios *ttmode) const
Wrapper around tcgetattr(3).
void closeSlave()
Close the pty slave descriptor.
void logout()
Removes the utmp entry for this tty.
const char * ttyName() const
bool tcSetAttr(struct ::termios *ttmode)
Wrapper around tcsetattr(3) with mode TCSANOW.
void close()
Close the pty master/slave pair.
bool openSlave()
Open the pty slave descriptor.
QString decodeName(const QByteArray &localFileName)
bool exists(const QString &path)
uint ownerId() const const
QFile::Permissions permissions() const const
int execute(const QString &program, const QStringList &arguments)
void setChildProcessModifier(const std::function< void()> &modifier)
QString fromLocal8Bit(QByteArrayView str)
QString number(double n, char format, int precision)
QByteArray toLatin1() const const