source: ogBrowser-Git/qtermwidget/lib/kpty.cpp @ ffbf8ac

jenkinsmain
Last change on this file since ffbf8ac was 64efc22, checked in by Vadim Troshchinskiy <vtroshchinskiy@…>, 19 months ago

Update qtermwidget to modern version

  • Property mode set to 100644
File size: 16.9 KB
Line 
1/*
2
3   This file is part of the KDE libraries
4   Copyright (C) 2002 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#include "kpty_p.h"
26
27#include <QtDebug>
28
29
30#if defined(__FreeBSD__) || defined(__DragonFly__)
31#define HAVE_LOGIN
32#define HAVE_LIBUTIL_H
33#endif
34
35#if defined(__OpenBSD__)
36#define HAVE_LOGIN
37#define HAVE_UTIL_H
38#endif
39
40#if defined(__NetBSD__)
41#define HAVE_LOGIN
42#define HAVE_UTIL_H
43#define HAVE_OPENPTY
44#endif
45
46#if defined(__APPLE__)
47#define HAVE_OPENPTY
48#define HAVE_UTIL_H
49#endif
50
51#ifdef __sgi
52#define __svr4__
53#endif
54
55#ifdef __osf__
56#define _OSF_SOURCE
57#include <float.h>
58#endif
59
60#ifdef _AIX
61#define _ALL_SOURCE
62#endif
63
64// __USE_XOPEN isn't defined by default in ICC
65// (needed for ptsname(), grantpt() and unlockpt())
66#ifdef __INTEL_COMPILER
67#  ifndef __USE_XOPEN
68#    define __USE_XOPEN
69#  endif
70#endif
71
72#include <sys/types.h>
73#include <sys/ioctl.h>
74#include <sys/time.h>
75#include <sys/resource.h>
76#include <sys/stat.h>
77#include <sys/param.h>
78
79#include <cerrno>
80#include <fcntl.h>
81#include <ctime>
82#include <cstdlib>
83#include <cstdio>
84#include <cstring>
85#include <unistd.h>
86#include <grp.h>
87
88#if defined(HAVE_PTY_H)
89# include <pty.h>
90#endif
91
92#ifdef HAVE_LIBUTIL_H
93# include <libutil.h>
94#elif defined(HAVE_UTIL_H)
95# include <util.h>
96#endif
97
98#ifdef HAVE_UTEMPTER
99extern "C" {
100# include <utempter.h>
101}
102#else
103# include <utmp.h>
104# ifdef HAVE_UTMPX
105#  include <utmpx.h>
106# endif
107# if !defined(_PATH_UTMPX) && defined(_UTMPX_FILE)
108#  define _PATH_UTMPX _UTMPX_FILE
109# endif
110# ifdef HAVE_UPDWTMPX
111#  if !defined(_PATH_WTMPX) && defined(_WTMPX_FILE)
112#   define _PATH_WTMPX _WTMPX_FILE
113#  endif
114# endif
115#endif
116
117/* for HP-UX (some versions) the extern C is needed, and for other
118   platforms it doesn't hurt */
119extern "C" {
120#include <termios.h>
121#if defined(HAVE_TERMIO_H)
122# include <termio.h> // struct winsize on some systems
123#endif
124}
125
126#if defined (_HPUX_SOURCE)
127# define _TERMIOS_INCLUDED
128# include <bsdtty.h>
129#endif
130
131#ifdef HAVE_SYS_STROPTS_H
132# include <sys/stropts.h> // Defines I_PUSH
133# define _NEW_TTY_CTRL
134#endif
135
136#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__)
137# define _tcgetattr(fd, ttmode) ioctl(fd, TIOCGETA, (char *)ttmode)
138#else
139# if defined(_HPUX_SOURCE) || defined(__Lynx__) || defined (__CYGWIN__) || defined(__GNU__)
140#  define _tcgetattr(fd, ttmode) tcgetattr(fd, ttmode)
141# else
142#  define _tcgetattr(fd, ttmode) ioctl(fd, TCGETS, (char *)ttmode)
143# endif
144#endif
145
146#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__)
147# define _tcsetattr(fd, ttmode) ioctl(fd, TIOCSETA, (char *)ttmode)
148#else
149# if defined(_HPUX_SOURCE) || defined(__CYGWIN__) || defined(__GNU__)
150#  define _tcsetattr(fd, ttmode) tcsetattr(fd, TCSANOW, ttmode)
151# else
152#  define _tcsetattr(fd, ttmode) ioctl(fd, TCSETS, (char *)ttmode)
153# endif
154#endif
155
156//#include <kdebug.h>
157//#include <kstandarddirs.h>  // findExe
158
159// not defined on HP-UX for example
160#ifndef CTRL
161# define CTRL(x) ((x) & 037)
162#endif
163
164#define TTY_GROUP "tty"
165
166///////////////////////
167// private functions //
168///////////////////////
169
170//////////////////
171// private data //
172//////////////////
173
174KPtyPrivate::KPtyPrivate(KPty* parent) :
175        masterFd(-1), slaveFd(-1), ownMaster(true), q_ptr(parent)
176{
177}
178
179KPtyPrivate::~KPtyPrivate()
180{
181}
182
183bool KPtyPrivate::chownpty(bool)
184{
185//    return !QProcess::execute(KStandardDirs::findExe("kgrantpty"),
186//        QStringList() << (grant?"--grant":"--revoke") << QString::number(masterFd));
187    return true;
188}
189
190/////////////////////////////
191// public member functions //
192/////////////////////////////
193
194KPty::KPty() :
195        d_ptr(new KPtyPrivate(this))
196{
197}
198
199KPty::KPty(KPtyPrivate *d) :
200        d_ptr(d)
201{
202    d_ptr->q_ptr = this;
203}
204
205KPty::~KPty()
206{
207    close();
208    delete d_ptr;
209}
210
211bool KPty::open()
212{
213    Q_D(KPty);
214
215    if (d->masterFd >= 0)
216        return true;
217
218    d->ownMaster = true;
219
220    QByteArray ptyName;
221
222    // Find a master pty that we can open ////////////////////////////////
223
224    // Because not all the pty animals are created equal, they want to
225    // be opened by several different methods.
226
227    // We try, as we know them, one by one.
228
229#ifdef HAVE_OPENPTY
230
231    char ptsn[PATH_MAX];
232    if (::openpty( &d->masterFd, &d->slaveFd, ptsn, 0, 0)) {
233        d->masterFd = -1;
234        d->slaveFd = -1;
235        qWarning() << "Can't open a pseudo teletype";
236        return false;
237    }
238    d->ttyName = ptsn;
239
240#else
241
242#ifdef HAVE__GETPTY // irix
243
244    char *ptsn = _getpty(&d->masterFd, O_RDWR|O_NOCTTY, S_IRUSR|S_IWUSR, 0);
245    if (ptsn) {
246        d->ttyName = ptsn;
247        goto grantedpt;
248    }
249
250#elif defined(HAVE_PTSNAME) || defined(TIOCGPTN)
251
252#ifdef HAVE_POSIX_OPENPT
253    d->masterFd = ::posix_openpt(O_RDWR|O_NOCTTY);
254#elif defined(HAVE_GETPT)
255    d->masterFd = ::getpt();
256#elif defined(PTM_DEVICE)
257    d->masterFd = ::open(PTM_DEVICE, O_RDWR|O_NOCTTY);
258#else
259# error No method to open a PTY master detected.
260#endif
261    if (d->masterFd >= 0) {
262#ifdef HAVE_PTSNAME
263        char *ptsn = ptsname(d->masterFd);
264        if (ptsn) {
265            d->ttyName = ptsn;
266#else
267    int ptyno;
268    if (ioctl(d->masterFd, TIOCGPTN, &ptyno) != -1) {
269        d->ttyName = QByteArray("/dev/pts/") + QByteArray::number(ptyno);
270#endif
271#ifdef HAVE_GRANTPT
272            if (!grantpt(d->masterFd)) {
273                goto grantedpt;
274            }
275#else
276
277    goto gotpty;
278#endif
279        }
280        ::close(d->masterFd);
281        d->masterFd = -1;
282    }
283#endif // HAVE_PTSNAME || TIOCGPTN
284
285    // Linux device names, FIXME: Trouble on other systems?
286    for (const char * s3 = "pqrstuvwxyzabcde"; *s3; s3++) {
287        for (const char * s4 = "0123456789abcdef"; *s4; s4++) {
288            ptyName = QByteArrayLiteral("/dev/pty") + *s3 + *s4;
289            d->ttyName = QByteArrayLiteral("/dev/tty") + *s3 + *s4;
290
291            d->masterFd = ::open(ptyName.data(), O_RDWR);
292            if (d->masterFd >= 0) {
293#ifdef Q_OS_SOLARIS
294                /* Need to check the process group of the pty.
295                 * If it exists, then the slave pty is in use,
296                 * and we need to get another one.
297                 */
298                int pgrp_rtn;
299                if (ioctl(d->masterFd, TIOCGPGRP, &pgrp_rtn) != -1 || errno != EIO) {
300                    ::close(d->masterFd);
301                    d->masterFd = -1;
302                    continue;
303                }
304#endif /* Q_OS_SOLARIS */
305                if (!access(d->ttyName.data(),R_OK|W_OK)) { // checks availability based on permission bits
306                    if (!geteuid()) {
307                        struct group * p = getgrnam(TTY_GROUP);
308                        if (!p) {
309                            p = getgrnam("wheel");
310                        }
311                        gid_t gid = p ? p->gr_gid : getgid ();
312
313                        if (!chown(d->ttyName.data(), getuid(), gid)) {
314                            chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IWGRP);
315                        }
316                    }
317                    goto gotpty;
318                }
319                ::close(d->masterFd);
320                d->masterFd = -1;
321            }
322        }
323    }
324
325    qWarning() << "Can't open a pseudo teletype";
326    return false;
327
328gotpty:
329    struct stat st;
330    if (stat(d->ttyName.data(), &st)) {
331        return false; // this just cannot happen ... *cough*  Yeah right, I just
332        // had it happen when pty #349 was allocated.  I guess
333        // there was some sort of leak?  I only had a few open.
334    }
335    if (((st.st_uid != getuid()) ||
336            (st.st_mode & (S_IRGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH))) &&
337            !d->chownpty(true)) {
338        qWarning()
339        << "chownpty failed for device " << ptyName << "::" << d->ttyName
340        << "\nThis means the communication can be eavesdropped."
341        << Qt::endl;
342    }
343
344#if defined (HAVE__GETPTY) || defined (HAVE_GRANTPT)
345grantedpt:
346#endif
347
348#ifdef HAVE_REVOKE
349    revoke(d->ttyName.data());
350#endif
351
352#ifdef HAVE_UNLOCKPT
353    unlockpt(d->masterFd);
354#elif defined(TIOCSPTLCK)
355    int flag = 0;
356    ioctl(d->masterFd, TIOCSPTLCK, &flag);
357#endif
358
359    d->slaveFd = ::open(d->ttyName.data(), O_RDWR | O_NOCTTY);
360    if (d->slaveFd < 0) {
361        qWarning() << "Can't open slave pseudo teletype";
362        ::close(d->masterFd);
363        d->masterFd = -1;
364        return false;
365    }
366
367#if (defined(__svr4__) || defined(__sgi__))
368    // Solaris
369    ioctl(d->slaveFd, I_PUSH, "ptem");
370    ioctl(d->slaveFd, I_PUSH, "ldterm");
371#endif
372
373#endif /* HAVE_OPENPTY */
374
375    fcntl(d->masterFd, F_SETFD, FD_CLOEXEC);
376    fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC);
377
378    return true;
379}
380
381bool KPty::open(int fd)
382{
383#if !defined(HAVE_PTSNAME) && !defined(TIOCGPTN)
384     qWarning() << "Unsupported attempt to open pty with fd" << fd;
385     return false;
386#else
387    Q_D(KPty);
388
389    if (d->masterFd >= 0) {
390        qWarning() << "Attempting to open an already open pty";
391         return false;
392    }
393
394    d->ownMaster = false;
395
396# ifdef HAVE_PTSNAME
397    char *ptsn = ptsname(fd);
398    if (ptsn) {
399        d->ttyName = ptsn;
400# else
401    int ptyno;
402    if (ioctl(fd, TIOCGPTN, &ptyno) != -1) {
403        const size_t sz = 32;
404        char buf[sz];
405        const size_t r = snprintf(buf, sz, "/dev/pts/%d", ptyno);
406        if (sz <= r) {
407            qWarning("KPty::open: Buffer too small\n");
408        }
409        d->ttyName = buf;
410# endif
411    } else {
412        qWarning() << "Failed to determine pty slave device for fd" << fd;
413        return false;
414    }
415
416    d->masterFd = fd;
417    if (!openSlave()) {
418
419        d->masterFd = -1;
420        return false;
421    }
422
423    return true;
424#endif
425}
426
427void KPty::closeSlave()
428{
429    Q_D(KPty);
430
431    if (d->slaveFd < 0) {
432        return;
433    }
434    ::close(d->slaveFd);
435    d->slaveFd = -1;
436}
437
438bool KPty::openSlave()
439{
440    Q_D(KPty);
441
442    if (d->slaveFd >= 0)
443        return true;
444    if (d->masterFd < 0) {
445        qDebug() << "Attempting to open pty slave while master is closed";
446        return false;
447    }
448    //d->slaveFd = KDE_open(d->ttyName.data(), O_RDWR | O_NOCTTY);
449    d->slaveFd = ::open(d->ttyName.data(), O_RDWR | O_NOCTTY);
450    if (d->slaveFd < 0) {
451        qDebug() << "Can't open slave pseudo teletype";
452        return false;
453    }
454    fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC);
455    return true;
456}
457
458void KPty::close()
459{
460    Q_D(KPty);
461
462    if (d->masterFd < 0) {
463        return;
464    }
465    closeSlave();
466    // don't bother resetting unix98 pty, it will go away after closing master anyway.
467    if (memcmp(d->ttyName.data(), "/dev/pts/", 9)) {
468        if (!geteuid()) {
469            struct stat st;
470            if (!stat(d->ttyName.data(), &st)) {
471                chown(d->ttyName.data(), 0, st.st_gid == getgid() ? 0 : -1);
472                chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
473            }
474        } else {
475            fcntl(d->masterFd, F_SETFD, 0);
476            d->chownpty(false);
477        }
478    }
479    ::close(d->masterFd);
480    d->masterFd = -1;
481}
482
483void KPty::setCTty()
484{
485    Q_D(KPty);
486
487    // Setup job control //////////////////////////////////
488
489    // Become session leader, process group leader,
490    // and get rid of the old controlling terminal.
491    setsid();
492
493    // make our slave pty the new controlling terminal.
494#ifdef TIOCSCTTY
495    ioctl(d->slaveFd, TIOCSCTTY, 0);
496#else
497    // __svr4__ hack: the first tty opened after setsid() becomes controlling tty
498    ::close(::open(d->ttyName, O_WRONLY, 0));
499#endif
500
501    // make our new process group the foreground group on the pty
502    int pgrp = getpid();
503#if defined(_POSIX_VERSION) || defined(__svr4__)
504    tcsetpgrp(d->slaveFd, pgrp);
505#elif defined(TIOCSPGRP)
506    ioctl(d->slaveFd, TIOCSPGRP, (char *)&pgrp);
507#endif
508}
509
510void KPty::login(const char * user, const char * remotehost)
511{
512#ifdef HAVE_UTEMPTER
513    Q_D(KPty);
514
515    addToUtmp(d->ttyName.constData(), remotehost, d->masterFd);
516    Q_UNUSED(user);
517#else
518# ifdef HAVE_UTMPX
519    struct utmpx l_struct;
520# else
521    struct utmp l_struct;
522# endif
523    memset(&l_struct, 0, sizeof(l_struct));
524    // note: strncpy without terminators _is_ correct here. man 4 utmp
525
526    if (user) {
527# ifdef HAVE_UTMPX
528        strncpy(l_struct.ut_user, user, sizeof(l_struct.ut_user));
529# else
530        strncpy(l_struct.ut_name, user, sizeof(l_struct.ut_name));
531# endif
532    }
533
534    if (remotehost) {
535        strncpy(l_struct.ut_host, remotehost, sizeof(l_struct.ut_host));
536# ifdef HAVE_STRUCT_UTMP_UT_SYSLEN
537        l_struct.ut_syslen = qMin(strlen(remotehost), sizeof(l_struct.ut_host));
538# endif
539    }
540
541# ifndef __GLIBC__
542    Q_D(KPty);
543    const char * str_ptr = d->ttyName.data();
544    if (!memcmp(str_ptr, "/dev/", 5)) {
545        str_ptr += 5;
546    }
547    strncpy(l_struct.ut_line, str_ptr, sizeof(l_struct.ut_line));
548#  ifdef HAVE_STRUCT_UTMP_UT_ID
549    strncpy(l_struct.ut_id,
550            str_ptr + strlen(str_ptr) - sizeof(l_struct.ut_id),
551            sizeof(l_struct.ut_id));
552#  endif
553# endif
554
555# ifdef HAVE_UTMPX
556    gettimeofday(&l_struct.ut_tv, 0);
557# else
558    l_struct.ut_time = time(nullptr);
559# endif
560
561# ifdef HAVE_LOGIN
562#  ifdef HAVE_LOGINX
563    ::loginx(&l_struct);
564#  else
565    ::login(&l_struct);
566#  endif
567# else
568#  ifdef HAVE_STRUCT_UTMP_UT_TYPE
569    l_struct.ut_type = USER_PROCESS;
570#  endif
571#  ifdef HAVE_STRUCT_UTMP_UT_PID
572    l_struct.ut_pid = getpid();
573#   ifdef HAVE_STRUCT_UTMP_UT_SESSION
574    l_struct.ut_session = getsid(0);
575#   endif
576#  endif
577#  ifdef HAVE_UTMPX
578    utmpxname(_PATH_UTMPX);
579    setutxent();
580    pututxline(&l_struct);
581    endutxent();
582#   ifdef HAVE_UPDWTMPX
583    updwtmpx(_PATH_WTMPX, &l_struct);
584#   endif
585#  else
586    utmpname(_PATH_UTMP);
587    setutent();
588    pututline(&l_struct);
589    endutent();
590    updwtmp(_PATH_WTMP, &l_struct);
591#  endif
592# endif
593#endif
594}
595
596void KPty::logout()
597{
598#ifdef HAVE_UTEMPTER
599    Q_D(KPty);
600
601    removeLineFromUtmp(d->ttyName.constData(), d->masterFd);
602#else
603    Q_D(KPty);
604
605    const char *str_ptr = d->ttyName.data();
606    if (!memcmp(str_ptr, "/dev/", 5)) {
607        str_ptr += 5;
608    }
609# ifdef __GLIBC__
610    else {
611        const char * sl_ptr = strrchr(str_ptr, '/');
612        if (sl_ptr) {
613            str_ptr = sl_ptr + 1;
614        }
615    }
616# endif
617# ifdef HAVE_LOGIN
618#  ifdef HAVE_LOGINX
619    ::logoutx(str_ptr, 0, DEAD_PROCESS);
620#  else
621    ::logout(str_ptr);
622#  endif
623# else
624#  ifdef HAVE_UTMPX
625    struct utmpx l_struct, *ut;
626#  else
627    struct utmp l_struct, *ut;
628#  endif
629    memset(&l_struct, 0, sizeof(l_struct));
630
631    strncpy(l_struct.ut_line, str_ptr, sizeof(l_struct.ut_line));
632
633#  ifdef HAVE_UTMPX
634    utmpxname(_PATH_UTMPX);
635    setutxent();
636    if ((ut = getutxline(&l_struct))) {
637#  else
638    utmpname(_PATH_UTMP);
639    setutent();
640    if ((ut = getutline(&l_struct))) {
641#  endif
642#  ifdef HAVE_UTMPX
643        memset(ut->ut_user, 0, sizeof(*ut->ut_user));
644#  else
645        memset(ut->ut_name, 0, sizeof(*ut->ut_name));
646#  endif
647        memset(ut->ut_host, 0, sizeof(*ut->ut_host));
648#  ifdef HAVE_STRUCT_UTMP_UT_SYSLEN
649        ut->ut_syslen = 0;
650#  endif
651#  ifdef HAVE_STRUCT_UTMP_UT_TYPE
652        ut->ut_type = DEAD_PROCESS;
653#  endif
654#  ifdef HAVE_UTMPX
655        gettimeofday(&ut->ut_tv, 0);
656        pututxline(ut);
657    }
658    endutxent();
659#  else
660    ut->ut_time = time(nullptr);
661    pututline(ut);
662}
663endutent();
664#  endif
665# endif
666#endif
667}
668
669// XXX Supposedly, tc[gs]etattr do not work with the master on Solaris.
670// Please verify.
671
672bool KPty::tcGetAttr(struct ::termios * ttmode) const
673{
674    Q_D(const KPty);
675
676    return _tcgetattr(d->masterFd, ttmode) == 0;
677}
678
679bool KPty::tcSetAttr(struct ::termios * ttmode)
680{
681    Q_D(KPty);
682
683    return _tcsetattr(d->masterFd, ttmode) == 0;
684}
685
686bool KPty::setWinSize(int lines, int columns)
687{
688    Q_D(KPty);
689
690    struct winsize winSize;
691    memset(&winSize, 0, sizeof(winSize));
692    winSize.ws_row = (unsigned short)lines;
693    winSize.ws_col = (unsigned short)columns;
694    return ioctl(d->masterFd, TIOCSWINSZ, (char *)&winSize) != -1;
695}
696
697bool KPty::setEcho(bool echo)
698{
699    struct ::termios ttmode;
700    if (!tcGetAttr(&ttmode)) {
701        return false;
702    }
703    if (!echo) {
704        ttmode.c_lflag &= ~ECHO;
705    } else {
706        ttmode.c_lflag |= ECHO;
707    }
708    return tcSetAttr(&ttmode);
709}
710
711const char * KPty::ttyName() const
712{
713    Q_D(const KPty);
714
715    return d->ttyName.data();
716}
717
718int KPty::masterFd() const
719{
720    Q_D(const KPty);
721
722    return d->masterFd;
723}
724
725int KPty::slaveFd() const
726{
727    Q_D(const KPty);
728
729    return d->slaveFd;
730}
Note: See TracBrowser for help on using the repository browser.