File indexing completed on 2024-04-28 04:38:36
0001 /* 0002 SPDX-FileCopyrightText: 1999 John Birch <jbb@kdevelop.org> 0003 0004 This code was copied originally from the KDEStudio project: 0005 SPDX-FileCopyrightText: Judin Maxim 0006 0007 It was then updated with later code from konsole (KDE). 0008 0009 It has also been enhanced with an idea from the code in kdbg: 0010 SPDX-FileCopyrightText: Johannes Sixt<Johannes.Sixt@telecom.at> 0011 0012 SPDX-License-Identifier: GPL-2.0-or-later 0013 */ 0014 0015 #ifdef HAVE_CONFIG_H 0016 #include <config.h> 0017 #endif 0018 0019 #ifdef __osf__ 0020 #define _XOPEN_SOURCE_EXTENDED 0021 #endif 0022 0023 #include <sys/types.h> 0024 #ifndef _MSC_VER 0025 #include <sys/ioctl.h> 0026 #include <sys/wait.h> 0027 #include <sys/time.h> 0028 #include <sys/resource.h> 0029 #endif 0030 #include <sys/stat.h> 0031 0032 #ifdef HAVE_SYS_STROPTS_H 0033 #include <sys/stropts.h> 0034 #define _NEW_TTY_CTRL 0035 #endif 0036 0037 #include <cassert> 0038 #include <fcntl.h> 0039 #ifndef _MSC_VER 0040 #include <grp.h> 0041 #include <termios.h> 0042 #include <unistd.h> 0043 #endif 0044 #include <cerrno> 0045 #include <csignal> 0046 #include <cstdio> 0047 #include <cstdlib> 0048 #include <ctime> 0049 0050 #if defined (_HPUX_SOURCE) 0051 #define _TERMIOS_INCLUDED 0052 #include <bsdtty.h> 0053 #endif 0054 0055 #include <QSocketNotifier> 0056 #include <QString> 0057 #include <QFile> 0058 #include <QProcess> 0059 #include <QTemporaryFile> 0060 0061 #include <KLocalizedString> 0062 0063 #include <QCoreApplication> 0064 #include <QStandardPaths> 0065 0066 #include "stty.h" 0067 #include "debuglog.h" 0068 0069 #define PTY_FILENO 3 0070 #define BASE_CHOWN "konsole_grantpty" 0071 0072 using namespace KDevMI; 0073 0074 static int chownpty(int fd, int grant) 0075 // param fd: the fd of a master pty. 0076 // param grant: 1 to grant, 0 to revoke 0077 // returns 1 on success 0 on fail 0078 { 0079 #ifndef Q_OS_WIN 0080 void(*tmp)(int) = signal(SIGCHLD,SIG_DFL); 0081 pid_t pid = fork(); 0082 if (pid < 0) { 0083 signal(SIGCHLD,tmp); 0084 return 0; 0085 } 0086 if (pid == 0) { 0087 /* We pass the master pseudo terminal as file descriptor PTY_FILENO. */ 0088 if (fd != PTY_FILENO && dup2(fd, PTY_FILENO) < 0) 0089 ::exit(1); 0090 0091 QString path = QStandardPaths::findExecutable(QStringLiteral(BASE_CHOWN)); 0092 const QByteArray encodedPath = QFile::encodeName(path); 0093 execle(encodedPath.constData(), BASE_CHOWN, grant?"--grant":"--revoke", (void *)nullptr, NULL); 0094 ::exit(1); // should not be reached 0095 } 0096 if (pid > 0) { 0097 int w; 0098 // retry: 0099 int rc = waitpid (pid, &w, 0); 0100 if (rc != pid) 0101 ::exit(1); 0102 0103 // { // signal from other child, behave like catchChild. 0104 // // guess this gives quite some control chaos... 0105 // Shell* sh = shells.indexOf(rc); 0106 // if (sh) { shells.remove(rc); sh->doneShell(w); } 0107 // goto retry; 0108 // } 0109 signal(SIGCHLD,tmp); 0110 return (rc != -1 && WIFEXITED(w) && WEXITSTATUS(w) == 0); 0111 } 0112 signal(SIGCHLD,tmp); 0113 #endif 0114 return 0; //dummy. 0115 } 0116 0117 // ************************************************************************** 0118 0119 STTY::STTY(bool ext, const QString &termAppName) 0120 : QObject(), 0121 m_externalTerminal(nullptr), 0122 external_(ext) 0123 { 0124 if (ext) { 0125 findExternalTTY(termAppName); 0126 } else { 0127 fout = findTTY(); 0128 if (fout >= 0) { 0129 ttySlave = QString::fromLatin1(tty_slave); 0130 out = new QSocketNotifier(fout, QSocketNotifier::Read, this); 0131 connect( out, &QSocketNotifier::activated, this, &STTY::OutReceived ); 0132 } 0133 } 0134 } 0135 0136 // ************************************************************************** 0137 0138 STTY::~STTY() 0139 { 0140 #ifndef Q_OS_WIN 0141 if (out) { 0142 ::close(fout); 0143 delete out; 0144 } 0145 #endif 0146 } 0147 0148 // ************************************************************************** 0149 0150 int STTY::findTTY() 0151 { 0152 int ptyfd = -1; 0153 bool needGrantPty = true; 0154 #ifndef Q_OS_WIN 0155 // Find a master pty that we can open //////////////////////////////// 0156 0157 #ifdef __sgi__ 0158 ptyfd = open("/dev/ptmx",O_RDWR); 0159 #elif defined(Q_OS_MAC) || defined(Q_OS_FREEBSD) 0160 ptyfd = posix_openpt(O_RDWR); 0161 #endif 0162 #if defined(__sgi__) || defined(Q_OS_MAC) || defined(Q_OS_FREEBSD) 0163 if (ptyfd == -1) { 0164 perror("Can't open a pseudo teletype"); 0165 return(-1); 0166 } else if (ptyfd >= 0) { 0167 strncpy(tty_slave, ptsname(ptyfd), 50); 0168 grantpt(ptyfd); 0169 unlockpt(ptyfd); 0170 needGrantPty = false; 0171 } 0172 #endif 0173 0174 // first we try UNIX PTY's 0175 #if defined(TIOCGPTN) && !defined(Q_OS_FREEBSD) 0176 strcpy(pty_master,"/dev/ptmx"); 0177 strcpy(tty_slave,"/dev/pts/"); 0178 ptyfd = open(pty_master,O_RDWR); 0179 if (ptyfd >= 0) { // got the master pty 0180 int ptyno; 0181 if (ioctl(ptyfd, TIOCGPTN, &ptyno) == 0) { 0182 struct stat sbuf; 0183 sprintf(tty_slave,"/dev/pts/%d",ptyno); 0184 if (stat(tty_slave,&sbuf) == 0 && S_ISCHR(sbuf.st_mode)) 0185 needGrantPty = false; 0186 else { 0187 close(ptyfd); 0188 ptyfd = -1; 0189 } 0190 } else { 0191 close(ptyfd); 0192 ptyfd = -1; 0193 } 0194 } 0195 #endif 0196 0197 #if defined(_SCO_DS) || defined(__USLC__) /* SCO OSr5 and UnixWare */ 0198 if (ptyfd < 0) { 0199 for (int idx = 0; idx < 256; idx++) 0200 { sprintf(pty_master, "/dev/ptyp%d", idx); 0201 sprintf(tty_slave, "/dev/ttyp%d", idx); 0202 if (access(tty_slave, F_OK) < 0) { idx = 256; break; } 0203 if ((ptyfd = open (pty_master, O_RDWR)) >= 0) 0204 { if (access (tty_slave, R_OK|W_OK) == 0) break; 0205 close(ptyfd); ptyfd = -1; 0206 } 0207 } 0208 } 0209 #endif 0210 if (ptyfd < 0) { /// \FIXME Linux, Trouble on other systems? 0211 for (const char* s3 = "pqrstuvwxyzabcde"; *s3 != 0; s3++) { 0212 for (const char* s4 = "0123456789abcdef"; *s4 != 0; s4++) { 0213 sprintf(pty_master,"/dev/pty%c%c",*s3,*s4); 0214 sprintf(tty_slave,"/dev/tty%c%c",*s3,*s4); 0215 if ((ptyfd = open(pty_master, O_RDWR)) >= 0) { 0216 if (geteuid() == 0 || access(tty_slave, R_OK|W_OK) == 0) 0217 break; 0218 0219 close(ptyfd); 0220 ptyfd = -1; 0221 } 0222 } 0223 0224 if (ptyfd >= 0) 0225 break; 0226 } 0227 } 0228 0229 if (ptyfd >= 0) { 0230 if (needGrantPty && !chownpty(ptyfd, true)) { 0231 fprintf(stderr,"kdevelop: chownpty failed for device %s::%s.\n",pty_master,tty_slave); 0232 fprintf(stderr," : This means the session can be eavesdroped.\n"); 0233 fprintf(stderr," : Make sure konsole_grantpty is installed and setuid root.\n"); 0234 } 0235 0236 ::fcntl(ptyfd, F_SETFL, O_NONBLOCK); 0237 #ifdef TIOCSPTLCK 0238 int flag = 0; 0239 ioctl(ptyfd, TIOCSPTLCK, &flag); // unlock pty 0240 #endif 0241 } 0242 if (ptyfd==-1) { 0243 m_lastError = i18n("Cannot use the tty* or pty* devices.\n" 0244 "Check the settings on /dev/tty* and /dev/pty*\n" 0245 "As root you may need to \"chmod ug+rw\" tty* and pty* devices " 0246 "and/or add the user to the tty group using " 0247 "\"usermod -aG tty username\"."); 0248 } 0249 #endif 0250 return ptyfd; 0251 } 0252 0253 // ************************************************************************** 0254 0255 void STTY::OutReceived(int f) 0256 { 0257 #ifndef Q_OS_WIN 0258 char buf[1024]; 0259 int n; 0260 0261 // read until socket is empty. We shouldn't be receiving a continuous 0262 // stream of data, so the loop is unlikely to cause problems. 0263 while ((n = ::read(f, buf, sizeof(buf)-1)) > 0) { 0264 *(buf+n) = 0; // a standard string 0265 QByteArray ba(buf); 0266 emit OutOutput(ba); 0267 } 0268 // Note: for some reason, n can be 0 here. 0269 // I can understand that non-blocking read returns 0, 0270 // but I don't understand how OutReceived can be even 0271 // called when there's no input. 0272 if (n == 0 /* eof */ 0273 || (n == -1 && errno != EAGAIN)) 0274 { 0275 // Found eof or error. Disable socket notifier, otherwise Qt 0276 // will repeatedly call this method, eating CPU 0277 // cycles. 0278 out->setEnabled(false); 0279 } 0280 #endif 0281 } 0282 0283 void STTY::readRemaining() 0284 { 0285 if (!external_) 0286 OutReceived(fout); 0287 } 0288 0289 bool STTY::findExternalTTY(const QString& termApp) 0290 { 0291 #ifndef Q_OS_WIN 0292 QString appName(termApp.isEmpty() ? QStringLiteral("xterm") : termApp); 0293 0294 if (QStandardPaths::findExecutable(appName).isEmpty()) { 0295 m_lastError = i18n("%1 is incorrect terminal name", termApp); 0296 return false; 0297 } 0298 0299 QTemporaryFile file; 0300 if (!file.open()) { 0301 m_lastError = i18n("Can't create a temporary file"); 0302 return false; 0303 } 0304 0305 m_externalTerminal.reset(new QProcess(this)); 0306 0307 if (appName == QLatin1String("konsole")) { 0308 m_externalTerminal->start(appName, QStringList{ 0309 QStringLiteral("-e"), 0310 QStringLiteral("sh"), 0311 QStringLiteral("-c"), 0312 QLatin1String("tty>") + file.fileName() + QLatin1String(";exec<&-;exec>&-;while :;do sleep 3600;done")}); 0313 } else if (appName == QLatin1String("xfce4-terminal")) { 0314 m_externalTerminal->start(appName, QStringList{ 0315 QStringLiteral("-e"), 0316 QLatin1String("sh -c \"tty>") + file.fileName() + QLatin1String(";\"\"<&\\-\"\">&\\-;\"\"while :;\"\"do sleep 3600;\"\"done\"")}); 0317 } else { 0318 m_externalTerminal->start(appName, QStringList{ 0319 QStringLiteral("-e"), 0320 QLatin1String("sh -c \"tty>") + file.fileName() + QLatin1String(";exec<&-;exec>&-;while :;do sleep 3600;done\"")}); 0321 } 0322 0323 if (!m_externalTerminal->waitForStarted(500)) { 0324 m_lastError = QLatin1String("Can't run terminal: ") + appName; 0325 m_externalTerminal->terminate(); 0326 return false; 0327 } 0328 0329 for (int i = 0; i < 800; i++) { 0330 if (!file.bytesAvailable()) { 0331 if (m_externalTerminal->state() == QProcess::NotRunning && m_externalTerminal->exitCode()) { 0332 break; 0333 } 0334 QCoreApplication::processEvents(QEventLoop::AllEvents, 100); 0335 usleep(8000); 0336 } else { 0337 qCDebug(DEBUGGERCOMMON) << "Received terminal output(tty)"; 0338 break; 0339 } 0340 } 0341 0342 usleep(1000); 0343 ttySlave = QString::fromUtf8(file.readAll().trimmed()); 0344 0345 file.close(); 0346 0347 if (ttySlave.isEmpty()) { 0348 m_lastError = i18n("Can't receive %1 tty/pty. Check that %1 is actually a terminal and that it accepts these arguments: -e sh -c \"tty> %2 ;exec<&-;exec>&-;while :;do sleep 3600;done\"", appName, file.fileName()); 0349 } 0350 #endif 0351 return true; 0352 } 0353 // ************************************************************************** 0354 0355 #include "moc_stty.cpp"