• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • tdecore
 

tdecore

  • tdecore
kpty.cpp
1 /*
2 
3  This file is part of the KDE libraries
4  Copyright (C) 1997-2002 The Konsole Developers
5  Copyright (C) 2002 Waldo Bastian <bastian@kde.org>
6  Copyright (C) 2002-2003 Oswald Buddenhagen <ossi@kde.org>
7 
8  This library is free software; you can redistribute it and/or
9  modify it under the terms of the GNU Library General Public
10  License as published by the Free Software Foundation; either
11  version 2 of the License, or (at your option) any later version.
12 
13  This library is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  Library General Public License for more details.
17 
18  You should have received a copy of the GNU Library General Public License
19  along with this library; see the file COPYING.LIB. If not, write to
20  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  Boston, MA 02110-1301, USA.
22 */
23 
24 #include <config.h>
25 
26 #include "kpty.h"
27 #include "kprocess.h"
28 
29 #ifdef __sgi
30 #define __svr4__
31 #endif
32 
33 #ifdef __osf__
34 #define _OSF_SOURCE
35 #include <float.h>
36 #endif
37 
38 #ifdef _AIX
39 #define _ALL_SOURCE
40 #endif
41 
42 // __USE_XOPEN isn't defined by default in ICC
43 // (needed for ptsname(), grantpt() and unlockpt())
44 #ifdef __INTEL_COMPILER
45 # ifndef __USE_XOPEN
46 # define __USE_XOPEN
47 # endif
48 #endif
49 
50 #include <sys/types.h>
51 #include <sys/ioctl.h>
52 #include <sys/time.h>
53 #include <sys/resource.h>
54 #include <sys/stat.h>
55 #include <sys/param.h>
56 
57 #ifdef HAVE_SYS_STROPTS_H
58 # include <sys/stropts.h> // Defines I_PUSH
59 # define _NEW_TTY_CTRL
60 #endif
61 
62 #include <errno.h>
63 #include <fcntl.h>
64 #include <time.h>
65 #include <stdlib.h>
66 #include <stdio.h>
67 #include <string.h>
68 #include <unistd.h>
69 #include <grp.h>
70 
71 #if defined(HAVE_LIBUTIL_H)
72 # include <libutil.h>
73 # if (!defined(__FreeBSD__) || __FreeBSD_version < 900007)
74 # define USE_LOGIN
75 # endif
76 #endif
77 #if defined(HAVE_UTIL_H)
78 # include <util.h>
79 # define USE_LOGIN
80 #endif
81 
82 #ifdef USE_LOGIN
83 # include <utmp.h>
84 #endif
85 
86 #ifdef HAVE_TERMIOS_H
87 /* for HP-UX (some versions) the extern C is needed, and for other
88  platforms it doesn't hurt */
89 extern "C" {
90 # include <termios.h>
91 }
92 #endif
93 
94 #if !defined(__osf__)
95 # ifdef HAVE_TERMIO_H
96 /* needed at least on AIX */
97 # include <termio.h>
98 # endif
99 #endif
100 
101 #if defined(HAVE_TCGETATTR)
102 # define _tcgetattr(fd, ttmode) tcgetattr(fd, ttmode)
103 #elif defined(TIOCGETA)
104 # define _tcgetattr(fd, ttmode) ioctl(fd, TIOCGETA, (char *)ttmode)
105 #elif defined(TCGETS)
106 # define _tcgetattr(fd, ttmode) ioctl(fd, TCGETS, (char *)ttmode)
107 #else
108 # error
109 #endif
110 
111 #if defined(HAVE_TCSETATTR) && defined(TCSANOW)
112 # define _tcsetattr(fd, ttmode) tcsetattr(fd, TCSANOW, ttmode)
113 #elif defined(TIOCSETA)
114 # define _tcsetattr(fd, ttmode) ioctl(fd, TIOCSETA, (char *)ttmode)
115 #elif defined(TCSETS)
116 # define _tcsetattr(fd, ttmode) ioctl(fd, TCSETS, (char *)ttmode)
117 #else
118 # error
119 #endif
120 
121 #if defined (_HPUX_SOURCE)
122 # define _TERMIOS_INCLUDED
123 # include <bsdtty.h>
124 #endif
125 
126 #if defined(HAVE_PTY_H)
127 # include <pty.h>
128 #endif
129 
130 #include <kdebug.h>
131 #include <kstandarddirs.h> // locate
132 
133 #ifndef CINTR
134 #define CINTR 0x03
135 #endif
136 #ifndef CQUIT
137 #define CQUIT 0x1c
138 #endif
139 #ifndef CERASE
140 #define CERASE 0x7f
141 #endif
142 
143 #define TTY_GROUP "tty"
144 
146 // private functions //
148 
149 #ifdef HAVE_UTEMPTER
150 class TDEProcess_Utmp : public TDEProcess
151 {
152 public:
153  int commSetupDoneC()
154  {
155  dup2(cmdFd, 0);
156  dup2(cmdFd, 1);
157  dup2(cmdFd, 3);
158  return 1;
159  }
160  int cmdFd;
161 };
162 #endif
163 
164 #define BASE_CHOWN "kgrantpty"
165 
166 
167 
169 // private data //
171 
172 struct KPtyPrivate {
173  KPtyPrivate() :
174  xonXoff(false),
175  utf8(false),
176  masterFd(-1), slaveFd(-1)
177  {
178  memset(&winSize, 0, sizeof(winSize));
179  winSize.ws_row = 24;
180  winSize.ws_col = 80;
181  }
182 
183  bool xonXoff : 1;
184  bool utf8 : 1;
185  int masterFd;
186  int slaveFd;
187  struct winsize winSize;
188 
189  TQCString ttyName;
190 };
191 
193 // public member functions //
195 
196 KPty::KPty()
197 {
198  d = new KPtyPrivate;
199 }
200 
201 KPty::~KPty()
202 {
203  close();
204  delete d;
205 }
206 
207 bool KPty::setPty(int pty_master)
208 {
209  // a pty is already open
210  if(d->masterFd >= 0) {
211  kdWarning(175) << "KPty::setPty(): " << "d->masterFd >= 0" << endl;
212  return false;
213  }
214  d->masterFd = pty_master;
215  return _attachPty(pty_master);
216 }
217 
218 bool KPty::_attachPty(int pty_master)
219 {
220  TQCString ptyName;
221 
222  kdDebug(175) << "KPty::_attachPty(): " << pty_master << endl;
223 #if defined(HAVE_PTSNAME) && defined(HAVE_GRANTPT)
224  char *ptsn = ptsname(d->masterFd);
225  if (ptsn) {
226  grantpt(d->masterFd);
227  d->ttyName = ptsn;
228  } else {
229  ::close(d->masterFd);
230  d->masterFd = -1;
231  }
232 #endif
233 
234  struct stat st;
235  if (stat(d->ttyName.data(), &st))
236  return false; // this just cannot happen ... *cough* Yeah right, I just
237  // had it happen when pty #349 was allocated. I guess
238  // there was some sort of leak? I only had a few open.
239  if (((st.st_uid != getuid()) ||
240  (st.st_mode & (S_IRGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH))) &&
241  !chownpty(true))
242  {
243  kdWarning(175)
244  << "KPty::_attachPty(): " << "chownpty failed for device " << ptyName << "::" << d->ttyName << endl
245  << "KPty::_attachPty(): " << "This means the communication can be eavesdropped." << endl;
246  }
247 
248 #ifdef BSD
249  revoke(d->ttyName.data());
250 #endif
251 
252 #ifdef HAVE_UNLOCKPT
253  unlockpt(d->masterFd);
254 #endif
255 
256  d->slaveFd = ::open(d->ttyName.data(), O_RDWR | O_NOCTTY);
257  if (d->slaveFd < 0)
258  {
259  kdWarning(175) << "KPty::_attachPty(): " << "Can't open slave pseudo teletype" << endl;
260  ::close(d->masterFd);
261  d->masterFd = -1;
262  return false;
263  }
264 
265 #if (defined(__svr4__) || defined(__sgi__))
266  // Solaris
267  ioctl(d->slaveFd, I_PUSH, "ptem");
268  ioctl(d->slaveFd, I_PUSH, "ldterm");
269 #endif
270 
271  // set xon/xoff & control keystrokes
272  // without the '::' some version of HP-UX thinks, this declares
273  // the struct in this class, in this method, and fails to find
274  // the correct tc[gs]etattr
275  struct ::termios ttmode;
276 
277  _tcgetattr(d->slaveFd, &ttmode);
278 
279  if (!d->xonXoff)
280  ttmode.c_iflag &= ~(IXOFF | IXON);
281  else
282  ttmode.c_iflag |= (IXOFF | IXON);
283 
284 #ifdef IUTF8
285  if (!d->utf8)
286  ttmode.c_iflag &= ~IUTF8;
287  else
288  ttmode.c_iflag |= IUTF8;
289 #endif
290 
291  ttmode.c_cc[VINTR] = CINTR;
292  ttmode.c_cc[VQUIT] = CQUIT;
293  ttmode.c_cc[VERASE] = CERASE;
294 
295  _tcsetattr(d->slaveFd, &ttmode);
296 
297  // set screen size
298  ioctl(d->slaveFd, TIOCSWINSZ, (char *)&d->winSize);
299 
300  fcntl(d->masterFd, F_SETFD, FD_CLOEXEC);
301  fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC);
302 
303  return true;
304 }
305 
306 bool KPty::open()
307 {
308  if (d->masterFd >= 0)
309  return true;
310 
311 #if defined(__OpenBSD__) || defined(__FreeBSD__)
312  char cpty[16];
313 
314  if (openpty(&d->masterFd, &d->slaveFd, cpty, NULL, &d->winSize) == 0) {
315  d->ttyName = cpty;
316  } else {
317  kdWarning(175) << "Can't open slave pseudo teletype" << endl;
318  return false;
319  }
320 #else
321 
322  TQCString ptyName;
323 
324  // Find a master pty that we can open ////////////////////////////////
325 
326  // Because not all the pty animals are created equal, they want to
327  // be opened by several different methods.
328 
329  // We try, as we know them, one by one.
330 
331 #if defined(HAVE_PTSNAME) && defined(HAVE_GRANTPT)
332 #ifdef _AIX
333  d->masterFd = ::open("/dev/ptc",O_RDWR);
334 #else
335  d->masterFd = ::open("/dev/ptmx",O_RDWR);
336 #endif
337  if (d->masterFd >= 0)
338  {
339  char *ptsn = ptsname(d->masterFd);
340  if (ptsn) {
341  grantpt(d->masterFd);
342  d->ttyName = ptsn;
343  goto gotpty;
344  } else {
345  ::close(d->masterFd);
346  d->masterFd = -1;
347  }
348  }
349 #endif
350 
351  // Linux device names, FIXME: Trouble on other systems?
352  for (const char* s3 = "pqrstuvwxyzabcdefghijklmno"; *s3; s3++)
353  {
354  for (const char* s4 = "0123456789abcdefghijklmnopqrstuvwxyz"; *s4; s4++)
355  {
356  ptyName.sprintf("/dev/pty%c%c", *s3, *s4);
357  d->ttyName.sprintf("/dev/tty%c%c", *s3, *s4);
358 
359  d->masterFd = ::open(ptyName.data(), O_RDWR);
360  if (d->masterFd >= 0)
361  {
362 #ifdef __sun
363  /* Need to check the process group of the pty.
364  * If it exists, then the slave pty is in use,
365  * and we need to get another one.
366  */
367  int pgrp_rtn;
368  if (ioctl(d->masterFd, TIOCGPGRP, &pgrp_rtn) == 0 || errno != EIO) {
369  ::close(d->masterFd);
370  d->masterFd = -1;
371  continue;
372  }
373 #endif /* sun */
374  if (!access(d->ttyName.data(),R_OK|W_OK)) // checks availability based on permission bits
375  {
376  if (!geteuid())
377  {
378  struct group* p = getgrnam(TTY_GROUP);
379  if (!p)
380  p = getgrnam("wheel");
381  gid_t gid = p ? p->gr_gid : getgid ();
382 
383  chown(d->ttyName.data(), getuid(), gid);
384  chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IWGRP);
385  }
386  goto gotpty;
387  }
388  ::close(d->masterFd);
389  d->masterFd = -1;
390  }
391  }
392  }
393 
394  kdWarning(175) << "KPty::open(): " << "Can't open a pseudo teletype" << endl;
395  return false;
396 #endif
397 
398  gotpty:
399  return _attachPty(d->masterFd);
400 
401  return true;
402 }
403 
404 void KPty::close()
405 {
406  if (d->masterFd < 0)
407  return;
408  // don't bother resetting unix98 pty, it will go away after closing master anyway.
409  if (memcmp(d->ttyName.data(), "/dev/pts/", 9)) {
410  if (!geteuid()) {
411  struct stat st;
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);
415  }
416  } else {
417  fcntl(d->masterFd, F_SETFD, 0);
418  chownpty(false);
419  }
420  }
421  ::close(d->slaveFd);
422  ::close(d->masterFd);
423  d->masterFd = d->slaveFd = -1;
424 }
425 
426 void KPty::setCTty()
427 {
428  // Setup job control //////////////////////////////////
429 
430  // Become session leader, process group leader,
431  // and get rid of the old controlling terminal.
432  setsid();
433 
434  // make our slave pty the new controlling terminal.
435 #ifdef TIOCSCTTY
436  ioctl(d->slaveFd, TIOCSCTTY, 0);
437 #else
438  // SVR4 hack: the first tty opened after setsid() becomes controlling tty
439  ::close(::open(d->ttyName, O_WRONLY, 0));
440 #endif
441 
442  // make our new process group the foreground group on the pty
443  int pgrp = getpid();
444 #if defined(_POSIX_VERSION) || defined(__svr4__)
445  tcsetpgrp (d->slaveFd, pgrp);
446 #elif defined(TIOCSPGRP)
447  ioctl(d->slaveFd, TIOCSPGRP, (char *)&pgrp);
448 #endif
449 }
450 
451 void KPty::login(const char *user, const char *remotehost)
452 {
453 #ifdef HAVE_UTEMPTER
454  TDEProcess_Utmp utmp;
455  utmp.cmdFd = d->masterFd;
456  utmp << UTEMPTER_HELPER << "add";
457  if (remotehost)
458  utmp << remotehost;
459  utmp.start(TDEProcess::Block);
460  Q_UNUSED(user);
461  Q_UNUSED(remotehost);
462 #elif defined(USE_LOGIN)
463  const char *str_ptr;
464  struct utmp l_struct;
465  memset(&l_struct, 0, sizeof(struct utmp));
466  // note: strncpy without terminators _is_ correct here. man 4 utmp
467 
468  if (user)
469  strncpy(l_struct.ut_name, user, UT_NAMESIZE);
470 
471  if (remotehost)
472  strncpy(l_struct.ut_host, remotehost, UT_HOSTSIZE);
473 
474 # ifndef __GLIBC__
475  str_ptr = d->ttyName.data();
476  if (!memcmp(str_ptr, "/dev/", 5))
477  str_ptr += 5;
478  strncpy(l_struct.ut_line, str_ptr, UT_LINESIZE);
479 # endif
480 
481  // Handle 64-bit time_t properly, where it may be larger
482  // than the integral type of ut_time.
483  {
484  time_t ut_time_temp;
485  time(&ut_time_temp);
486  l_struct.ut_time=ut_time_temp;
487  }
488 
489  ::login(&l_struct);
490 #else
491  Q_UNUSED(user);
492  Q_UNUSED(remotehost);
493 #endif
494 }
495 
496 void KPty::logout()
497 {
498 #ifdef HAVE_UTEMPTER
499  TDEProcess_Utmp utmp;
500  utmp.cmdFd = d->masterFd;
501  utmp << UTEMPTER_HELPER << "del";
502  utmp.start(TDEProcess::Block);
503 #elif defined(USE_LOGIN)
504  const char *str_ptr = d->ttyName.data();
505  if (!memcmp(str_ptr, "/dev/", 5))
506  str_ptr += 5;
507 # ifdef __GLIBC__
508  else {
509  const char *sl_ptr = strrchr(str_ptr, '/');
510  if (sl_ptr)
511  str_ptr = sl_ptr + 1;
512  }
513 # endif
514  ::logout(str_ptr);
515 #endif
516 }
517 
518 void KPty::setWinSize(int lines, int columns)
519 {
520  d->winSize.ws_row = (unsigned short)lines;
521  d->winSize.ws_col = (unsigned short)columns;
522  if (d->masterFd >= 0)
523  ioctl( d->masterFd, TIOCSWINSZ, (char *)&d->winSize );
524 }
525 
526 void KPty::setXonXoff(bool useXonXoff)
527 {
528  d->xonXoff = useXonXoff;
529  if (d->masterFd >= 0) {
530  // without the '::' some version of HP-UX thinks, this declares
531  // the struct in this class, in this method, and fails to find
532  // the correct tc[gs]etattr
533  struct ::termios ttmode;
534 
535  _tcgetattr(d->masterFd, &ttmode);
536 
537  if (!useXonXoff)
538  ttmode.c_iflag &= ~(IXOFF | IXON);
539  else
540  ttmode.c_iflag |= (IXOFF | IXON);
541 
542  _tcsetattr(d->masterFd, &ttmode);
543  }
544 }
545 
546 void KPty::setUtf8Mode(bool useUtf8)
547 {
548  d->utf8 = useUtf8;
549 #ifdef IUTF8
550  if (d->masterFd >= 0) {
551  // without the '::' some version of HP-UX thinks, this declares
552  // the struct in this class, in this method, and fails to find
553  // the correct tc[gs]etattr
554  struct ::termios ttmode;
555 
556  _tcgetattr(d->masterFd, &ttmode);
557 
558  if (!useUtf8)
559  ttmode.c_iflag &= ~IUTF8;
560  else
561  ttmode.c_iflag |= IUTF8;
562 
563  _tcsetattr(d->masterFd, &ttmode);
564  }
565 #endif
566 }
567 
568 const char *KPty::ttyName() const
569 {
570  return d->ttyName.data();
571 }
572 
573 int KPty::masterFd() const
574 {
575  return d->masterFd;
576 }
577 
578 int KPty::slaveFd() const
579 {
580  return d->slaveFd;
581 }
582 
583 // private
584 bool KPty::chownpty(bool grant)
585 {
586 #if !defined(__OpenBSD__) && !defined(__FreeBSD__)
587  TDEProcess proc;
588  proc << locate("exe", BASE_CHOWN) << (grant?"--grant":"--revoke") << TQString::number(d->masterFd);
589  return proc.start(TDEProcess::Block) && proc.normalExit() && !proc.exitStatus();
590 #endif
591 }
592 
KPty::ttyName
const char * ttyName() const
Definition: kpty.cpp:568
KPty::login
void login(const char *user=0, const char *remotehost=0)
Creates an utmp entry for the tty.
Definition: kpty.cpp:451
TDEProcess::start
virtual bool start(RunMode runmode=NotifyOnExit, Communication comm=NoCommunication)
Starts the process.
Definition: kprocess.cpp:298
KPty::open
bool open()
Create a pty master/slave pair.
Definition: kpty.cpp:306
TDEProcess::Block
The application is suspended until the started process is finished.
Definition: kprocess.h:182
KPty::setUtf8Mode
void setUtf8Mode(bool useUtf8)
Set the pty in utf8 mode on systems that support it.
Definition: kpty.cpp:546
KPty::~KPty
~KPty()
Destructor:
Definition: kpty.cpp:201
TDEProcess::exitStatus
int exitStatus() const
Returns the exit status of the process.
Definition: kprocess.cpp:616
KPty::setWinSize
void setWinSize(int lines, int columns)
Change the logical (screen) size of the pty.
Definition: kpty.cpp:518
TDEProcess::normalExit
bool normalExit() const
Checks whether the process exited cleanly.
Definition: kprocess.cpp:594
KPty::slaveFd
int slaveFd() const
Definition: kpty.cpp:578
KPty::masterFd
int masterFd() const
Definition: kpty.cpp:573
KPty::logout
void logout()
Removes the utmp entry for this tty.
Definition: kpty.cpp:496
KPty::KPty
KPty()
Constructor.
Definition: kpty.cpp:196
KPty::close
void close()
Close the pty master/slave pair.
Definition: kpty.cpp:404
TDEProcess
Child process invocation, monitoring and control.
Definition: kprocess.h:130
endl
kndbgstream & endl(kndbgstream &s)
Does nothing.
Definition: kdebug.h:583
KPty::setCTty
void setCTty()
Creates a new session and process group and makes this pty the controlling tty.
Definition: kpty.cpp:426
KPty::setXonXoff
void setXonXoff(bool useXonXoff)
Set whether the pty should honor Xon/Xoff flow control.
Definition: kpty.cpp:526
TDEProcess::commSetupDoneC
virtual int commSetupDoneC()
Called right after a (successful) fork(), but before an exec() on the child process&#39; side...
Definition: kprocess.cpp:987
KPty::setPty
bool setPty(int pty_master)
Attach a existing pty master.
Definition: kpty.cpp:207

tdecore

Skip menu "tdecore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

tdecore

Skip menu "tdecore"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdecore by doxygen 1.8.13
This website is maintained by Timothy Pearson.