changeset 13662:32f4713142d8

Reverted KPty to work with MacOS again.
author Jacob Dawid <jacob.dawid@googlemail.com>
date Fri, 09 Sep 2011 20:58:52 +0200
parents 8a1896fb82d4
children 746c99f44c4b
files gui/src/terminal/KPty.cpp
diffstat 1 files changed, 444 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/gui/src/terminal/KPty.cpp	Thu Sep 08 00:06:43 2011 -0500
+++ b/gui/src/terminal/KPty.cpp	Fri Sep 09 20:58:52 2011 +0200
@@ -23,21 +23,109 @@
 */
 
 #include "KPty.h"
+
+#if defined(Q_OS_MAC)
+#define HAVE_UTIL_H
+#define HAVE_UTMPX
+#define _UTMPX_COMPAT
+#define HAVE_PTSNAME
+#define HAVE_SYS_TIME_H
+#else
+#define HAVE_PTY_H
+#endif
+
+#define HAVE_OPENPTY
+
+#include <QtCore/QByteArray>
+
+#ifdef __sgi
+#define __svr4__
+#endif
+
+#ifdef __osf__
+#define _OSF_SOURCE
+#include <float.h>
+#endif
+
+#ifdef _AIX
+#define _ALL_SOURCE
+#endif
+
+// __USE_XOPEN isn't defined by default in ICC
+// (needed for ptsname(), grantpt() and unlockpt())
+#ifdef __INTEL_COMPILER
+#ifndef __USE_XOPEN
+#define __USE_XOPEN
+#endif
+#endif
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include <errno.h>
 #include <fcntl.h>
+#include <time.h>
+#include <stdlib.h>
 #include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <grp.h>
 
+#if defined(HAVE_PTY_H)
 #include <pty.h>
-#include <utmp.h>
+#endif
+
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#elif defined(HAVE_UTIL_H)
+#include <util.h>
+#endif
+
+#define HAVE_UTMPX
+#define _UTMPX_COMPAT
 
-#define PATH_MAX 1024
+#ifdef HAVE_UTEMPTER
+extern "C"
+{
+#include <utempter.h>
+}
+#else
+#include <utmp.h>
+#ifdef HAVE_UTMPX
+#include <utmpx.h>
+#endif
+#if !defined(_PATH_UTMPX) && defined(_UTMPX_FILE)
+#define _PATH_UTMPX _UTMPX_FILE
+#endif
+#if !defined(_PATH_WTMPX) && defined(_WTMPX_FILE)
+#define _PATH_WTMPX _WTMPX_FILE
+#endif
+#endif
 
 /* for HP-UX (some versions) the extern C is needed, and for other
    platforms it doesn't hurt */
 extern "C"
 {
 #include <termios.h>
+#if defined(HAVE_TERMIO_H)
+#include <termio.h>		// struct winsize on some systems
+#endif
 }
 
+#if defined (_HPUX_SOURCE)
+#define _TERMIOS_INCLUDED
+#include <bsdtty.h>
+#endif
+
+#ifdef HAVE_SYS_STROPTS_H
+#include <sys/stropts.h>	// Defines I_PUSH
+#define _NEW_TTY_CTRL
+#endif
+
 #if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__)
 #define _tcgetattr(fd, ttmode) ioctl(fd, TIOCGETA, (char *)ttmode)
 #else
@@ -62,6 +150,22 @@
 
 #define TTY_GROUP "tty"
 
+#ifndef PATH_MAX
+#ifdef MAXPATHLEN
+#define PATH_MAX MAXPATHLEN
+#else
+#define PATH_MAX 1024
+#endif
+#endif
+
+///////////////////////
+// private functions //
+///////////////////////
+
+//////////////////
+// private data //
+//////////////////
+
 KPtyPrivate::KPtyPrivate (KPty * parent):
 masterFd (-1),
 slaveFd (-1),
@@ -74,6 +178,21 @@
 {
 }
 
