GNU Octave  4.0.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
kpty.cpp
Go to the documentation of this file.
1 /*
2 
3  This file is part of the KDE libraries
4  Copyright (C) 2002, 2013 Waldo Bastian <bastian@kde.org>
5  Copyright (C) 2002-2003,2007 Oswald Buddenhagen <ossi@kde.org>
6 
7  Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
8 
9  This library is free software; you can redistribute it and/or
10  modify it under the terms of the GNU Library General Public
11  License as published by the Free Software Foundation; either
12  version 2 of the License, or (at your option) any later version.
13 
14  This library is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  Library General Public License for more details.
18 
19  You should have received a copy of the GNU Library General Public License
20  along with this library; see the file COPYING.LIB. If not, write to
21  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  Boston, MA 02110-1301, USA.
23 */
24 
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28 
29 #include "unix/kpty_p.h"
30 
31 #ifdef __sgi
32 #define __svr4__
33 #endif
34 
35 #ifdef __osf__
36 #define _OSF_SOURCE
37 #include <float.h>
38 #endif
39 
40 #ifdef _AIX
41 #define _ALL_SOURCE
42 #endif
43 
44 // __USE_XOPEN isn't defined by default in ICC
45 // (needed for ptsname(), grantpt() and unlockpt())
46 #ifdef __INTEL_COMPILER
47 # ifndef __USE_XOPEN
48 # define __USE_XOPEN
49 # endif
50 #endif
51 
52 #include <sys/types.h>
53 #include <sys/ioctl.h>
54 #include <sys/time.h>
55 #include <sys/resource.h>
56 #include <sys/stat.h>
57 #include <sys/param.h>
58 
59 #include <errno.h>
60 #include <fcntl.h>
61 #include <time.h>
62 #include <stdlib.h>
63 #include <stdio.h>
64 #include <string.h>
65 #include <unistd.h>
66 #include <grp.h>
67 
68 #ifdef Q_OS_MAC
69 # include <util.h>
70 #else
71 # if defined(HAVE_PTY_H)
72 # include <pty.h>
73 # endif
74 # ifdef HAVE_LIBUTIL_H
75 # include <libutil.h>
76 # elif defined(HAVE_UTIL_H)
77 # include <util.h>
78 # endif
79 #endif
80 
81 /*
82 #ifdef HAVE_UTEMPTER
83 extern "C" {
84 # include <utempter.h>
85 }
86 #else
87 # include <utmp.h>
88 # ifdef HAVE_UTMPX
89 # include <utmpx.h>
90 # endif
91 # if !defined(_PATH_UTMPX) && defined(_UTMPX_FILE)
92 # define _PATH_UTMPX _UTMPX_FILE
93 # endif
94 # if !defined(_PATH_WTMPX) && defined(_WTMPX_FILE)
95 # define _PATH_WTMPX _WTMPX_FILE
96 # endif
97 #endif
98 */
99 
100 /* for HP-UX (some versions) the extern C is needed, and for other
101  platforms it doesn't hurt */
102 extern "C" {
103 #include <termios.h>
104 #if defined(HAVE_TERMIO_H)
105 # include <termio.h> // struct winsize on some systems
106 #endif
107 }
108 
109 #if defined (_HPUX_SOURCE)
110 # define _TERMIOS_INCLUDED
111 # include <bsdtty.h>
112 #endif
113 
114 #ifdef HAVE_SYS_STROPTS_H
115 # include <sys/stropts.h> // Defines I_PUSH
116 # define _NEW_TTY_CTRL
117 #endif
118 
119 #if defined(HAVE_TCGETATTR)
120 # define _tcgetattr(fd, ttmode) tcgetattr(fd, ttmode)
121 #elif defined(TIOCGETA)
122 # define _tcgetattr(fd, ttmode) ioctl(fd, TIOCGETA, (char *)ttmode)
123 #elif defined(TCGETS)
124 # define _tcgetattr(fd, ttmode) ioctl(fd, TCGETS, (char *)ttmode)
125 #else
126 # error No method available to get terminal attributes
127 #endif
128 
129 #if defined(HAVE_TCSETATTR) && defined(TCSANOW)
130 # define _tcsetattr(fd, ttmode) tcsetattr(fd, TCSANOW, ttmode)
131 #elif defined(TIOCSETA)
132 # define _tcsetattr(fd, ttmode) ioctl(fd, TIOCSETA, (char *)ttmode)
133 #elif defined(TCSETS)
134 # define _tcsetattr(fd, ttmode) ioctl(fd, TCSETS, (char *)ttmode)
135 #else
136 # error No method available to set terminal attributes
137 #endif
138 
139 #include <QtCore>
140 
141 // not defined on HP-UX for example
142 #ifndef CTRL
143 # define CTRL(x) ((x) & 037)
144 #endif
145 
146 #define TTY_GROUP "tty"
147 
148 #ifndef PATH_MAX
149 # ifdef MAXPATHLEN
150 # define PATH_MAX MAXPATHLEN
151 # else
152 # define PATH_MAX 1024
153 # endif
154 #endif
155 
156 ///////////////////////
157 // private functions //
158 ///////////////////////
159 
160 //////////////////
161 // private data //
162 //////////////////
163 
165  masterFd(-1), slaveFd(-1), ownMaster(true), q_ptr(parent)
166 {
167 }
168 
169 KPtyPrivate::KPtyPrivate(KPty *parent, int _masterFd, int _slaveFd):
170  masterFd(_masterFd), slaveFd(_slaveFd), ownMaster(true), q_ptr(parent)
171 {
172 }
173 
174 
176 {
177 }
178 
179 #ifndef HAVE_OPENPTY
181 {
182 // return !QProcess::execute(KStandardDirs::findExe("kgrantpty"),
183 // QStringList() << (grant?"--grant":"--revoke") << QString::number(masterFd));
184  return true;
185 }
186 #endif
187 
188 /////////////////////////////
189 // public member functions //
190 /////////////////////////////
191 
193  d_ptr(new KPtyPrivate(this))
194 {
195 }
196 
197 KPty::KPty(int masterFd, int slaveFd) :
198  d_ptr(new KPtyPrivate(this, masterFd, slaveFd))
199 {
200 }
201 
203  d_ptr(d)
204 {
205  d_ptr->q_ptr = this;
206 }
207 
209 {
210  close();
211  delete d_ptr;
212 }
213 
215 {
216  Q_D(KPty);
217 
218  if (d->masterFd >= 0) {
219  return true;
220  }
221 
222  d->ownMaster = true;
223 
224  QByteArray ptyName;
225 
226  // Find a master pty that we can open ////////////////////////////////
227 
228  // Because not all the pty animals are created equal, they want to
229  // be opened by several different methods.
230 
231  // We try, as we know them, one by one.
232 
233 #ifdef HAVE_OPENPTY
234 
235  char ptsn[PATH_MAX];
236  if (::openpty( &d->masterFd, &d->slaveFd, ptsn, 0, 0))
237  {
238  d->masterFd = -1;
239  d->slaveFd = -1;
240  qWarning() << "Can't open a pseudo teletype";
241  return false;
242  }
243  d->ttyName = ptsn;
244 
245 #else
246 
247 #ifdef HAVE__GETPTY // irix
248 
249  char *ptsn = _getpty(&d->masterFd, O_RDWR|O_NOCTTY, S_IRUSR|S_IWUSR, 0);
250  if (ptsn) {
251  d->ttyName = ptsn;
252  goto grantedpt;
253  }
254 
255 #elif defined(HAVE_PTSNAME) || defined(TIOCGPTN)
256 
257 #ifdef HAVE_POSIX_OPENPT
258  d->masterFd = ::posix_openpt(O_RDWR|O_NOCTTY);
259 #elif defined(HAVE_GETPT)
260  d->masterFd = ::getpt();
261 #elif defined(PTM_DEVICE)
262  d->masterFd = ::open(PTM_DEVICE, O_RDWR|O_NOCTTY);
263 #else
264 # error No method to open a PTY master detected.
265 #endif
266  if (d->masterFd >= 0)
267  {
268 #ifdef HAVE_PTSNAME
269  char *ptsn = ptsname(d->masterFd);
270  if (ptsn) {
271  d->ttyName = ptsn;
272 #else
273  int ptyno;
274  if (!ioctl(d->masterFd, TIOCGPTN, &ptyno)) {
275  char buf[32];
276  sprintf(buf, "/dev/pts/%d", ptyno);
277  d->ttyName = buf;
278 #endif
279 #ifdef HAVE_GRANTPT
280  if (!grantpt(d->masterFd))
281  goto grantedpt;
282 #else
283  goto gotpty;
284 #endif
285  }
286  ::close(d->masterFd);
287  d->masterFd = -1;
288  }
289 #endif // HAVE_PTSNAME || TIOCGPTN
290 
291  // Linux device names, FIXME: Trouble on other systems?
292  for (const char* s3 = "pqrstuvwxyzabcde"; *s3; s3++)
293  {
294  for (const char* s4 = "0123456789abcdef"; *s4; s4++)
295  {
296  ptyName = QString().sprintf("/dev/pty%c%c", *s3, *s4).toAscii();
297  d->ttyName = QString().sprintf("/dev/tty%c%c", *s3, *s4).toAscii();
298 
299  d->masterFd = ::open(ptyName.data(), O_RDWR);
300  if (d->masterFd >= 0)
301  {
302 #ifdef Q_OS_SOLARIS
303  /* Need to check the process group of the pty.
304  * If it exists, then the slave pty is in use,
305  * and we need to get another one.
306  */
307  int pgrp_rtn;
308  if (ioctl(d->masterFd, TIOCGPGRP, &pgrp_rtn) == 0 || errno != EIO) {
309  ::close(d->masterFd);
310  d->masterFd = -1;
311  continue;
312  }
313 #endif /* Q_OS_SOLARIS */
314  if (!access(d->ttyName.data(),R_OK|W_OK)) // checks availability based on permission bits
315  {
316  if (!geteuid())
317  {
318  struct group* p = getgrnam(TTY_GROUP);
319  if (!p)
320  p = getgrnam("wheel");
321  gid_t gid = p ? p->gr_gid : getgid ();
322 
323  if (!chown(d->ttyName.data(), getuid(), gid)) {
324  chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IWGRP);
325  }
326  }
327  goto gotpty;
328  }
329  ::close(d->masterFd);
330  d->masterFd = -1;
331  }
332  }
333  }
334 
335  qWarning() << "Can't open a pseudo teletype";
336  return false;
337 
338  gotpty:
339  struct stat st;
340  if (stat(d->ttyName.data(), &st))
341  return false; // this just cannot happen ... *cough* Yeah right, I just
342  // had it happen when pty #349 was allocated. I guess
343  // there was some sort of leak? I only had a few open.
344  if (((st.st_uid != getuid()) ||
345  (st.st_mode & (S_IRGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH))) &&
346  !d->chownpty(true))
347  {
348  qWarning()
349  << "chownpty failed for device " << ptyName << "::" << d->ttyName
350  << "\nThis means the communication can be eavesdropped." << endl;
351  }
352 
353 #if defined(HAVE_GRANTPT) || defined(HAVE__GETPTY)
354  grantedpt:
355 #endif
356 
357 #ifdef HAVE_REVOKE
358  revoke(d->ttyName.data());
359 #endif
360 
361 #ifdef HAVE_UNLOCKPT
362  unlockpt(d->masterFd);
363 #elif defined(TIOCSPTLCK)
364  int flag = 0;
365  ioctl(d->masterFd, TIOCSPTLCK, &flag);
366 #endif
367 
368  d->slaveFd = ::open(d->ttyName.data(), O_RDWR | O_NOCTTY);
369  if (d->slaveFd < 0)
370  {
371  qWarning() << "Can't open slave pseudo teletype";
372  ::close(d->masterFd);
373  d->masterFd = -1;
374  return false;
375  }
376 
377 #if (defined(__svr4__) || defined(__sgi__))
378  // Solaris
379  ioctl(d->slaveFd, I_PUSH, "ptem");
380  ioctl(d->slaveFd, I_PUSH, "ldterm");
381 #endif
382 
383 #endif /* HAVE_OPENPTY */
384  fcntl(d->masterFd, F_SETFD, FD_CLOEXEC);
385  fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC);
386 
387  struct ::termios t;
388  tcGetAttr(&t);
389  t.c_iflag &= ~IXON;
390  t.c_lflag &= ~ECHOCTL;
391  tcSetAttr(&t);
392  return true;
393 }
394 
396 {
397  Q_D(KPty);
398 
399  if (d->slaveFd < 0)
400  return;
401  ::close(d->slaveFd);
402  d->slaveFd = -1;
403 }
404 
406 {
407  Q_D(KPty);
408 
409  if (d->masterFd < 0)
410  return;
411  closeSlave();
412  if (d->ownMaster) {
413 #ifndef HAVE_OPENPTY
414  // don't bother resetting unix98 pty, it will go away after closing master anyway.
415  if (memcmp(d->ttyName.data(), "/dev/pts/", 9)) {
416  if (!geteuid()) {
417  struct stat st;
418  if (!stat(d->ttyName.data(), &st)) {
419  if (!chown(d->ttyName.data(), 0, st.st_gid == getgid() ? 0 : -1)) {
420  chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
421  }
422  }
423  } else {
424  fcntl(d->masterFd, F_SETFD, 0);
425  d->chownpty(false);
426  }
427  }
428  #endif
429  }
430  ::close(d->masterFd);
431  d->masterFd = -1;
432 }
433 
434 // XXX Supposedly, tc[gs]etattr do not work with the master on Solaris.
435 // Please verify.
436 
437 bool KPty::tcGetAttr(struct ::termios *ttmode) const
438 {
439  Q_D(const KPty);
440 
441  return _tcgetattr(d->masterFd, ttmode) == 0;
442 }
443 
444 bool KPty::tcSetAttr(struct ::termios *ttmode)
445 {
446  Q_D(KPty);
447 
448  return _tcsetattr(d->masterFd, ttmode) == 0;
449 }
450 
451 bool KPty::setWinSize(int lines, int columns)
452 {
453  Q_D(KPty);
454 
455  struct winsize winSize;
456  memset(&winSize, 0, sizeof(winSize));
457  winSize.ws_row = (unsigned short)lines;
458  winSize.ws_col = (unsigned short)columns;
459  return ioctl(d->masterFd, TIOCSWINSZ, (char *)&winSize) == 0;
460 }
461 
462 bool KPty::setEcho(bool echo)
463 {
464  struct ::termios ttmode;
465  if (!tcGetAttr(&ttmode))
466  return false;
467  if (!echo)
468  ttmode.c_lflag &= ~ECHO;
469  else
470  ttmode.c_lflag |= ECHO;
471  return tcSetAttr(&ttmode);
472 }
473 
474 const char *KPty::ttyName() const
475 {
476  Q_D(const KPty);
477 
478  return d->ttyName.data();
479 }
480 
481 int KPty::masterFd() const
482 {
483  Q_D(const KPty);
484 
485  return d->masterFd;
486 }
487 
488 int KPty::slaveFd() const
489 {
490  Q_D(const KPty);
491 
492  return d->slaveFd;
493 }
int masterFd() const
Definition: kpty.cpp:481
bool chownpty(bool grant)
Definition: kpty.cpp:180
const char * ttyName() const
Definition: kpty.cpp:474
KPty * q_ptr
Definition: kpty_p.h:47
bool tcGetAttr(struct::termios *ttmode) const
Wrapper around tcgetattr(3).
Definition: kpty.cpp:437
void closeSlave()
Close the pty slave descriptor.
Definition: kpty.cpp:395
bool open()
Create a pty master/slave pair.
Definition: kpty.cpp:214
~KPty()
Destructor:
Definition: kpty.cpp:208
F77_RET_T const double const double double * d
int slaveFd() const
Definition: kpty.cpp:488
bool setWinSize(int lines, int columns)
Change the logical (screen) size of the pty.
Definition: kpty.cpp:451
#define PATH_MAX
Definition: kpty.cpp:152
#define ECHO
KPty()
Constructor.
Definition: kpty.cpp:192
void close()
Close the pty master/slave pair.
Definition: kpty.cpp:405
bool tcSetAttr(struct::termios *ttmode)
Wrapper around tcsetattr(3) with mode TCSANOW.
Definition: kpty.cpp:444
virtual ~KPtyPrivate()
Definition: kpty.cpp:175
subroutine stat(x, n, av, var, xmin, xmax)
Definition: tstgmn.for:111
KPtyPrivate(KPty *parent)
Definition: kpty.cpp:164
KPtyPrivate *const d_ptr
Definition: kpty.h:162
bool setEcho(bool echo)
Set whether the pty should echo input.
Definition: kpty.cpp:462
Provides primitives for opening & closing a pseudo TTY pair, assigning the controlling TTY...
Definition: kpty.h:35
#define TTY_GROUP
Definition: kpty.cpp:146