GNU Octave  3.8.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 (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__)
120 # define _tcgetattr(fd, ttmode) ioctl(fd, TIOCGETA, (char *)ttmode)
121 #else
122 # if defined(_HPUX_SOURCE) || defined(__Lynx__) || defined (__CYGWIN__)
123 # define _tcgetattr(fd, ttmode) tcgetattr(fd, ttmode)
124 # else
125 # define _tcgetattr(fd, ttmode) ioctl(fd, TCGETS, (char *)ttmode)
126 # endif
127 #endif
128 
129 #if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__)
130 # define _tcsetattr(fd, ttmode) ioctl(fd, TIOCSETA, (char *)ttmode)
131 #else
132 # if defined(_HPUX_SOURCE) || defined(__CYGWIN__)
133 # define _tcsetattr(fd, ttmode) tcsetattr(fd, TCSANOW, ttmode)
134 # else
135 # define _tcsetattr(fd, ttmode) ioctl(fd, TCSETS, (char *)ttmode)
136 # endif
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 ///////////////////////
149 // private functions //
150 ///////////////////////
151 
152 //////////////////
153 // private data //
154 //////////////////
155 
157  masterFd(-1), slaveFd(-1), ownMaster(true), q_ptr(parent)
158 {
159 }
160 
161 KPtyPrivate::KPtyPrivate(KPty *parent, int _masterFd, int _slaveFd):
162  masterFd(_masterFd), slaveFd(_slaveFd), ownMaster(true), q_ptr(parent)
163 {
164 }
165 
166 
168 {
169 }
170 
171 #ifndef HAVE_OPENPTY
173 {
174 // return !QProcess::execute(KStandardDirs::findExe("kgrantpty"),
175 // QStringList() << (grant?"--grant":"--revoke") << QString::number(masterFd));
176  return true;
177 }
178 #endif
179 
180 /////////////////////////////
181 // public member functions //
182 /////////////////////////////
183 
185  d_ptr(new KPtyPrivate(this))
186 {
187 }
188 
189 KPty::KPty(int masterFd, int slaveFd) :
190  d_ptr(new KPtyPrivate(this, masterFd, slaveFd))
191 {
192 }
193 
195  d_ptr(d)
196 {
197  d_ptr->q_ptr = this;
198 }
199 
201 {
202  close();
203  delete d_ptr;
204 }
205 
207 {
208  Q_D(KPty);
209 
210  if (d->masterFd >= 0) {
211  return true;
212  }
213 
214  d->ownMaster = true;
215 
216  QByteArray ptyName;
217 
218  // Find a master pty that we can open ////////////////////////////////
219 
220  // Because not all the pty animals are created equal, they want to
221  // be opened by several different methods.
222 
223  // We try, as we know them, one by one.
224 
225 #ifdef HAVE_OPENPTY
226 
227  char ptsn[PATH_MAX];
228  if (::openpty( &d->masterFd, &d->slaveFd, ptsn, 0, 0))
229  {
230  d->masterFd = -1;
231  d->slaveFd = -1;
232  qWarning() << "Can't open a pseudo teletype";
233  return false;
234  }
235  d->ttyName = ptsn;
236 
237 #else
238 
239 #ifdef HAVE__GETPTY // irix
240 
241  char *ptsn = _getpty(&d->masterFd, O_RDWR|O_NOCTTY, S_IRUSR|S_IWUSR, 0);
242  if (ptsn) {
243  d->ttyName = ptsn;
244  goto grantedpt;
245  }
246 
247 #elif defined(HAVE_PTSNAME) || defined(TIOCGPTN)
248 
249 #ifdef HAVE_POSIX_OPENPT
250  d->masterFd = ::posix_openpt(O_RDWR|O_NOCTTY);
251 #elif defined(HAVE_GETPT)
252  d->masterFd = ::getpt();
253 #elif defined(PTM_DEVICE)
254  d->masterFd = ::open(PTM_DEVICE, O_RDWR|O_NOCTTY);
255 #else
256 # error No method to open a PTY master detected.
257 #endif
258  if (d->masterFd >= 0)
259  {
260 #ifdef HAVE_PTSNAME
261  char *ptsn = ptsname(d->masterFd);
262  if (ptsn) {
263  d->ttyName = ptsn;
264 #else
265  int ptyno;
266  if (!ioctl(d->masterFd, TIOCGPTN, &ptyno)) {
267  char buf[32];
268  sprintf(buf, "/dev/pts/%d", ptyno);
269  d->ttyName = buf;
270 #endif
271 #ifdef HAVE_GRANTPT
272  if (!grantpt(d->masterFd))
273  goto grantedpt;
274 #else
275  goto gotpty;
276 #endif
277  }
278  ::close(d->masterFd);
279  d->masterFd = -1;
280  }
281 #endif // HAVE_PTSNAME || TIOCGPTN
282 
283  // Linux device names, FIXME: Trouble on other systems?
284  for (const char* s3 = "pqrstuvwxyzabcde"; *s3; s3++)
285  {
286  for (const char* s4 = "0123456789abcdef"; *s4; s4++)
287  {
288  ptyName = QString().sprintf("/dev/pty%c%c", *s3, *s4).toAscii();
289  d->ttyName = QString().sprintf("/dev/tty%c%c", *s3, *s4).toAscii();
290 
291  d->masterFd = ::open(ptyName.data(), O_RDWR);
292  if (d->masterFd >= 0)
293  {
294 #ifdef Q_OS_SOLARIS
295  /* Need to check the process group of the pty.
296  * If it exists, then the slave pty is in use,
297  * and we need to get another one.
298  */
299  int pgrp_rtn;
300  if (ioctl(d->masterFd, TIOCGPGRP, &pgrp_rtn) == 0 || errno != EIO) {
301  ::close(d->masterFd);
302  d->masterFd = -1;
303  continue;
304  }
305 #endif /* Q_OS_SOLARIS */
306  if (!access(d->ttyName.data(),R_OK|W_OK)) // checks availability based on permission bits
307  {
308  if (!geteuid())
309  {
310  struct group* p = getgrnam(TTY_GROUP);
311  if (!p)
312  p = getgrnam("wheel");
313  gid_t gid = p ? p->gr_gid : getgid ();
314 
315  if (!chown(d->ttyName.data(), getuid(), gid)) {
316  chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IWGRP);
317  }
318  }
319  goto gotpty;
320  }
321  ::close(d->masterFd);
322  d->masterFd = -1;
323  }
324  }
325  }
326 
327  qWarning() << "Can't open a pseudo teletype";
328  return false;
329 
330  gotpty:
331  struct stat st;
332  if (stat(d->ttyName.data(), &st))
333  return false; // this just cannot happen ... *cough* Yeah right, I just
334  // had it happen when pty #349 was allocated. I guess
335  // there was some sort of leak? I only had a few open.
336  if (((st.st_uid != getuid()) ||
337  (st.st_mode & (S_IRGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH))) &&
338  !d->chownpty(true))
339  {
340  qWarning()
341  << "chownpty failed for device " << ptyName << "::" << d->ttyName
342  << "\nThis means the communication can be eavesdropped." << endl;
343  }
344 
345 #if defined(HAVE_GRANTPT) || defined(HAVE__GETPTY)
346  grantedpt:
347 #endif
348 
349 #ifdef HAVE_REVOKE
350  revoke(d->ttyName.data());
351 #endif
352 
353 #ifdef HAVE_UNLOCKPT
354  unlockpt(d->masterFd);
355 #elif defined(TIOCSPTLCK)
356  int flag = 0;
357  ioctl(d->masterFd, TIOCSPTLCK, &flag);
358 #endif
359 
360  d->slaveFd = ::open(d->ttyName.data(), O_RDWR | O_NOCTTY);
361  if (d->slaveFd < 0)
362  {
363  qWarning() << "Can't open slave pseudo teletype";
364  ::close(d->masterFd);
365  d->masterFd = -1;
366  return false;
367  }
368 
369 #if (defined(__svr4__) || defined(__sgi__))
370  // Solaris
371  ioctl(d->slaveFd, I_PUSH, "ptem");
372  ioctl(d->slaveFd, I_PUSH, "ldterm");
373 #endif
374 
375 #endif /* HAVE_OPENPTY */
376  fcntl(d->masterFd, F_SETFD, FD_CLOEXEC);
377  fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC);
378 
379  struct ::termios t;
380  tcGetAttr(&t);
381  t.c_lflag &= ~ECHOCTL;
382  tcSetAttr(&t);
383  return true;
384 }
385 
387 {
388  Q_D(KPty);
389 
390  if (d->slaveFd < 0)
391  return;
392  ::close(d->slaveFd);
393  d->slaveFd = -1;
394 }
395 
397 {
398  Q_D(KPty);
399 
400  if (d->masterFd < 0)
401  return;
402  closeSlave();
403  if (d->ownMaster) {
404 #ifndef HAVE_OPENPTY
405  // don't bother resetting unix98 pty, it will go away after closing master anyway.
406  if (memcmp(d->ttyName.data(), "/dev/pts/", 9)) {
407  if (!geteuid()) {
408  struct stat st;
409  if (!stat(d->ttyName.data(), &st)) {
410  if (!chown(d->ttyName.data(), 0, st.st_gid == getgid() ? 0 : -1)) {
411  chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
412  }
413  }
414  } else {
415  fcntl(d->masterFd, F_SETFD, 0);
416  d->chownpty(false);
417  }
418  }
419  #endif
420  }
421  ::close(d->masterFd);
422  d->masterFd = -1;
423 }
424 
425 // XXX Supposedly, tc[gs]etattr do not work with the master on Solaris.
426 // Please verify.
427 
428 bool KPty::tcGetAttr(struct ::termios *ttmode) const
429 {
430  Q_D(const KPty);
431 
432  return _tcgetattr(d->masterFd, ttmode) == 0;
433 }
434 
435 bool KPty::tcSetAttr(struct ::termios *ttmode)
436 {
437  Q_D(KPty);
438 
439  return _tcsetattr(d->masterFd, ttmode) == 0;
440 }
441 
442 bool KPty::setWinSize(int lines, int columns)
443 {
444  Q_D(KPty);
445 
446  struct winsize winSize;
447  memset(&winSize, 0, sizeof(winSize));
448  winSize.ws_row = (unsigned short)lines;
449  winSize.ws_col = (unsigned short)columns;
450  return ioctl(d->masterFd, TIOCSWINSZ, (char *)&winSize) == 0;
451 }
452 
453 bool KPty::setEcho(bool echo)
454 {
455  struct ::termios ttmode;
456  if (!tcGetAttr(&ttmode))
457  return false;
458  if (!echo)
459  ttmode.c_lflag &= ~ECHO;
460  else
461  ttmode.c_lflag |= ECHO;
462  return tcSetAttr(&ttmode);
463 }
464 
465 const char *KPty::ttyName() const
466 {
467  Q_D(const KPty);
468 
469  return d->ttyName.data();
470 }
471 
472 int KPty::masterFd() const
473 {
474  Q_D(const KPty);
475 
476  return d->masterFd;
477 }
478 
479 int KPty::slaveFd() const
480 {
481  Q_D(const KPty);
482 
483  return d->slaveFd;
484 }