• 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  if (d->slaveFd < 0 ) {
221 
222  kdDebug(175) << "KPty::_attachPty(): " << pty_master << endl;
223 #if defined(HAVE_PTSNAME)
224  char *ptsn = ptsname(d->masterFd);
225  if (ptsn) {
226  d->ttyName = ptsn;
227  } else {
228  ::close(d->masterFd);
229  d->masterFd = -1;
230  return false;
231  }
232 #endif
233 
234 #if defined(HAVE_GRANTPT)
235  if (grantpt(d->masterFd)) {
236  return false;
237  }
238 #else
239  struct stat st;
240  if (stat(d->ttyName.data(), &st))
241  return false; // this just cannot happen ... *cough* Yeah right, I just
242  // had it happen when pty #349 was allocated. I guess
243  // there was some sort of leak? I only had a few open.
244  if (((st.st_uid != getuid()) ||
245  (st.st_mode & (S_IRGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH))) &&
246  !chownpty(true))
247  {
248  kdWarning(175)
249  << "KPty::_attachPty(): " << "chownpty failed for device " << d->ttyName << endl
250  << "KPty::_attachPty(): " << "This means the communication can be eavesdropped." << endl;
251  }
252 #endif
253 
254 #ifdef BSD
255  revoke(d->ttyName.data());
256 #endif
257 
258 #ifdef HAVE_UNLOCKPT
259  unlockpt(d->masterFd);
260 #endif
261 
262  d->slaveFd = ::open(d->ttyName.data(), O_RDWR | O_NOCTTY);
263  if (d->slaveFd < 0)
264  {
265  kdWarning(175) << "KPty::_attachPty(): " << "Can't open slave pseudo teletype" << endl;
266  ::close(d->masterFd);
267  d->masterFd = -1;
268  return false;
269  }
270 #ifdef HAVE_OPENPTY
271  // set screen size
272  ioctl(d->slaveFd, TIOCSWINSZ, (char *)&d->winSize);
273 #endif
274  }
275 
276 #if (defined(__svr4__) || defined(__sgi__))
277  // Solaris
278  ioctl(d->slaveFd, I_PUSH, "ptem");
279  ioctl(d->slaveFd, I_PUSH, "ldterm");
280 #endif
281 
282  // set xon/xoff & control keystrokes
283  // without the '::' some version of HP-UX thinks, this declares
284  // the struct in this class, in this method, and fails to find
285  // the correct tc[gs]etattr
286  struct ::termios ttmode;
287 
288  _tcgetattr(d->slaveFd, &ttmode);
289 
290  if (!d->xonXoff)
291  ttmode.c_iflag &= ~(IXOFF | IXON);
292  else
293  ttmode.c_iflag |= (IXOFF | IXON);
294 
295 #ifdef IUTF8
296  if (!d->utf8)
297  ttmode.c_iflag &= ~IUTF8;
298  else
299  ttmode.c_iflag |= IUTF8;
300 #endif
301 
302  ttmode.c_cc[VINTR] = CINTR;
303  ttmode.c_cc[VQUIT] = CQUIT;
304  ttmode.c_cc[VERASE] = CERASE;
305 
306  _tcsetattr(d->slaveFd, &ttmode);
307 
308 #ifndef HAVE_OPENPTY
309  // set screen size
310  ioctl(d->slaveFd, TIOCSWINSZ, (char *)&d->winSize);
311 #endif
312 
313  fcntl(d->masterFd, F_SETFD, FD_CLOEXEC);
314  fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC);
315 
316  return true;
317 }
318 
319 bool KPty::open()
320 {
321  if (d->masterFd >= 0)
322  return true;
323 
324 #if defined(HAVE_OPENPTY)
325  char cpty[16];
326 
327  if (openpty(&d->masterFd, &d->slaveFd, cpty, NULL, &d->winSize) == 0) {
328  d->ttyName = cpty;
329  } else {
330  kdWarning(175) << "Can't open slave pseudo teletype" << endl;
331  return false;
332  }
333 #else
334 
335  TQCString ptyName;
336 
337  // Find a master pty that we can open ////////////////////////////////
338 
339  // Because not all the pty animals are created equal, they want to
340  // be opened by several different methods.
341 
342  // We try, as we know them, one by one.
343 
344 #if defined(HAVE_PTSNAME) && defined(HAVE_GRANTPT)
345 #if defined(HAVE_GETPT)
346  d->masterFd = ::getpt();
347 #elif defined(HAVE_POSIX_OPENPT)
348  d->masterFd = ::posix_openpt(O_RDWR);
349 #elif defined(_AIX)
350  d->masterFd = ::open("/dev/ptc",O_RDWR);
351 #else
352  d->masterFd = ::open("/dev/ptmx",O_RDWR);
353 #endif
354  if (d->masterFd >= 0)
355  {
356  char *ptsn = ptsname(d->masterFd);
357  if (ptsn) {
358  grantpt(d->masterFd);
359  d->ttyName = ptsn;
360  goto gotpty;
361  } else {
362  ::close(d->masterFd);
363  d->masterFd = -1;
364  }
365  }
366 #endif
367 
368  // Linux device names, FIXME: Trouble on other systems?
369  for (const char* s3 = "pqrstuvwxyzabcdefghijklmno"; *s3; s3++)
370  {
371  for (const char* s4 = "0123456789abcdefghijklmnopqrstuvwxyz"; *s4; s4++)
372  {
373  ptyName.sprintf("/dev/pty%c%c", *s3, *s4);
374  d->ttyName.sprintf("/dev/tty%c%c", *s3, *s4);
375 
376  d->masterFd = ::open(ptyName.data(), O_RDWR);
377  if (d->masterFd >= 0)
378  {
379 #ifdef __sun
380  /* Need to check the process group of the pty.
381  * If it exists, then the slave pty is in use,
382  * and we need to get another one.
383  */
384  int pgrp_rtn;
385  if (ioctl(d->masterFd, TIOCGPGRP, &pgrp_rtn) == 0 || errno != EIO) {
386  ::close(d->masterFd);
387  d->masterFd = -1;
388  continue;
389  }
390 #endif /* sun */
391  if (!access(d->ttyName.data(),R_OK|W_OK)) // checks availability based on permission bits
392  {
393  if (!geteuid())
394  {
395  struct group* p = getgrnam(TTY_GROUP);
396  if (!p)
397  p = getgrnam("wheel");
398  gid_t gid = p ? p->gr_gid : getgid ();
399 
400  chown(d->ttyName.data(), getuid(), gid);
401  chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IWGRP);
402  }
403  goto gotpty;
404  }
405  ::close(d->masterFd);
406  d->masterFd = -1;
407  }
408  }
409  }
410 
411  kdWarning(175) << "KPty::open(): " << "Can't open a pseudo teletype" << endl;
412  return false;
413 #endif
414 
415  gotpty:
416  return _attachPty(d->masterFd);
417 
418  return true;
419 }
420 
421 void KPty::close()
422 {
423  if (d->masterFd < 0)
424  return;
425  // don't bother resetting unix98 pty, it will go away after closing master anyway.
426  if (memcmp(d->ttyName.data(), "/dev/pts/", 9)) {
427  if (!geteuid()) {
428  struct stat st;
429  if (!stat(d->ttyName.data(), &st)) {
430  chown(d->ttyName.data(), 0, st.st_gid == getgid() ? 0 : -1);
431  chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
432  }
433  } else {
434  fcntl(d->masterFd, F_SETFD, 0);
435  chownpty(false);
436  }
437  }
438  ::close(d->slaveFd);
439  ::close(d->masterFd);
440  d->masterFd = d->slaveFd = -1;
441 }
442 
443 void KPty::setCTty()
444 {
445  // Setup job control //////////////////////////////////
446 
447  // Become session leader, process group leader,
448  // and get rid of the old controlling terminal.
449  setsid();
450 
451  // make our slave pty the new controlling terminal.
452 #ifdef TIOCSCTTY
453  ioctl(d->slaveFd, TIOCSCTTY, 0);
454 #else
455  // SVR4 hack: the first tty opened after setsid() becomes controlling tty
456  ::close(::open(d->ttyName, O_WRONLY, 0));
457 #endif
458 
459  // make our new process group the foreground group on the pty
460  int pgrp = getpid();
461 #if defined(_POSIX_VERSION) || defined(__svr4__)
462  tcsetpgrp (d->slaveFd, pgrp);
463 #elif defined(TIOCSPGRP)
464  ioctl(d->slaveFd, TIOCSPGRP, (char *)&pgrp);
465 #endif
466 }
467 
468 void KPty::login(const char *user, const char *remotehost)
469 {
470 #ifdef HAVE_UTEMPTER
471  TDEProcess_Utmp utmp;
472  utmp.cmdFd = d->masterFd;
473  utmp << UTEMPTER_HELPER << "add";
474  if (remotehost)
475  utmp << remotehost;
476  utmp.start(TDEProcess::Block);
477  Q_UNUSED(user);
478  Q_UNUSED(remotehost);
479 #elif defined(USE_LOGIN)
480  const char *str_ptr;
481  struct utmp l_struct;
482  memset(&l_struct, 0, sizeof(struct utmp));
483  // note: strncpy without terminators _is_ correct here. man 4 utmp
484 
485  if (user)
486  strncpy(l_struct.ut_name, user, UT_NAMESIZE);
487 
488  if (remotehost)
489  strncpy(l_struct.ut_host, remotehost, UT_HOSTSIZE);
490 
491 # ifndef __GLIBC__
492  str_ptr = d->ttyName.data();
493  if (!memcmp(str_ptr, "/dev/", 5))
494  str_ptr += 5;
495  strncpy(l_struct.ut_line, str_ptr, UT_LINESIZE);
496 # endif
497 
498  // Handle 64-bit time_t properly, where it may be larger
499  // than the integral type of ut_time.
500  {
501  time_t ut_time_temp;
502  time(&ut_time_temp);
503  l_struct.ut_time=ut_time_temp;
504  }
505 
506  ::login(&l_struct);
507 #else
508  Q_UNUSED(user);
509  Q_UNUSED(remotehost);
510 #endif
511 }
512 
513 void KPty::logout()
514 {
515 #ifdef HAVE_UTEMPTER
516  TDEProcess_Utmp utmp;
517  utmp.cmdFd = d->masterFd;
518  utmp << UTEMPTER_HELPER << "del";
519  utmp.start(TDEProcess::Block);
520 #elif defined(USE_LOGIN)
521  const char *str_ptr = d->ttyName.data();
522  if (!memcmp(str_ptr, "/dev/", 5))
523  str_ptr += 5;
524 # ifdef __GLIBC__
525  else {
526  const char *sl_ptr = strrchr(str_ptr, '/');
527  if (sl_ptr)
528  str_ptr = sl_ptr + 1;
529  }
530 # endif
531  ::logout(str_ptr);
532 #endif
533 }
534 
535 void KPty::setWinSize(int lines, int columns)
536 {
537  d->winSize.ws_row = (unsigned short)lines;
538  d->winSize.ws_col = (unsigned short)columns;
539  if (d->masterFd >= 0)
540  ioctl( d->masterFd, TIOCSWINSZ, (char *)&d->winSize );
541 }
542 
543 void KPty::setXonXoff(bool useXonXoff)
544 {
545  d->xonXoff = useXonXoff;
546  if (d->masterFd >= 0) {
547  // without the '::' some version of HP-UX thinks, this declares
548  // the struct in this class, in this method, and fails to find
549  // the correct tc[gs]etattr
550  struct ::termios ttmode;
551 
552  _tcgetattr(d->masterFd, &ttmode);
553 
554  if (!useXonXoff)
555  ttmode.c_iflag &= ~(IXOFF | IXON);
556  else
557  ttmode.c_iflag |= (IXOFF | IXON);
558 
559  _tcsetattr(d->masterFd, &ttmode);
560  }
561 }
562 
563 void KPty::setUtf8Mode(bool useUtf8)
564 {
565  d->utf8 = useUtf8;
566 #ifdef IUTF8
567  if (d->masterFd >= 0) {
568  // without the '::' some version of HP-UX thinks, this declares
569  // the struct in this class, in this method, and fails to find
570  // the correct tc[gs]etattr
571  struct ::termios ttmode;
572 
573  _tcgetattr(d->masterFd, &ttmode);
574 
575  if (!useUtf8)
576  ttmode.c_iflag &= ~IUTF8;
577  else
578  ttmode.c_iflag |= IUTF8;
579 
580  _tcsetattr(d->masterFd, &ttmode);
581  }
582 #endif
583 }
584 
585 const char *KPty::ttyName() const
586 {
587  return d->ttyName.data();
588 }
589 
590 int KPty::masterFd() const
591 {
592  return d->masterFd;
593 }
594 
595 int KPty::slaveFd() const
596 {
597  return d->slaveFd;
598 }
599 
600 // private
601 bool KPty::chownpty(bool grant)
602 {
603 #if !defined(__OpenBSD__) && !defined(__FreeBSD__)
604  TDEProcess proc;
605  proc << locate("exe", BASE_CHOWN) << (grant?"--grant":"--revoke") << TQString::number(d->masterFd);
606  return proc.start(TDEProcess::Block) && proc.normalExit() && !proc.exitStatus();
607 #endif
608 }
609 
KPty::masterFd
int masterFd() const
Definition: kpty.cpp:590
KPty::ttyName
const char * ttyName() const
Definition: kpty.cpp:585
KPty::login
void login(const char *user=0, const char *remotehost=0)
Creates an utmp entry for the tty.
Definition: kpty.cpp:468
TDEProcess::normalExit
bool normalExit() const
Checks whether the process exited cleanly.
Definition: kprocess.cpp:594
TDEProcess::start
virtual bool start(RunMode runmode=NotifyOnExit, Communication comm=NoCommunication)
Starts the process.
Definition: kprocess.cpp:298
TDEProcess::exitStatus
int exitStatus() const
Returns the exit status of the process.
Definition: kprocess.cpp:616
KPty::open
bool open()
Create a pty master/slave pair.
Definition: kpty.cpp:319
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:563
KPty::~KPty
~KPty()
Destructor:
Definition: kpty.cpp:201
KPty::slaveFd
int slaveFd() const
Definition: kpty.cpp:595
KPty::setWinSize
void setWinSize(int lines, int columns)
Change the logical (screen) size of the pty.
Definition: kpty.cpp:535
KPty::logout
void logout()
Removes the utmp entry for this tty.
Definition: kpty.cpp:513
KPty::KPty
KPty()
Constructor.
Definition: kpty.cpp:196
KPty::close
void close()
Close the pty master/slave pair.
Definition: kpty.cpp:421
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:443
KPty::setXonXoff
void setXonXoff(bool useXonXoff)
Set whether the pty should honor Xon/Xoff flow control.
Definition: kpty.cpp:543
TDEProcess::commSetupDoneC
virtual int commSetupDoneC()
Called right after a (successful) fork(), but before an exec() on the child process' 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
  •     tdecore
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  • tdeioslave
  •   http
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdecore by doxygen 1.8.8
This website is maintained by Timothy Pearson.