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