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