15 #include <kpty_debug.h>
19 #ifdef __INTEL_COMPILER
25 #include <sys/ioctl.h>
26 #include <sys/param.h>
27 #include <sys/resource.h>
30 #include <sys/types.h>
54 #define UTEMPTER_ADD "add"
55 #define UTEMPTER_DEL "del"
57 #define UTEMPTER_ADD "login"
58 #define UTEMPTER_DEL "logout"
60 class UtemptProcess :
public QProcess
63 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
66 setChildProcessModifier([
this]() {
90 #if !defined(_PATH_UTMPX) && defined(_UTMPX_FILE)
91 #define _PATH_UTMPX _UTMPX_FILE
93 #if !defined(_PATH_WTMPX) && defined(_WTMPX_FILE)
94 #define _PATH_WTMPX _WTMPX_FILE
107 #if defined(_HPUX_SOURCE)
108 #define _TERMIOS_INCLUDED
112 #if HAVE_SYS_STROPTS_H
113 #include <sys/stropts.h>
114 #define _NEW_TTY_CTRL
118 #define _tcgetattr(fd, ttmode) tcgetattr(fd, ttmode)
119 #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__APPLE__) || defined(__DragonFly__)
120 #define _tcgetattr(fd, ttmode) ioctl(fd, TIOCGETA, (char *)ttmode)
122 #define _tcgetattr(fd, ttmode) ioctl(fd, TCGETS, (char *)ttmode)
126 #define _tcsetattr(fd, ttmode) tcsetattr(fd, TCSANOW, ttmode)
127 #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__APPLE__) || defined(__DragonFly__)
128 #define _tcsetattr(fd, ttmode) ioctl(fd, TIOCSETA, (char *)ttmode)
130 #define _tcsetattr(fd, ttmode) ioctl(fd, TCSETS, (char *)ttmode)
133 #include <qplatformdefs.h>
135 #define TTY_GROUP "tty"
139 #define PATH_MAX MAXPATHLEN
141 #define PATH_MAX 1024
153 KPtyPrivate::KPtyPrivate(
KPty *parent)
160 utempterPath = QStringLiteral(UTEMPTER_PATH);
164 KPtyPrivate::~KPtyPrivate()
169 bool KPtyPrivate::chownpty(
bool grant)
181 : d_ptr(new KPtyPrivate(this))
200 if (d->masterFd >= 0) {
218 if (::openpty(&d->masterFd, &d->slaveFd, ptsn,
nullptr,
nullptr)) {
221 qCWarning(KPTY_LOG) <<
"Can't open a pseudo teletype";
228 #if HAVE_PTSNAME || defined(TIOCGPTN)
230 #if HAVE_POSIX_OPENPT
231 d->masterFd = ::posix_openpt(O_RDWR | O_NOCTTY);
233 d->masterFd = ::getpt();
234 #elif defined(PTM_DEVICE)
235 d->masterFd = QT_OPEN(PTM_DEVICE, QT_OPEN_RDWR | O_NOCTTY);
237 #error No method to open a PTY master detected.
239 if (d->masterFd >= 0) {
241 char *ptsn = ptsname(d->masterFd);
246 if (!ioctl(d->masterFd, TIOCGPTN, &ptyno)) {
248 sprintf(buf,
"/dev/pts/%d", ptyno);
252 if (!grantpt(d->masterFd)) {
262 #endif // HAVE_PTSNAME || TIOCGPTN
265 for (
const char *s3 =
"pqrstuvwxyzabcde"; *s3; s3++) {
266 for (
const char *s4 =
"0123456789abcdef"; *s4; s4++) {
270 d->masterFd = QT_OPEN(ptyName.
data(), QT_OPEN_RDWR);
271 if (d->masterFd >= 0) {
272 if (!
access(d->ttyName.data(), R_OK | W_OK)) {
274 struct group *p = getgrnam(TTY_GROUP);
276 p = getgrnam(
"wheel");
278 gid_t gid = p ? p->gr_gid : getgid();
280 chown(d->ttyName.data(), getuid(), gid);
281 chmod(d->ttyName.data(), S_IRUSR | S_IWUSR | S_IWGRP);
291 qCWarning(KPTY_LOG) <<
"Can't open a pseudo teletype";
303 && !d->chownpty(
true)) {
304 qCWarning(KPTY_LOG) <<
"chownpty failed for device " << ptyName <<
"::" << d->ttyName <<
"\nThis means the communication can be eavesdropped." << endl;
310 revoke(d->ttyName.data());
314 unlockpt(d->masterFd);
315 #elif defined(TIOCSPTLCK)
317 ioctl(d->masterFd, TIOCSPTLCK, &flag);
320 d->slaveFd = QT_OPEN(d->ttyName.data(), QT_OPEN_RDWR | O_NOCTTY);
321 if (d->slaveFd < 0) {
322 qCWarning(KPTY_LOG) <<
"Can't open slave pseudo teletype";
330 fcntl(d->masterFd, F_SETFD, FD_CLOEXEC);
331 fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC);
338 #if !HAVE_PTSNAME && !defined(TIOCGPTN)
339 qCWarning(KPTY_LOG) <<
"Unsupported attempt to open pty with fd" << fd;
344 if (d->masterFd >= 0) {
345 qCWarning(KPTY_LOG) <<
"Attempting to open an already open pty";
349 d->ownMaster =
false;
352 char *ptsn = ptsname(fd);
357 if (!ioctl(fd, TIOCGPTN, &ptyno)) {
359 sprintf(buf,
"/dev/pts/%d", ptyno);
363 qCWarning(KPTY_LOG) <<
"Failed to determine pty slave device for fd" << fd;
381 if (d->slaveFd < 0) {
392 if (d->slaveFd >= 0) {
395 if (d->masterFd < 0) {
396 qCWarning(KPTY_LOG) <<
"Attempting to open pty slave while master is closed";
399 d->slaveFd = QT_OPEN(d->ttyName.data(), QT_OPEN_RDWR | O_NOCTTY);
400 if (d->slaveFd < 0) {
401 qCWarning(KPTY_LOG) <<
"Can't open slave pseudo teletype";
404 fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC);
412 if (d->masterFd < 0) {
419 if (memcmp(d->ttyName.data(),
"/dev/pts/", 9)) {
422 if (!
stat(d->ttyName.data(), &st)) {
423 chown(d->ttyName.data(), 0, st.st_gid == getgid() ? 0 : -1);
424 chmod(d->ttyName.data(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
427 fcntl(d->masterFd, F_SETFD, 0);
452 ioctl(d->slaveFd, TIOCSCTTY, 0);
456 tcsetpgrp(d->slaveFd, pgrp);
467 if (!d->utempterPath.isEmpty()) {
468 UtemptProcess utemptProcess;
469 utemptProcess.cmdFd = d->masterFd;
470 utemptProcess.setProgram(d->utempterPath);
473 utemptProcess.start();
474 utemptProcess.waitForFinished();
479 struct utmpx l_struct;
481 struct utmp l_struct;
483 memset(&l_struct, 0,
sizeof(l_struct));
487 strncpy(l_struct.ut_name, user,
sizeof(l_struct.ut_name));
491 strncpy(l_struct.ut_host, remotehost,
sizeof(l_struct.ut_host));
492 #if HAVE_STRUCT_UTMP_UT_SYSLEN
493 l_struct.ut_syslen = qMin(strlen(remotehost),
sizeof(l_struct.ut_host));
499 const char *str_ptr = d->ttyName.data();
500 if (!memcmp(str_ptr,
"/dev/", 5)) {
503 strncpy(l_struct.ut_line, str_ptr,
sizeof(l_struct.ut_line));
504 #if HAVE_STRUCT_UTMP_UT_ID
505 strncpy(l_struct.ut_id, str_ptr + strlen(str_ptr) -
sizeof(l_struct.ut_id),
sizeof(l_struct.ut_id));
510 gettimeofday(&l_struct.ut_tv, 0);
512 l_struct.ut_time = time(0);
522 #if HAVE_STRUCT_UTMP_UT_TYPE
523 l_struct.ut_type = USER_PROCESS;
525 #if HAVE_STRUCT_UTMP_UT_PID
526 l_struct.ut_pid = getpid();
527 #if HAVE_STRUCT_UTMP_UT_SESSION
528 l_struct.ut_session = getsid(0);
532 utmpxname(_PATH_UTMPX);
534 pututxline(&l_struct);
536 updwtmpx(_PATH_WTMPX, &l_struct);
538 utmpname(_PATH_UTMP);
540 pututline(&l_struct);
542 updwtmp(_PATH_WTMP, &l_struct);
554 if (!d->utempterPath.isEmpty()) {
555 UtemptProcess utemptProcess;
556 utemptProcess.cmdFd = d->masterFd;
557 utemptProcess.setProgram(d->utempterPath);
558 utemptProcess.setArguments(
QStringList(QStringLiteral(UTEMPTER_DEL)));
560 utemptProcess.start();
561 utemptProcess.waitForFinished();
567 const char *str_ptr = d->ttyName.data();
568 if (!memcmp(str_ptr,
"/dev/", 5)) {
573 const char *sl_ptr = strrchr(str_ptr,
'/');
575 str_ptr = sl_ptr + 1;
581 ::logoutx(str_ptr, 0, DEAD_PROCESS);
587 struct utmpx l_struct, *ut;
589 struct utmp l_struct, *ut;
591 memset(&l_struct, 0,
sizeof(l_struct));
593 strncpy(l_struct.ut_line, str_ptr,
sizeof(l_struct.ut_line));
596 utmpxname(_PATH_UTMPX);
598 if ((ut = getutxline(&l_struct))) {
600 utmpname(_PATH_UTMP);
602 if ((ut = getutline(&l_struct))) {
604 memset(ut->ut_name, 0,
sizeof(*ut->ut_name));
605 memset(ut->ut_host, 0,
sizeof(*ut->ut_host));
606 #if HAVE_STRUCT_UTMP_UT_SYSLEN
609 #if HAVE_STRUCT_UTMP_UT_TYPE
610 ut->ut_type = DEAD_PROCESS;
613 gettimeofday(&(ut->ut_tv), 0);
618 ut->ut_time = time(0);
631 return _tcgetattr(d->masterFd, ttmode) == 0;
638 return _tcsetattr(d->masterFd, ttmode) == 0;
645 struct winsize winSize;
646 winSize.ws_row = (
unsigned short)lines;
647 winSize.ws_col = (
unsigned short)columns;
648 winSize.ws_ypixel = (
unsigned short)height;
649 winSize.ws_xpixel = (
unsigned short)width;
650 return ioctl(d->masterFd, TIOCSWINSZ, (
char *)&winSize) == 0;
660 struct ::termios ttmode;
665 ttmode.c_lflag &= ~ECHO;
667 ttmode.c_lflag |= ECHO;
676 return d->ttyName.data();
697 d->withCTty = enable;