kdesu_pty.cpp
00001 /* vi: ts=8 sts=4 sw=4 00002 * 00003 * $Id$ 00004 * 00005 * This file is part of the KDE project, module kdesu. 00006 * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org> 00007 * 00008 * This file contains code from TEShell.C of the KDE konsole. 00009 * Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> 00010 * 00011 * This is free software; you can use this library under the GNU Library 00012 * General Public License, version 2. See the file "COPYING.LIB" for the 00013 * exact licensing terms. 00014 * 00015 * pty.cpp: Access to PTY's on different systems a la UNIX98. 00016 */ 00017 00018 00019 #ifndef _GNU_SOURCE 00020 #define _GNU_SOURCE /* Needed for getpt, ptsname in glibc 2.1.x systems */ 00021 #endif 00022 00023 #include <config.h> 00024 00025 #include <stdio.h> 00026 #include <fcntl.h> 00027 #include <unistd.h> 00028 #include <string.h> 00029 #include <stdlib.h> 00030 #include <errno.h> 00031 00032 #include <sys/types.h> 00033 #include <sys/stat.h> 00034 #include <sys/wait.h> 00035 #include <sys/ioctl.h> 00036 #if defined(__osf__) || defined(__CYGWIN__) 00037 #include <pty.h> 00038 #endif 00039 00040 #include <qglobal.h> 00041 #include <qcstring.h> 00042 00043 #include <kdebug.h> 00044 #include <kstandarddirs.h> 00045 #include "kdesu_pty.h" 00046 00047 // stdlib.h is meant to declare the prototypes but doesn't :( 00048 #ifndef __THROW 00049 #define __THROW 00050 #endif 00051 00052 #ifdef HAVE_GRANTPT 00053 extern "C" int grantpt(int fd) __THROW; 00054 #endif 00055 00056 #ifdef HAVE_PTSNAME 00057 extern "C" char * ptsname(int fd) __THROW; 00058 #endif 00059 00060 #ifdef HAVE_UNLOCKPT 00061 extern "C" int unlockpt(int fd) __THROW; 00062 #endif 00063 00064 #ifdef HAVE__GETPTY 00065 extern "C" char *_getpty(int *, int, mode_t, int); 00066 #endif 00067 00068 #ifdef HAVE_PTY_H 00069 #include <pty.h> 00070 #endif 00071 00072 #include <termios.h> 00073 00074 #ifdef HAVE_LIBUTIL_H 00075 #include <libutil.h> 00076 #elif defined(HAVE_UTIL_H) 00077 #include <util.h> 00078 #endif 00079 00080 PTY::PTY() 00081 { 00082 ptyfd = -1; 00083 } 00084 00085 PTY::~PTY() 00086 { 00087 if (ptyfd >= 0) 00088 close(ptyfd); 00089 } 00090 00091 00092 // Opens a pty master and returns its filedescriptor. 00093 00094 int PTY::getpt() 00095 { 00096 00097 #if defined(HAVE_GETPT) && defined(HAVE_PTSNAME) 00098 00099 // 1: UNIX98: preferred way 00100 ptyfd = ::getpt(); 00101 ttyname = ::ptsname(ptyfd); 00102 return ptyfd; 00103 00104 #elif defined(HAVE_OPENPTY) 00105 // 2: BSD interface 00106 // More preferred than the linux hacks 00107 char name[30]; 00108 int master_fd, slave_fd; 00109 if (openpty(&master_fd, &slave_fd, name, 0L, 0L) != -1) { 00110 ttyname = name; 00111 name[5]='p'; 00112 ptyname = name; 00113 close(slave_fd); // We don't need this yet // Yes, we do. 00114 ptyfd = master_fd; 00115 return ptyfd; 00116 } 00117 ptyfd = -1; 00118 kdDebug(900) << k_lineinfo << "Opening pty failed.\n"; 00119 return -1; 00120 00121 #elif defined(HAVE__GETPTY) 00122 // 3: Irix interface 00123 int master_fd; 00124 ttyname = _getpty(&master_fd,O_RDWR,0600,0); 00125 if (ttyname) 00126 ptyfd = master_fd; 00127 else{ 00128 ptyfd = -1; 00129 kdDebug(900) << k_lineinfo << "Opening pty failed.error" << errno << '\n'; 00130 } 00131 return ptyfd; 00132 00133 #else 00134 00135 // 4: Open terminal device directly 00136 // 4.1: Try /dev/ptmx first. (Linux w/ Unix98 PTYs, Solaris) 00137 00138 ptyfd = open("/dev/ptmx", O_RDWR); 00139 if (ptyfd >= 0) { 00140 ptyname = "/dev/ptmx"; 00141 #ifdef HAVE_PTSNAME 00142 ttyname = ::ptsname(ptyfd); 00143 return ptyfd; 00144 #elif defined (TIOCGPTN) 00145 int ptyno; 00146 if (ioctl(ptyfd, TIOCGPTN, &ptyno) == 0) { 00147 ttyname.sprintf("/dev/pts/%d", ptyno); 00148 return ptyfd; 00149 } 00150 #endif 00151 close(ptyfd); 00152 } 00153 00154 // 4.2: Try /dev/pty[p-e][0-f] (Linux w/o UNIX98 PTY's) 00155 00156 for (const char *c1 = "pqrstuvwxyzabcde"; *c1 != '\0'; c1++) 00157 { 00158 for (const char *c2 = "0123456789abcdef"; *c2 != '\0'; c2++) 00159 { 00160 ptyname.sprintf("/dev/pty%c%c", *c1, *c2); 00161 ttyname.sprintf("/dev/tty%c%c", *c1, *c2); 00162 if (access(ptyname, F_OK) < 0) 00163 goto linux_out; 00164 ptyfd = open(ptyname, O_RDWR); 00165 if (ptyfd >= 0) 00166 return ptyfd; 00167 } 00168 } 00169 linux_out: 00170 00171 // 4.3: Try /dev/pty%d (SCO, Unixware) 00172 00173 for (int i=0; i<256; i++) 00174 { 00175 ptyname.sprintf("/dev/ptyp%d", i); 00176 ttyname.sprintf("/dev/ttyp%d", i); 00177 if (access(ptyname, F_OK) < 0) 00178 break; 00179 ptyfd = open(ptyname, O_RDWR); 00180 if (ptyfd >= 0) 00181 return ptyfd; 00182 } 00183 00184 00185 // Other systems ?? 00186 ptyfd = -1; 00187 kdDebug(900) << k_lineinfo << "Unknown system or all methods failed.\n"; 00188 return -1; 00189 00190 #endif // HAVE_GETPT && HAVE_PTSNAME 00191 00192 } 00193 00194 00195 int PTY::grantpt() 00196 { 00197 if (ptyfd < 0) 00198 return -1; 00199 00200 #ifdef HAVE_GRANTPT 00201 00202 return ::grantpt(ptyfd); 00203 00204 #elif defined(HAVE_OPENPTY) 00205 00206 // the BSD openpty() interface chowns the devices properly for us, 00207 // no need to do this at all 00208 return 0; 00209 00210 #else 00211 00212 // konsole_grantpty only does /dev/pty?? 00213 if (ptyname.left(8) != "/dev/pty") 00214 return 0; 00215 00216 // Use konsole_grantpty: 00217 if (KStandardDirs::findExe("konsole_grantpty").isEmpty()) 00218 { 00219 kdError(900) << k_lineinfo << "konsole_grantpty not found.\n"; 00220 return -1; 00221 } 00222 00223 // As defined in konsole_grantpty.c 00224 const int pty_fileno = 3; 00225 00226 pid_t pid; 00227 if ((pid = fork()) == -1) 00228 { 00229 kdError(900) << k_lineinfo << "fork(): " << perror << "\n"; 00230 return -1; 00231 } 00232 00233 if (pid) 00234 { 00235 // Parent: wait for child 00236 int ret; 00237 waitpid(pid, &ret, 0); 00238 if (WIFEXITED(ret) && !WEXITSTATUS(ret)) 00239 return 0; 00240 kdError(900) << k_lineinfo << "konsole_grantpty returned with error: " 00241 << WEXITSTATUS(ret) << "\n"; 00242 return -1; 00243 } else 00244 { 00245 // Child: exec konsole_grantpty 00246 if (ptyfd != pty_fileno && dup2(ptyfd, pty_fileno) < 0) 00247 _exit(1); 00248 execlp("konsole_grantpty", "konsole_grantpty", "--grant", (void *)0); 00249 kdError(900) << k_lineinfo << "exec(): " << perror << "\n"; 00250 _exit(1); 00251 } 00252 00253 // shut up, gcc 00254 return 0; 00255 00256 #endif // HAVE_GRANTPT 00257 } 00258 00259 00264 int PTY::unlockpt() 00265 { 00266 if (ptyfd < 0) 00267 return -1; 00268 00269 #ifdef HAVE_UNLOCKPT 00270 00271 // (Linux w/ glibc 2.1, Solaris, ...) 00272 00273 return ::unlockpt(ptyfd); 00274 00275 #elif defined(TIOCSPTLCK) 00276 00277 // Unlock pty (Linux w/ UNIX98 PTY's & glibc 2.0) 00278 int flag = 0; 00279 return ioctl(ptyfd, TIOCSPTLCK, &flag); 00280 00281 #else 00282 00283 // Other systems (Linux w/o UNIX98 PTY's, ...) 00284 return 0; 00285 00286 #endif 00287 00288 } 00289 00290 00295 QCString PTY::ptsname() 00296 { 00297 if (ptyfd < 0) 00298 return 0; 00299 00300 return ttyname; 00301 } 00302