process.cpp
00001 /* vi: ts=8 sts=4 sw=4 00002 * 00003 * $Id$ 00004 * 00005 * This file is part of the KDE project, module tdesu. 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 * process.cpp: Functionality to build a front end to password asking 00016 * terminal programs. 00017 */ 00018 00019 #include <config.h> 00020 00021 #include <stdio.h> 00022 #include <stdlib.h> 00023 #include <unistd.h> 00024 #include <fcntl.h> 00025 #include <signal.h> 00026 #include <errno.h> 00027 #include <string.h> 00028 #include <termios.h> 00029 #include <signal.h> 00030 00031 #include <sys/types.h> 00032 #include <sys/wait.h> 00033 #include <sys/stat.h> 00034 #include <sys/time.h> 00035 #include <sys/resource.h> 00036 #include <sys/ioctl.h> 00037 00038 #if defined(__SVR4) && defined(sun) 00039 #include <stropts.h> 00040 #include <sys/stream.h> 00041 #endif 00042 00043 #ifdef HAVE_SYS_SELECT_H 00044 #include <sys/select.h> // Needed on some systems. 00045 #endif 00046 00047 #include <tqglobal.h> 00048 #include <tqcstring.h> 00049 #include <tqfile.h> 00050 00051 #include <tdeconfig.h> 00052 #include <kdebug.h> 00053 #include <kstandarddirs.h> 00054 00055 #include "process.h" 00056 #include "tdesu_pty.h" 00057 #include "kcookie.h" 00058 00059 int PtyProcess::waitMS(int fd,int ms) 00060 { 00061 struct timeval tv; 00062 tv.tv_sec = 0; 00063 tv.tv_usec = 1000*ms; 00064 00065 fd_set fds; 00066 FD_ZERO(&fds); 00067 FD_SET(fd,&fds); 00068 return select(fd+1, &fds, 0L, 0L, &tv); 00069 } 00070 00071 /* 00072 ** Basic check for the existence of @p pid. 00073 ** Returns true iff @p pid is an extant process. 00074 */ 00075 bool PtyProcess::checkPid(pid_t pid) 00076 { 00077 TDEConfig* config = TDEGlobal::config(); 00078 config->setGroup("super-user-command"); 00079 TQString superUserCommand = config->readEntry("super-user-command", DEFAULT_SUPER_USER_COMMAND); 00080 //sudo does not accept signals from user so we except it 00081 if (superUserCommand == "sudo") { 00082 return true; 00083 } else { 00084 return kill(pid,0) == 0; 00085 } 00086 } 00087 00088 /* 00089 ** Check process exit status for process @p pid. 00090 ** On error (no child, no exit), return Error (-1). 00091 ** If child @p pid has exited, return its exit status, 00092 ** (which may be zero). 00093 ** If child @p has not exited, return NotExited (-2). 00094 */ 00095 00096 int PtyProcess::checkPidExited(pid_t pid) 00097 { 00098 int state, ret; 00099 ret = waitpid(pid, &state, WNOHANG); 00100 00101 if (ret < 0) 00102 { 00103 kdError(900) << k_lineinfo << "waitpid(): " << perror << "\n"; 00104 return Error; 00105 } 00106 if (ret == pid) 00107 { 00108 if (WIFEXITED(state)) 00109 return WEXITSTATUS(state); 00110 return Killed; 00111 } 00112 00113 return NotExited; 00114 } 00115 00116 00117 class PtyProcess::PtyProcessPrivate 00118 { 00119 public: 00120 QCStringList env; 00121 }; 00122 00123 00124 PtyProcess::PtyProcess() 00125 { 00126 m_bTerminal = false; 00127 m_bErase = false; 00128 m_pPTY = 0L; 00129 d = new PtyProcessPrivate; 00130 } 00131 00132 00133 int PtyProcess::init() 00134 { 00135 delete m_pPTY; 00136 m_pPTY = new PTY(); 00137 m_Fd = m_pPTY->getpt(); 00138 if (m_Fd < 0) 00139 return -1; 00140 if ((m_pPTY->grantpt() < 0) || (m_pPTY->unlockpt() < 0)) 00141 { 00142 kdError(900) << k_lineinfo << "Master setup failed.\n"; 00143 m_Fd = -1; 00144 return -1; 00145 } 00146 m_TTY = m_pPTY->ptsname(); 00147 m_Inbuf.resize(0); 00148 return 0; 00149 } 00150 00151 00152 PtyProcess::~PtyProcess() 00153 { 00154 delete m_pPTY; 00155 delete d; 00156 } 00157 00159 void PtyProcess::setEnvironment( const QCStringList &env ) 00160 { 00161 d->env = env; 00162 } 00163 00164 const QCStringList& PtyProcess::environment() const 00165 { 00166 return d->env; 00167 } 00168 00169 /* 00170 * Read one line of input. The terminal is in canonical mode, so you always 00171 * read a line at at time, but it's possible to receive multiple lines in 00172 * one time. 00173 */ 00174 00175 TQCString PtyProcess::readLine(bool block) 00176 { 00177 int pos; 00178 TQCString ret; 00179 00180 if (!m_Inbuf.isEmpty()) 00181 { 00182 pos = m_Inbuf.find('\n'); 00183 if (pos == -1) 00184 { 00185 ret = m_Inbuf; 00186 m_Inbuf.resize(0); 00187 } else 00188 { 00189 ret = m_Inbuf.left(pos); 00190 m_Inbuf = m_Inbuf.mid(pos+1); 00191 } 00192 return ret; 00193 } 00194 00195 int flags = fcntl(m_Fd, F_GETFL); 00196 if (flags < 0) 00197 { 00198 kdError(900) << k_lineinfo << "fcntl(F_GETFL): " << perror << "\n"; 00199 return ret; 00200 } 00201 int oflags = flags; 00202 if (block) 00203 flags &= ~O_NONBLOCK; 00204 else 00205 flags |= O_NONBLOCK; 00206 00207 if ((flags != oflags) && (fcntl(m_Fd, F_SETFL, flags) < 0)) 00208 { 00209 // We get an error here when the child process has closed 00210 // the file descriptor already. 00211 return ret; 00212 } 00213 00214 int nbytes; 00215 char buf[256]; 00216 while (1) 00217 { 00218 nbytes = read(m_Fd, buf, 255); 00219 if (nbytes == -1) 00220 { 00221 if (errno == EINTR) 00222 continue; 00223 else break; 00224 } 00225 if (nbytes == 0) 00226 break; // eof 00227 00228 buf[nbytes] = '\000'; 00229 m_Inbuf += buf; 00230 00231 pos = m_Inbuf.find('\n'); 00232 if (pos == -1) 00233 { 00234 ret = m_Inbuf; 00235 m_Inbuf.resize(0); 00236 } else 00237 { 00238 ret = m_Inbuf.left(pos); 00239 m_Inbuf = m_Inbuf.mid(pos+1); 00240 } 00241 break; 00242 } 00243 00244 return ret; 00245 } 00246 00247 TQCString PtyProcess::readAll(bool block) 00248 { 00249 TQCString ret; 00250 00251 if (!m_Inbuf.isEmpty()) 00252 { 00253 // if there is still something in the buffer, we need not block. 00254 // we should still try to read any further output, from the fd, though. 00255 block = false; 00256 ret = m_Inbuf; 00257 m_Inbuf.resize(0); 00258 } 00259 00260 int flags = fcntl(m_Fd, F_GETFL); 00261 if (flags < 0) 00262 { 00263 kdError(900) << k_lineinfo << "fcntl(F_GETFL): " << perror << "\n"; 00264 return ret; 00265 } 00266 int oflags = flags; 00267 if (block) 00268 flags &= ~O_NONBLOCK; 00269 else 00270 flags |= O_NONBLOCK; 00271 00272 if ((flags != oflags) && (fcntl(m_Fd, F_SETFL, flags) < 0)) 00273 { 00274 // We get an error here when the child process has closed 00275 // the file descriptor already. 00276 return ret; 00277 } 00278 00279 int nbytes; 00280 char buf[256]; 00281 while (1) 00282 { 00283 nbytes = read(m_Fd, buf, 255); 00284 if (nbytes == -1) 00285 { 00286 if (errno == EINTR) 00287 continue; 00288 else break; 00289 } 00290 if (nbytes == 0) 00291 break; // eof 00292 00293 buf[nbytes] = '\000'; 00294 ret += buf; 00295 break; 00296 } 00297 00298 return ret; 00299 } 00300 00301 00302 void PtyProcess::writeLine(const TQCString &line, bool addnl) 00303 { 00304 if (!line.isEmpty()) 00305 write(m_Fd, line, line.length()); 00306 if (addnl) 00307 write(m_Fd, "\n", 1); 00308 } 00309 00310 00311 void PtyProcess::unreadLine(const TQCString &line, bool addnl) 00312 { 00313 TQCString tmp = line; 00314 if (addnl) 00315 tmp += '\n'; 00316 if (!tmp.isEmpty()) 00317 m_Inbuf.prepend(tmp); 00318 } 00319 00320 /* 00321 * Fork and execute the command. This returns in the parent. 00322 */ 00323 00324 int PtyProcess::exec(const TQCString &command, const QCStringList &args) 00325 { 00326 kdDebug(900) << k_lineinfo << "Running `" << command << "'\n"; 00327 00328 if (init() < 0) 00329 return -1; 00330 00331 // Open the pty slave before forking. See SetupTTY() 00332 int slave = open(m_TTY, O_RDWR); 00333 if (slave < 0) 00334 { 00335 kdError(900) << k_lineinfo << "Could not open slave pty.\n"; 00336 return -1; 00337 } 00338 00339 if ((m_Pid = fork()) == -1) 00340 { 00341 kdError(900) << k_lineinfo << "fork(): " << perror << "\n"; 00342 return -1; 00343 } 00344 00345 // Parent 00346 if (m_Pid) 00347 { 00348 close(slave); 00349 return 0; 00350 } 00351 00352 // Child 00353 if (SetupTTY(slave) < 0) 00354 _exit(1); 00355 00356 for(QCStringList::ConstIterator it = d->env.begin(); 00357 it != d->env.end(); it++) 00358 { 00359 putenv(const_cast<TQCString&>(*it).data()); 00360 } 00361 unsetenv("TDE_FULL_SESSION"); 00362 00363 // set temporarily LC_ALL to C, for su (to be able to parse "Password:") 00364 const char* old_lc_all = getenv( "LC_ALL" ); 00365 if( old_lc_all != NULL ) 00366 setenv( "TDESU_LC_ALL", old_lc_all, 1 ); 00367 else 00368 unsetenv( "TDESU_LC_ALL" ); 00369 setenv("LC_ALL", "C", 1); 00370 00371 // From now on, terminal output goes through the tty. 00372 00373 TQCString path; 00374 if (command.contains('/')) 00375 path = command; 00376 else 00377 { 00378 TQString file = TDEStandardDirs::findExe(command); 00379 if (file.isEmpty()) 00380 { 00381 kdError(900) << k_lineinfo << command << " not found\n"; 00382 _exit(1); 00383 } 00384 path = TQFile::encodeName(file); 00385 } 00386 00387 const char **argp = (const char **)malloc((args.count()+2)*sizeof(char *)); 00388 int i = 0; 00389 argp[i++] = path; 00390 for (QCStringList::ConstIterator it=args.begin(); it!=args.end(); ++it) 00391 argp[i++] = *it; 00392 00393 argp[i] = 0L; 00394 00395 execv(path, (char * const *)argp); 00396 kdError(900) << k_lineinfo << "execv(\"" << path << "\"): " << perror << "\n"; 00397 _exit(1); 00398 return -1; // Shut up compiler. Never reached. 00399 } 00400 00401 00402 /* 00403 * Wait until the terminal is set into no echo mode. At least one su 00404 * (RH6 w/ Linux-PAM patches) sets noecho mode AFTER writing the Password: 00405 * prompt, using TCSAFLUSH. This flushes the terminal I/O queues, possibly 00406 * taking the password with it. So we wait until no echo mode is set 00407 * before writing the password. 00408 * Note that this is done on the slave fd. While Linux allows tcgetattr() on 00409 * the master side, Solaris doesn't. 00410 */ 00411 00412 int PtyProcess::WaitSlave() 00413 { 00414 int slave = open(m_TTY, O_RDWR); 00415 if (slave < 0) 00416 { 00417 kdError(900) << k_lineinfo << "Could not open slave tty.\n"; 00418 return -1; 00419 } 00420 00421 kdDebug(900) << k_lineinfo << "Child pid " << m_Pid << endl; 00422 00423 struct termios tio; 00424 while (1) 00425 { 00426 if (!checkPid(m_Pid)) 00427 { 00428 close(slave); 00429 return -1; 00430 } 00431 if (tcgetattr(slave, &tio) < 0) 00432 { 00433 kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n"; 00434 close(slave); 00435 return -1; 00436 } 00437 if (tio.c_lflag & ECHO) 00438 { 00439 kdDebug(900) << k_lineinfo << "Echo mode still on.\n"; 00440 waitMS(slave,100); 00441 continue; 00442 } 00443 break; 00444 } 00445 close(slave); 00446 return 0; 00447 } 00448 00449 00450 int PtyProcess::enableLocalEcho(bool enable) 00451 { 00452 int slave = open(m_TTY, O_RDWR); 00453 if (slave < 0) 00454 { 00455 kdError(900) << k_lineinfo << "Could not open slave tty.\n"; 00456 return -1; 00457 } 00458 struct termios tio; 00459 if (tcgetattr(slave, &tio) < 0) 00460 { 00461 kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n"; 00462 close(slave); return -1; 00463 } 00464 if (enable) 00465 tio.c_lflag |= ECHO; 00466 else 00467 tio.c_lflag &= ~ECHO; 00468 if (tcsetattr(slave, TCSANOW, &tio) < 0) 00469 { 00470 kdError(900) << k_lineinfo << "tcsetattr(): " << perror << "\n"; 00471 close(slave); return -1; 00472 } 00473 close(slave); 00474 return 0; 00475 } 00476 00477 00478 /* 00479 * Copy output to stdout until the child process exists, or a line of output 00480 * matches `m_Exit'. 00481 * We have to use waitpid() to test for exit. Merely waiting for EOF on the 00482 * pty does not work, because the target process may have children still 00483 * attached to the terminal. 00484 */ 00485 00486 int PtyProcess::waitForChild() 00487 { 00488 int retval = 1; 00489 00490 fd_set fds; 00491 FD_ZERO(&fds); 00492 00493 while (1) 00494 { 00495 int ret = 0; 00496 00497 if (m_Fd != -1) 00498 { 00499 FD_SET(m_Fd, &fds); 00500 ret = select(m_Fd+1, &fds, 0L, 0L, 0L); 00501 } 00502 if (ret == -1) 00503 { 00504 if (errno != EINTR) 00505 { 00506 kdError(900) << k_lineinfo << "select(): " << perror << "\n"; 00507 return -1; 00508 } 00509 ret = 0; 00510 } 00511 00512 if (ret) 00513 { 00514 TQCString output = readAll(false); 00515 bool lineStart = true; 00516 while (!output.isNull()) 00517 { 00518 if (!m_Exit.isEmpty()) 00519 { 00520 // match exit string only at line starts 00521 int pos = output.find(m_Exit.data()); 00522 if ((pos >= 0) && ((pos == 0 && lineStart) || (output.at (pos - 1) == '\n'))) 00523 { 00524 kill(m_Pid, SIGTERM); 00525 } 00526 } 00527 if (m_bTerminal) 00528 { 00529 fputs(output, stdout); 00530 fflush(stdout); 00531 } 00532 lineStart = output.at( output.length() - 1 ) == '\n'; 00533 output = readAll(false); 00534 } 00535 } 00536 00537 ret = checkPidExited(m_Pid); 00538 if (ret == Error) 00539 { 00540 if (errno == ECHILD) retval = 0; 00541 else retval = 1; 00542 break; 00543 } 00544 else if (ret == Killed) 00545 { 00546 retval = 0; 00547 break; 00548 } 00549 else if (ret == NotExited) 00550 { 00551 // keep checking 00552 } 00553 else 00554 { 00555 retval = ret; 00556 break; 00557 } 00558 } 00559 return retval; 00560 } 00561 00562 /* 00563 * SetupTTY: Creates a new session. The filedescriptor "fd" should be 00564 * connected to the tty. It is closed after the tty is reopened to make it 00565 * our controlling terminal. This way the tty is always opened at least once 00566 * so we'll never get EIO when reading from it. 00567 */ 00568 00569 int PtyProcess::SetupTTY(int fd) 00570 { 00571 // Reset signal handlers 00572 for (int sig = 1; sig < NSIG; sig++) 00573 signal(sig, SIG_DFL); 00574 signal(SIGHUP, SIG_IGN); 00575 00576 // Close all file handles 00577 struct rlimit rlp; 00578 getrlimit(RLIMIT_NOFILE, &rlp); 00579 for (int i = 0; i < (int)rlp.rlim_cur; i++) 00580 if (i != fd) close(i); 00581 00582 // Create a new session. 00583 setsid(); 00584 00585 // Open slave. This will make it our controlling terminal 00586 int slave = open(m_TTY, O_RDWR); 00587 if (slave < 0) 00588 { 00589 kdError(900) << k_lineinfo << "Could not open slave side: " << perror << "\n"; 00590 return -1; 00591 } 00592 close(fd); 00593 00594 #if defined(__SVR4) && defined(sun) 00595 00596 // Solaris STREAMS environment. 00597 // Push these modules to make the stream look like a terminal. 00598 ioctl(slave, I_PUSH, "ptem"); 00599 ioctl(slave, I_PUSH, "ldterm"); 00600 00601 #endif 00602 00603 #ifdef TIOCSCTTY 00604 ioctl(slave, TIOCSCTTY, NULL); 00605 #endif 00606 00607 // Connect stdin, stdout and stderr 00608 dup2(slave, 0); dup2(slave, 1); dup2(slave, 2); 00609 if (slave > 2) 00610 close(slave); 00611 00612 // Disable OPOST processing. Otherwise, '\n' are (on Linux at least) 00613 // translated to '\r\n'. 00614 struct termios tio; 00615 if (tcgetattr(0, &tio) < 0) 00616 { 00617 kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n"; 00618 return -1; 00619 } 00620 tio.c_oflag &= ~OPOST; 00621 if (tcsetattr(0, TCSANOW, &tio) < 0) 00622 { 00623 kdError(900) << k_lineinfo << "tcsetattr(): " << perror << "\n"; 00624 return -1; 00625 } 00626 00627 return 0; 00628 } 00629 00630 void PtyProcess::virtual_hook( int, void* ) 00631 { /*BASE::virtual_hook( id, data );*/ }