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

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.9.1
This website is maintained by Timothy Pearson.