+#ifndef HAVE_OPENPTY
+bool
+KPtyPrivate::chownpty (bool grant)
+{
+  return !QProcess::execute (KStandardDirs::findExe ("kgrantpty"),
+                             QStringList () << (grant ? "--grant" :
+                                                "--revoke") << QString::
+                             number (masterFd));
+}
+#endif
+
+/////////////////////////////
+// public member functions //
+/////////////////////////////
+
 KPty::KPty ():
 d_ptr (new KPtyPrivate (this))
 {
@@ -101,6 +220,8 @@
 
   d->ownMaster = true;
 
+  QByteArray ptyName;
+
   // Find a master pty that we can open ////////////////////////////////
 
   // Because not all the pty animals are created equal, they want to
@@ -108,15 +229,174 @@
 
   // We try, as we know them, one by one.
 
+#ifdef HAVE_OPENPTY
+
   char ptsn[PATH_MAX];
   if (::openpty (&d->masterFd, &d->slaveFd, ptsn, 0, 0))
     {
       d->masterFd = -1;
       d->slaveFd = -1;
+      //kWarning(175) << "Can't open a pseudo teletype";
       return false;
     }
   d->ttyName = ptsn;
 
+#else
+
+#ifdef HAVE__GETPTY		// irix
+
+  char *ptsn =
+    _getpty (&d->masterFd, O_RDWR | O_NOCTTY, S_IRUSR | S_IWUSR, 0);
+  if (ptsn)
+    {
+      d->ttyName = ptsn;
+      goto grantedpt;
+    }
+
+#elif defined(HAVE_PTSNAME) || defined(TIOCGPTN)
+
+#ifdef HAVE_POSIX_OPENPT
+  d->masterFd =::posix_openpt (O_RDWR | O_NOCTTY);
+#elif defined(HAVE_GETPT)
+  d->masterFd =::getpt ();
+#elif defined(PTM_DEVICE)
+  //d->masterFd = KDE_open(PTM_DEVICE, O_RDWR|O_NOCTTY);
+  d->masterFd =::open (PTM_DEVICE, O_RDWR | O_NOCTTY);
+#else
+#error No method to open a PTY master detected.
+#endif
+  if (d->masterFd >= 0)
+    {
+#ifdef HAVE_PTSNAME
+      char *ptsn = ptsname (d->masterFd);
+      if (ptsn)
+        {
+          d->ttyName = ptsn;
+#else
+      int ptyno;
+      if (!ioctl (d->masterFd, TIOCGPTN, &ptyno))
+        {
+          char buf[32];
+          sprintf (buf, "/dev/pts/%d", ptyno);
+          d->ttyName = buf;
+#endif
+#ifdef HAVE_GRANTPT
+          if (!grantpt (d->masterFd))
+            goto grantedpt;
+#else
+          goto gotpty;
+#endif
+        }
+      ::close (d->masterFd);
+      d->masterFd = -1;
+    }
+#endif // HAVE_PTSNAME || TIOCGPTN
+
+  // Linux device names, FIXME: Trouble on other systems?
+  for (const char *s3 = "pqrstuvwxyzabcde"; *s3; s3++)
+    {
+      for (const char *s4 = "0123456789abcdef"; *s4; s4++)
+        {
+          ptyName = QString ().sprintf ("/dev/pty%c%c", *s3, *s4).toAscii ();
+          d->ttyName =
+            QString ().sprintf ("/dev/tty%c%c", *s3, *s4).toAscii ();
+
+          d->masterFd =::open (ptyName.data (), O_RDWR);
+          if (d->masterFd >= 0)
+            {
+#ifdef Q_OS_SOLARIS
+              /* Need to check the process group of the pty.
+               * If it exists, then the slave pty is in use,
+               * and we need to get another one.
+               */
+              int pgrp_rtn;
+              if (ioctl (d->masterFd, TIOCGPGRP, &pgrp_rtn) == 0
+                  || errno != EIO)
+                {
+                  ::close (d->masterFd);
+                  d->masterFd = -1;
+                  continue;
+                }
+#endif /* Q_OS_SOLARIS */
+              if (!access (d->ttyName.data (), R_OK | W_OK))	// checks availability based on permission bits
+                {
+                  if (!geteuid ())
+                    {
+                      struct group *p = getgrnam (TTY_GROUP);
+                      if (!p)
+                        p = getgrnam ("wheel");
+                      gid_t gid = p ? p->gr_gid : getgid ();
+
+                      chown (d->ttyName.data (), getuid (), gid);
+                      chmod (d->ttyName.data (), S_IRUSR | S_IWUSR | S_IWGRP);
+                    }
+                  goto gotpty;
+                }
+              ::close (d->masterFd);
+              d->masterFd = -1;
+            }
+        }
+    }
+
+  //kWarning(175) << "Can't open a pseudo teletype";
+  return false;
+
+gotpty:
+  KDE_struct_stat st;
+  if (KDE_stat (d->ttyName.data (), &st))
+    return false;		// this just cannot happen ... *cough*  Yeah right, I just
+  // had it happen when pty #349 was allocated.  I guess
+  // there was some sort of leak?  I only had a few open.
+  if (((st.st_uid != getuid ()) ||
+       (st.st_mode & (S_IRGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH))) &&
+      !d->chownpty (true))
+    {
+
+      /*kWarning(175)
+         << "chownpty failed for device " << ptyName << "::" << d->ttyName
+         << "\nThis means the communication can be eavesdropped." << endl;
+       */
+    }
+
+grantedpt:
+
+#ifdef HAVE_REVOKE
+  revoke (d->ttyName.data ());
+#endif
+
+#ifdef HAVE_UNLOCKPT
+  unlockpt (d->masterFd);
+#elif defined(TIOCSPTLCK)
+  int flag = 0;
+  ioctl (d->masterFd, TIOCSPTLCK, &flag);
+#endif
+
+  d->slaveFd =::open (d->ttyName.data (), O_RDWR | O_NOCTTY);
+  if (d->slaveFd < 0)
+    {
+      //kWarning(175) << "Can't open slave pseudo teletype";
+      ::close (d->masterFd);
+      d->masterFd = -1;
+      return false;
+    }
+
+#if (defined(__svr4__) || defined(__sgi__) || defined(Q_OS_SOLARIS))
+  // Solaris uses STREAMS for terminal handling. It is possible
+  // for the pty handling modules to be left off the stream; in that
+  // case push them on. ioctl(fd, I_FIND, ...) is documented to return
+  // 1 if the module is on the stream already.
+  {
+    static const char *pt = "ptem";
+    static const char *ld = "ldterm";
+    if (ioctl (d->slaveFd, I_FIND, pt) == 0)
+      ioctl (d->slaveFd, I_PUSH, pt);
+    if (ioctl (d->slaveFd, I_FIND, ld) == 0)
+      ioctl (d->slaveFd, I_PUSH, ld);
+  }
+#endif
+
+#endif /* HAVE_OPENPTY */
+
   fcntl (d->masterFd, F_SETFD, FD_CLOEXEC);
   fcntl (d->slaveFd, F_SETFD, FD_CLOEXEC);
 
@@ -126,25 +406,38 @@
 bool
 KPty::open (int fd)
 {
+#if !defined(HAVE_PTSNAME) && !defined(TIOCGPTN)
+  //kWarning(175) << "Unsupported attempt to open pty with fd" << fd;
+  return false;
+#else
   Q_D (KPty);
 
   if (d->masterFd >= 0)
     {
+      //kWarning(175) << "Attempting to open an already open pty";
       return false;
     }
 
   d->ownMaster = false;
 
+#ifdef HAVE_PTSNAME
+  char *ptsn = ptsname (fd);
+  if (ptsn)
+    {
+      d->ttyName = ptsn;
+#else
   int ptyno;
   if (!ioctl (fd, TIOCGPTN, &ptyno))
     {
       char buf[32];
       sprintf (buf, "/dev/pts/%d", ptyno);
       d->ttyName = buf;
+#endif
     }
   else
     {
-       return false;
+      //kWarning(175) << "Failed to determine pty slave device for fd" << fd;
+      return false;
     }
 
   d->masterFd = fd;
@@ -155,6 +448,7 @@
     }
 
   return true;
+#endif
 }
 
 void
@@ -177,11 +471,13 @@
     return true;
   if (d->masterFd < 0)
     {
+      //kWarning(175) << "Attempting to open pty slave while master is closed";
       return false;
     }
   d->slaveFd =::open (d->ttyName.data (), O_RDWR | O_NOCTTY);
   if (d->slaveFd < 0)
     {
+      //kWarning(175) << "Can't open slave pseudo teletype";
       return false;
     }
   fcntl (d->slaveFd, F_SETFD, FD_CLOEXEC);
@@ -198,6 +494,29 @@
   closeSlave ();
   if (d->ownMaster)
     {
+#ifndef HAVE_OPENPTY
+      // don't bother resetting unix98 pty, it will go away after closing master anyway.
+      if (memcmp (d->ttyName.data (), "/dev/pts/", 9))
+        {
+          if (!geteuid ())
+            {
+              struct stat st;
+              if (!stat (d->ttyName.data (), &st))
+                {
+                  chown (d->ttyName.data (), 0,
+                         st.st_gid == getgid ()? 0 : -1);
+                  chmod (d->ttyName.data (),
+                         S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH |
+                         S_IWOTH);
+                }
+            }
+          else
+            {
+              fcntl (d->masterFd, F_SETFD, 0);
+              d->chownpty (false);
+            }
+        }
+#endif
       ::close (d->masterFd);
     }
   d->masterFd = -1;
@@ -215,18 +534,36 @@
   setsid ();
 
   // make our slave pty the new controlling terminal.
+#ifdef TIOCSCTTY
   ioctl (d->slaveFd, TIOCSCTTY, 0);
+#else
+  // __svr4__ hack: the first tty opened after setsid() becomes controlling tty
+  ::close (open (d->ttyName, O_WRONLY, 0));
+#endif
 
   // make our new process group the foreground group on the pty
   int pgrp = getpid ();
+#if defined(_POSIX_VERSION) || defined(__svr4__)
   tcsetpgrp (d->slaveFd, pgrp);
+#elif defined(TIOCSPGRP)
+  ioctl (d->slaveFd, TIOCSPGRP, (char *) &pgrp);
+#endif
 }
 
 void
 KPty::login (const char *user, const char *remotehost)
 {
+#ifdef HAVE_UTEMPTER
+  Q_D (KPty);
+
+  addToUtmp (d->ttyName, remotehost, d->masterFd);
+  Q_UNUSED (user);
+#else
+#ifdef HAVE_UTMPX
+  struct utmpx l_struct;
+#else
   struct utmp l_struct;
-
+#endif
   memset (&l_struct, 0, sizeof (l_struct));
   // note: strncpy without terminators _is_ correct here. man 4 utmp
 
@@ -236,51 +573,146 @@
   if (remotehost)
     {
       strncpy (l_struct.ut_host, remotehost, sizeof (l_struct.ut_host));
+#ifdef HAVE_STRUCT_UTMP_UT_SYSLEN
+      l_struct.ut_syslen =
+        qMin (strlen (remotehost), sizeof (l_struct.ut_host));
+#endif
     }
 
+#ifndef __GLIBC__
+  Q_D (KPty);
+  const char *str_ptr = d->ttyName.data ();
+  if (!memcmp (str_ptr, "/dev/", 5))
+    str_ptr += 5;
+  strncpy (l_struct.ut_line, str_ptr, sizeof (l_struct.ut_line));
+#ifdef HAVE_STRUCT_UTMP_UT_ID
+  strncpy (l_struct.ut_id,
+           str_ptr + strlen (str_ptr) - sizeof (l_struct.ut_id),
+           sizeof (l_struct.ut_id));
+#endif
+#endif
+
+#ifdef HAVE_UTMPX
+  //gettimeofday(&l_struct.ut_tv, 0);
+  gettimeofday ((struct timeval *) &l_struct.ut_tv, 0);
+#else
   l_struct.ut_time = time (0);
+#endif
+
+#ifdef HAVE_LOGIN
+#ifdef HAVE_LOGINX
+  ::loginx (&l_struct);
+#else
+  ::login (&l_struct);
+#endif
+#else
+#ifdef HAVE_STRUCT_UTMP_UT_TYPE
+  l_struct.ut_type = USER_PROCESS;
+#endif
+#ifdef HAVE_STRUCT_UTMP_UT_PID
+  l_struct.ut_pid = getpid ();
+#ifdef HAVE_STRUCT_UTMP_UT_SESSION
+  l_struct.ut_session = getsid (0);
+#endif
+#endif
+#ifdef HAVE_UTMPX
+  utmpxname (_PATH_UTMPX);
+  setutxent ();
+  pututxline (&l_struct);
+  endutxent ();
+  //updwtmpx(_PATH_WTMPX, &l_struct);
+#else
   utmpname (_PATH_UTMP);
   setutent ();
   pututline (&l_struct);
   endutent ();
   updwtmp (_PATH_WTMP, &l_struct);
+#endif
+#endif
+#endif
 }
 
 void
 KPty::logout ()
 {
+#ifdef HAVE_UTEMPTER
+  Q_D (KPty);
+
+  removeLineFromUtmp (d->ttyName, d->masterFd);
+#else
   Q_D (KPty);
 
   const char *str_ptr = d->ttyName.data ();
   if (!memcmp (str_ptr, "/dev/", 5))
     str_ptr += 5;
+#ifdef __GLIBC__
   else
     {
       const char *sl_ptr = strrchr (str_ptr, '/');
       if (sl_ptr)
-	str_ptr = sl_ptr + 1;
+        str_ptr = sl_ptr + 1;
     }
-
+#endif
+#ifdef HAVE_LOGIN
+#ifdef HAVE_LOGINX
+  ::logoutx (str_ptr, 0, DEAD_PROCESS);
+#else
+  ::logout (str_ptr);
+#endif
+#else
+#ifdef HAVE_UTMPX
+  struct utmpx l_struct, *ut;
+#else
   struct utmp l_struct, *ut;
-
+#endif
   memset (&l_struct, 0, sizeof (l_struct));
+
   strncpy (l_struct.ut_line, str_ptr, sizeof (l_struct.ut_line));
+
+#ifdef HAVE_UTMPX
+  utmpxname (_PATH_UTMPX);
+  setutxent ();
+  if ((ut = getutxline (&l_struct)))
+    {
+#else
   utmpname (_PATH_UTMP);
   setutent ();
   if ((ut = getutline (&l_struct)))
     {
+#endif
       memset (ut->ut_name, 0, sizeof (*ut->ut_name));
       memset (ut->ut_host, 0, sizeof (*ut->ut_host));
+#ifdef HAVE_STRUCT_UTMP_UT_SYSLEN
+      ut->ut_syslen = 0;
+#endif
+#ifdef HAVE_STRUCT_UTMP_UT_TYPE
+      ut->ut_type = DEAD_PROCESS;
+#endif
+#ifdef HAVE_UTMPX
+      //gettimeofday(&(ut->ut_tv), 0);
+      gettimeofday ((struct timeval *) &(ut->ut_tv), 0);
+      pututxline (ut);
+    }
+  endutxent ();
+#else
       ut->ut_time = time (0);
       pututline (ut);
     }
   endutent ();
+#endif
+#endif
+#endif
 }
 
 bool
 KPty::tcGetAttr (struct::termios * ttmode) const
 {
   Q_D (const KPty);
+
+#ifdef Q_OS_SOLARIS
+  if (_tcgetattr (d->slaveFd, ttmode) == 0)
+    return true;
+#endif
   return _tcgetattr (d->masterFd, ttmode) == 0;
 }
 
@@ -288,6 +720,11 @@
 KPty::tcSetAttr (struct::termios * ttmode)
 {
   Q_D (KPty);
+
+#ifdef Q_OS_SOLARIS
+  if (_tcsetattr (d->slaveFd, ttmode) == 0)
+    return true;
+#endif
   return _tcsetattr (d->masterFd, ttmode) == 0;
 }