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 |
77 | extern "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 */ |
95 | extern "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 | |
156 | KPtyPrivate::KPtyPrivate(KPty* parent) : |
157 | masterFd(-1), slaveFd(-1), ownMaster(true), q_ptr(parent) |
158 | { |
159 | } |
160 | |
161 | KPtyPrivate::~KPtyPrivate() |
162 | { |
163 | } |
164 | |
165 | #ifndef HAVE_OPENPTY |
166 | bool 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 | |
177 | KPty::KPty() : |
178 | d_ptr(new KPtyPrivate(this)) |
179 | { |
180 | } |
181 | |
182 | KPty::KPty(KPtyPrivate *d) : |
183 | d_ptr(d) |
184 | { |
185 | d_ptr->q_ptr = this; |
186 | } |
187 | |
188 | KPty::~KPty() |
189 | { |
190 | close(); |
191 | delete d_ptr; |
192 | } |
193 | |
194 | bool 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 | |
376 | bool 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 | |
417 | void 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 | |
427 | bool 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 | |
446 | void 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 | |
474 | void 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 | |
501 | void 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 | |
579 | void 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 | |
646 | bool 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 | |
656 | bool 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 | |
666 | bool 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 | |
677 | bool 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 | |
689 | const char *KPty::ttyName() const |
690 | { |
691 | Q_D(const KPty); |
692 | |
693 | return d->ttyName.data(); |
694 | } |
695 | |
696 | int KPty::masterFd() const |
697 | { |
698 | Q_D(const KPty); |
699 | |
700 | return d->masterFd; |
701 | } |
702 | |
703 | int KPty::slaveFd() const |
704 | { |
705 | Q_D(const KPty); |
706 | |
707 | return d->slaveFd; |
708 | } |
709 | |