KPty

kpty.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 2002 Waldo Bastian <[email protected]>
4  SPDX-FileCopyrightText: 2002-2003, 2007-2008 Oswald Buddenhagen <[email protected]>
5  SPDX-FileCopyrightText: 2010 KDE e.V. <[email protected]>
6  SPDX-FileContributor: 2010 Adriaan de Groot <[email protected]>
7  SPDX-FileCopyrightText: 2022 Harald Sitter <[email protected]>
8 
9  SPDX-License-Identifier: LGPL-2.0-or-later
10 */
11 
12 #include "kpty_p.h"
13 
14 #include <QProcess>
15 #include <kpty_debug.h>
16 
17 // __USE_XOPEN isn't defined by default in ICC
18 // (needed for ptsname(), grantpt() and unlockpt())
19 #ifdef __INTEL_COMPILER
20 #ifndef __USE_XOPEN
21 #define __USE_XOPEN
22 #endif
23 #endif
24 
25 #include <sys/ioctl.h>
26 #include <sys/param.h>
27 #include <sys/resource.h>
28 #include <sys/stat.h>
29 #include <sys/time.h>
30 #include <sys/types.h>
31 
32 #include <cerrno>
33 #include <fcntl.h>
34 #include <grp.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <time.h>
39 #include <unistd.h>
40 
41 #if HAVE_PTY_H
42 #include <pty.h>
43 #endif
44 
45 #if HAVE_LIBUTIL_H
46 #include <libutil.h>
47 #elif HAVE_UTIL_H
48 #include <util.h>
49 #endif
50 
51 #ifdef UTEMPTER_PATH
52 // utempter uses 'add' and 'del' whereas ulog-helper uses 'login' and 'logout'
53 #ifndef UTEMPTER_ULOG
54 #define UTEMPTER_ADD "add"
55 #define UTEMPTER_DEL "del"
56 #else
57 #define UTEMPTER_ADD "login"
58 #define UTEMPTER_DEL "logout"
59 #endif
60 class UtemptProcess : public QProcess
61 {
62 public:
63 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
64  UtemptProcess()
65  {
66  setChildProcessModifier([this]() {
67  // These are the file descriptors the utempter helper wants
68  dup2(cmdFd, 0);
69  dup2(cmdFd, 1);
70  dup2(cmdFd, 3);
71  });
72  }
73 #else
74  void setupChildProcess() override
75  {
76  // These are the file descriptors the utempter helper wants
77  dup2(cmdFd, 0);
78  dup2(cmdFd, 1);
79  dup2(cmdFd, 3);
80  }
81 #endif
82 
83  int cmdFd;
84 };
85 #else
86 #include <utmp.h>
87 #if HAVE_UTMPX
88 #include <utmpx.h>
89 #endif
90 #if !defined(_PATH_UTMPX) && defined(_UTMPX_FILE)
91 #define _PATH_UTMPX _UTMPX_FILE
92 #endif
93 #if !defined(_PATH_WTMPX) && defined(_WTMPX_FILE)
94 #define _PATH_WTMPX _WTMPX_FILE
95 #endif
96 #endif
97 
98 /* for HP-UX (some versions) the extern C is needed, and for other
99  platforms it doesn't hurt */
100 extern "C" {
101 #include <termios.h>
102 #if HAVE_TERMIO_H
103 #include <termio.h> // struct winsize on some systems
104 #endif
105 }
106 
107 #if defined(_HPUX_SOURCE)
108 #define _TERMIOS_INCLUDED
109 #include <bsdtty.h>
110 #endif
111 
112 #if HAVE_SYS_STROPTS_H
113 #include <sys/stropts.h> // Defines I_PUSH
114 #define _NEW_TTY_CTRL
115 #endif
116 
117 #if HAVE_TCGETATTR
118 #define _tcgetattr(fd, ttmode) tcgetattr(fd, ttmode)
119 #elif 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 #define _tcgetattr(fd, ttmode) ioctl(fd, TCGETS, (char *)ttmode)
123 #endif
124 
125 #if HAVE_TCSETATTR
126 #define _tcsetattr(fd, ttmode) tcsetattr(fd, TCSANOW, ttmode)
127 #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__APPLE__) || defined(__DragonFly__)
128 #define _tcsetattr(fd, ttmode) ioctl(fd, TIOCSETA, (char *)ttmode)
129 #else
130 #define _tcsetattr(fd, ttmode) ioctl(fd, TCSETS, (char *)ttmode)
131 #endif
132 
133 #include <qplatformdefs.h>
134 
135 #define TTY_GROUP "tty"
136 
137 #ifndef PATH_MAX
138 #ifdef MAXPATHLEN
139 #define PATH_MAX MAXPATHLEN
140 #else
141 #define PATH_MAX 1024
142 #endif
143 #endif
144 
145 ///////////////////////
146 // private functions //
147 ///////////////////////
148 
149 //////////////////
150 // private data //
151 //////////////////
152 
153 KPtyPrivate::KPtyPrivate(KPty *parent)
154  : masterFd(-1)
155  , slaveFd(-1)
156  , ownMaster(true)
157  , q_ptr(parent)
158 {
159 #ifdef UTEMPTER_PATH
160  utempterPath = QStringLiteral(UTEMPTER_PATH);
161 #endif
162 }
163 
164 KPtyPrivate::~KPtyPrivate()
165 {
166 }
167 
168 #if !HAVE_OPENPTY
169 bool KPtyPrivate::chownpty(bool grant)
170 {
171  return !QProcess::execute(QFile::decodeName(CMAKE_INSTALL_PREFIX "/" KDE_INSTALL_LIBEXECDIR_KF "/kgrantpty"),
172  QStringList() << (grant ? "--grant" : "--revoke") << QString::number(masterFd));
173 }
174 #endif
175 
176 /////////////////////////////
177 // public member functions //
178 /////////////////////////////
179 
181  : d_ptr(new KPtyPrivate(this))
182 {
183 }
184 
185 KPty::KPty(KPtyPrivate *d)
186  : d_ptr(d)
187 {
188  d_ptr->q_ptr = this;
189 }
190 
192 {
193  close();
194 }
195 
197 {
198  Q_D(KPty);
199 
200  if (d->masterFd >= 0) {
201  return true;
202  }
203 
204  d->ownMaster = true;
205 
206  QByteArray ptyName;
207 
208  // Find a master pty that we can open ////////////////////////////////
209 
210  // Because not all the pty animals are created equal, they want to
211  // be opened by several different methods.
212 
213  // We try, as we know them, one by one.
214 
215 #if HAVE_OPENPTY
216 
217  char ptsn[PATH_MAX];
218  if (::openpty(&d->masterFd, &d->slaveFd, ptsn, nullptr, nullptr)) {
219  d->masterFd = -1;
220  d->slaveFd = -1;
221  qCWarning(KPTY_LOG) << "Can't open a pseudo teletype";
222  return false;
223  }
224  d->ttyName = ptsn;
225 
226 #else
227 
228 #if HAVE_PTSNAME || defined(TIOCGPTN)
229 
230 #if HAVE_POSIX_OPENPT
231  d->masterFd = ::posix_openpt(O_RDWR | O_NOCTTY);
232 #elif HAVE_GETPT
233  d->masterFd = ::getpt();
234 #elif defined(PTM_DEVICE)
235  d->masterFd = QT_OPEN(PTM_DEVICE, QT_OPEN_RDWR | O_NOCTTY);
236 #else
237 #error No method to open a PTY master detected.
238 #endif
239  if (d->masterFd >= 0) {
240 #if HAVE_PTSNAME
241  char *ptsn = ptsname(d->masterFd);
242  if (ptsn) {
243  d->ttyName = ptsn;
244 #else
245  int ptyno;
246  if (!ioctl(d->masterFd, TIOCGPTN, &ptyno)) {
247  char buf[32];
248  sprintf(buf, "/dev/pts/%d", ptyno);
249  d->ttyName = buf;
250 #endif
251 #if HAVE_GRANTPT
252  if (!grantpt(d->masterFd)) {
253  goto grantedpt;
254  }
255 #else
256  goto gotpty;
257 #endif
258  }
259  ::close(d->masterFd);
260  d->masterFd = -1;
261  }
262 #endif // HAVE_PTSNAME || TIOCGPTN
263 
264  // Linux device names, FIXME: Trouble on other systems?
265  for (const char *s3 = "pqrstuvwxyzabcde"; *s3; s3++) {
266  for (const char *s4 = "0123456789abcdef"; *s4; s4++) {
267  ptyName = QString().sprintf("/dev/pty%c%c", *s3, *s4).toLatin1();
268  d->ttyName = QString().sprintf("/dev/tty%c%c", *s3, *s4).toLatin1();
269 
270  d->masterFd = QT_OPEN(ptyName.data(), QT_OPEN_RDWR);
271  if (d->masterFd >= 0) {
272  if (!access(d->ttyName.data(), R_OK | W_OK)) { // checks availability based on permission bits
273  if (!geteuid()) {
274  struct group *p = getgrnam(TTY_GROUP);
275  if (!p) {
276  p = getgrnam("wheel");
277  }
278  gid_t gid = p ? p->gr_gid : getgid();
279 
280  chown(d->ttyName.data(), getuid(), gid);
281  chmod(d->ttyName.data(), S_IRUSR | S_IWUSR | S_IWGRP);
282  }
283  goto gotpty;
284  }
285  ::close(d->masterFd);
286  d->masterFd = -1;
287  }
288  }
289  }
290 
291  qCWarning(KPTY_LOG) << "Can't open a pseudo teletype";
292  return false;
293 
294 gotpty:
295  QFileInfo info(d->ttyName.data());
296  if (!info.exists()) {
297  return false; // this just cannot happen ... *cough* Yeah right, I just
298  }
299  // had it happen when pty #349 was allocated. I guess
300  // there was some sort of leak? I only had a few open.
302  if ((info.ownerId() != getuid() || (info.permissions() & perms)) //
303  && !d->chownpty(true)) {
304  qCWarning(KPTY_LOG) << "chownpty failed for device " << ptyName << "::" << d->ttyName << "\nThis means the communication can be eavesdropped." << endl;
305  }
306 
307 grantedpt:
308 
309 #ifdef HAVE_REVOKE
310  revoke(d->ttyName.data());
311 #endif
312 
313 #ifdef HAVE_UNLOCKPT
314  unlockpt(d->masterFd);
315 #elif defined(TIOCSPTLCK)
316  int flag = 0;
317  ioctl(d->masterFd, TIOCSPTLCK, &flag);
318 #endif
319 
320  d->slaveFd = QT_OPEN(d->ttyName.data(), QT_OPEN_RDWR | O_NOCTTY);
321  if (d->slaveFd < 0) {
322  qCWarning(KPTY_LOG) << "Can't open slave pseudo teletype";
323  ::close(d->masterFd);
324  d->masterFd = -1;
325  return false;
326  }
327 
328 #endif /* HAVE_OPENPTY */
329 
330  fcntl(d->masterFd, F_SETFD, FD_CLOEXEC);
331  fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC);
332 
333  return true;
334 }
335 
336 bool KPty::open(int fd)
337 {
338 #if !HAVE_PTSNAME && !defined(TIOCGPTN)
339  qCWarning(KPTY_LOG) << "Unsupported attempt to open pty with fd" << fd;
340  return false;
341 #else
342  Q_D(KPty);
343 
344  if (d->masterFd >= 0) {
345  qCWarning(KPTY_LOG) << "Attempting to open an already open pty";
346  return false;
347  }
348 
349  d->ownMaster = false;
350 
351 #if HAVE_PTSNAME
352  char *ptsn = ptsname(fd);
353  if (ptsn) {
354  d->ttyName = ptsn;
355 #else
356  int ptyno;
357  if (!ioctl(fd, TIOCGPTN, &ptyno)) {
358  char buf[32];
359  sprintf(buf, "/dev/pts/%d", ptyno);
360  d->ttyName = buf;
361 #endif
362  } else {
363  qCWarning(KPTY_LOG) << "Failed to determine pty slave device for fd" << fd;
364  return false;
365  }
366 
367  d->masterFd = fd;
368  if (!openSlave()) {
369  d->masterFd = -1;
370  return false;
371  }
372 
373  return true;
374 #endif
375 }
376 
378 {
379  Q_D(KPty);
380 
381  if (d->slaveFd < 0) {
382  return;
383  }
384  ::close(d->slaveFd);
385  d->slaveFd = -1;
386 }
387 
389 {
390  Q_D(KPty);
391 
392  if (d->slaveFd >= 0) {
393  return true;
394  }
395  if (d->masterFd < 0) {
396  qCWarning(KPTY_LOG) << "Attempting to open pty slave while master is closed";
397  return false;
398  }
399  d->slaveFd = QT_OPEN(d->ttyName.data(), QT_OPEN_RDWR | O_NOCTTY);
400  if (d->slaveFd < 0) {
401  qCWarning(KPTY_LOG) << "Can't open slave pseudo teletype";
402  return false;
403  }
404  fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC);
405  return true;
406 }
407 
409 {
410  Q_D(KPty);
411 
412  if (d->masterFd < 0) {
413  return;
414  }
415  closeSlave();
416  if (d->ownMaster) {
417 #if !HAVE_OPENPTY
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  d->chownpty(false);
429  }
430  }
431 #endif
432  ::close(d->masterFd);
433  }
434  d->masterFd = -1;
435 }
436 
438 {
439  Q_D(KPty);
440 
441  if (!d->withCTty) {
442  return;
443  }
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  ioctl(d->slaveFd, TIOCSCTTY, 0);
453 
454  // make our new process group the foreground group on the pty
455  int pgrp = getpid();
456  tcsetpgrp(d->slaveFd, pgrp);
457 }
458 
459 void KPty::login(const char *user, const char *remotehost)
460 {
461 #ifdef UTEMPTER_PATH
462  Q_D(KPty);
463 
464  Q_UNUSED(user);
465 
466  // Emulating libutempter version 1.1.6
467  if (!d->utempterPath.isEmpty()) {
468  UtemptProcess utemptProcess;
469  utemptProcess.cmdFd = d->masterFd;
470  utemptProcess.setProgram(d->utempterPath);
471  utemptProcess.setArguments(QStringList() << QStringLiteral(UTEMPTER_ADD) << QString::fromLocal8Bit(remotehost));
472  utemptProcess.setProcessChannelMode(QProcess::ForwardedChannels);
473  utemptProcess.start();
474  utemptProcess.waitForFinished();
475  }
476 
477 #else
478 #if HAVE_UTMPX
479  struct utmpx l_struct;
480 #else
481  struct utmp l_struct;
482 #endif
483  memset(&l_struct, 0, sizeof(l_struct));
484  // note: strncpy without terminators _is_ correct here. man 4 utmp
485 
486  if (user) {
487  strncpy(l_struct.ut_name, user, sizeof(l_struct.ut_name));
488  }
489 
490  if (remotehost) {
491  strncpy(l_struct.ut_host, remotehost, sizeof(l_struct.ut_host));
492 #if HAVE_STRUCT_UTMP_UT_SYSLEN
493  l_struct.ut_syslen = qMin(strlen(remotehost), sizeof(l_struct.ut_host));
494 #endif
495  }
496 
497 #ifndef __GLIBC__
498  Q_D(KPty);
499  const char *str_ptr = d->ttyName.data();
500  if (!memcmp(str_ptr, "/dev/", 5)) {
501  str_ptr += 5;
502  }
503  strncpy(l_struct.ut_line, str_ptr, sizeof(l_struct.ut_line));
504 #if HAVE_STRUCT_UTMP_UT_ID
505  strncpy(l_struct.ut_id, str_ptr + strlen(str_ptr) - sizeof(l_struct.ut_id), sizeof(l_struct.ut_id));
506 #endif
507 #endif
508 
509 #if HAVE_UTMPX
510  gettimeofday(&l_struct.ut_tv, 0);
511 #else
512  l_struct.ut_time = time(0);
513 #endif
514 
515 #if HAVE_LOGIN
516 #if HAVE_LOGINX
517  ::loginx(&l_struct);
518 #else
519  ::login(&l_struct);
520 #endif
521 #else
522 #if HAVE_STRUCT_UTMP_UT_TYPE
523  l_struct.ut_type = USER_PROCESS;
524 #endif
525 #if HAVE_STRUCT_UTMP_UT_PID
526  l_struct.ut_pid = getpid();
527 #if HAVE_STRUCT_UTMP_UT_SESSION
528  l_struct.ut_session = getsid(0);
529 #endif
530 #endif
531 #if HAVE_UTMPX
532  utmpxname(_PATH_UTMPX);
533  setutxent();
534  pututxline(&l_struct);
535  endutxent();
536  updwtmpx(_PATH_WTMPX, &l_struct);
537 #else
538  utmpname(_PATH_UTMP);
539  setutent();
540  pututline(&l_struct);
541  endutent();
542  updwtmp(_PATH_WTMP, &l_struct);
543 #endif
544 #endif
545 #endif
546 }
547 
549 {
550 #ifdef UTEMPTER_PATH
551  Q_D(KPty);
552 
553  // Emulating libutempter version 1.1.6
554  if (!d->utempterPath.isEmpty()) {
555  UtemptProcess utemptProcess;
556  utemptProcess.cmdFd = d->masterFd;
557  utemptProcess.setProgram(d->utempterPath);
558  utemptProcess.setArguments(QStringList(QStringLiteral(UTEMPTER_DEL)));
559  utemptProcess.setProcessChannelMode(QProcess::ForwardedChannels);
560  utemptProcess.start();
561  utemptProcess.waitForFinished();
562  }
563 
564 #else
565  Q_D(KPty);
566 
567  const char *str_ptr = d->ttyName.data();
568  if (!memcmp(str_ptr, "/dev/", 5)) {
569  str_ptr += 5;
570  }
571 #ifdef __GLIBC__
572  else {
573  const char *sl_ptr = strrchr(str_ptr, '/');
574  if (sl_ptr) {
575  str_ptr = sl_ptr + 1;
576  }
577  }
578 #endif
579 #if HAVE_LOGIN
580 #if HAVE_LOGINX
581  ::logoutx(str_ptr, 0, DEAD_PROCESS);
582 #else
583  ::logout(str_ptr);
584 #endif
585 #else
586 #if HAVE_UTMPX
587  struct utmpx l_struct, *ut;
588 #else
589  struct utmp l_struct, *ut;
590 #endif
591  memset(&l_struct, 0, sizeof(l_struct));
592 
593  strncpy(l_struct.ut_line, str_ptr, sizeof(l_struct.ut_line));
594 
595 #if HAVE_UTMPX
596  utmpxname(_PATH_UTMPX);
597  setutxent();
598  if ((ut = getutxline(&l_struct))) {
599 #else
600  utmpname(_PATH_UTMP);
601  setutent();
602  if ((ut = getutline(&l_struct))) {
603 #endif
604  memset(ut->ut_name, 0, sizeof(*ut->ut_name));
605  memset(ut->ut_host, 0, sizeof(*ut->ut_host));
606 #if HAVE_STRUCT_UTMP_UT_SYSLEN
607  ut->ut_syslen = 0;
608 #endif
609 #if HAVE_STRUCT_UTMP_UT_TYPE
610  ut->ut_type = DEAD_PROCESS;
611 #endif
612 #if HAVE_UTMPX
613  gettimeofday(&(ut->ut_tv), 0);
614  pututxline(ut);
615  }
616  endutxent();
617 #else
618  ut->ut_time = time(0);
619  pututline(ut);
620  }
621  endutent();
622 #endif
623 #endif
624 #endif
625 }
626 
627 bool KPty::tcGetAttr(struct ::termios *ttmode) const
628 {
629  Q_D(const KPty);
630 
631  return _tcgetattr(d->masterFd, ttmode) == 0;
632 }
633 
634 bool KPty::tcSetAttr(struct ::termios *ttmode)
635 {
636  Q_D(KPty);
637 
638  return _tcsetattr(d->masterFd, ttmode) == 0;
639 }
640 
641 bool KPty::setWinSize(int lines, int columns, int height, int width)
642 {
643  Q_D(KPty);
644 
645  struct winsize winSize;
646  winSize.ws_row = (unsigned short)lines;
647  winSize.ws_col = (unsigned short)columns;
648  winSize.ws_ypixel = (unsigned short)height;
649  winSize.ws_xpixel = (unsigned short)width;
650  return ioctl(d->masterFd, TIOCSWINSZ, (char *)&winSize) == 0;
651 }
652 
653 bool KPty::setWinSize(int lines, int columns)
654 {
655  return setWinSize(lines, columns, 0, 0);
656 }
657 
658 bool KPty::setEcho(bool echo)
659 {
660  struct ::termios ttmode;
661  if (!tcGetAttr(&ttmode)) {
662  return false;
663  }
664  if (!echo) {
665  ttmode.c_lflag &= ~ECHO;
666  } else {
667  ttmode.c_lflag |= ECHO;
668  }
669  return tcSetAttr(&ttmode);
670 }
671 
672 const char *KPty::ttyName() const
673 {
674  Q_D(const KPty);
675 
676  return d->ttyName.data();
677 }
678 
679 int KPty::masterFd() const
680 {
681  Q_D(const KPty);
682 
683  return d->masterFd;
684 }
685 
686 int KPty::slaveFd() const
687 {
688  Q_D(const KPty);
689 
690  return d->slaveFd;
691 }
692 
693 void KPty::setCTtyEnabled(bool enable)
694 {
695  Q_D(KPty);
696 
697  d->withCTty = enable;
698 }
int slaveFd() const
Definition: kpty.cpp:686
QString number(int n, int base)
KPty()
Constructor.
Definition: kpty.cpp:180
int masterFd() const
Definition: kpty.cpp:679
const std::unique_ptr< KPtyPrivate > d_ptr
Definition: kpty.h:220
void logout()
Removes the utmp entry for this tty.
Definition: kpty.cpp:548
QByteArray toLatin1() const const
int chmod(const QString &path, mode_t mode)
bool tcSetAttr(struct ::termios *ttmode)
Wrapper around tcsetattr(3) with mode TCSANOW.
Definition: kpty.cpp:634
virtual void setupChildProcess()
bool exists() const const
void close()
Close the pty master/slave pair.
Definition: kpty.cpp:408
uint ownerId() const const
Provides primitives for opening & closing a pseudo TTY pair, assigning the controlling TTY,...
Definition: kpty.h:25
bool tcGetAttr(struct ::termios *ttmode) const
Wrapper around tcgetattr(3).
Definition: kpty.cpp:627
bool openSlave()
Open the pty slave descriptor.
Definition: kpty.cpp:388
QString & sprintf(const char *cformat,...)
QString fromLocal8Bit(const char *str, int size)
const char * ttyName() const
Definition: kpty.cpp:672
int execute(const QString &program, const QStringList &arguments)
bool setEcho(bool echo)
Set whether the pty should echo input.
Definition: kpty.cpp:658
bool setWinSize(int lines, int columns, int height, int width)
Change the logical (screen) size of the pty.
Definition: kpty.cpp:641
void setCTty()
Creates a new session and process group and makes this pty the controlling tty.
Definition: kpty.cpp:437
bool open()
Create a pty master/slave pair.
Definition: kpty.cpp:196
~KPty()
Destructor:
Definition: kpty.cpp:191
QFile::Permissions permissions() const const
void setCTtyEnabled(bool enable)
Whether this will be a controlling terminal.
Definition: kpty.cpp:693
void login(const char *user=nullptr, const char *remotehost=nullptr)
Creates an utmp entry for the tty.
Definition: kpty.cpp:459
void closeSlave()
Close the pty slave descriptor.
Definition: kpty.cpp:377
Q_D(Todo)
char * data()
int access(const QString &path, int mode)
QString decodeName(const QByteArray &localFileName)
int stat(const QString &path, KDE_struct_stat *buf)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Mar 26 2023 04:11:32 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.