00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
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>
00045 #endif
00046
00047 #include <qglobal.h>
00048 #include <qcstring.h>
00049 #include <qfile.h>
00050
00051 #include <kconfig.h>
00052 #include <kdebug.h>
00053 #include <kstandarddirs.h>
00054
00055 #include "process.h"
00056 #include "kdesu_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
00073
00074
00075 bool PtyProcess::checkPid(pid_t pid)
00076 {
00077 KConfig* config = KGlobal::config();
00078 config->setGroup("super-user-command");
00079 QString superUserCommand = config->readEntry("super-user-command", "sudo");
00080
00081 if (superUserCommand == "sudo") {
00082 return true;
00083 } else {
00084 return kill(pid,0) == 0;
00085 }
00086 }
00087
00088
00089
00090
00091
00092
00093
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
00171
00172
00173
00174
00175 QCString PtyProcess::readLine(bool block)
00176 {
00177 int pos;
00178 QCString 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
00210
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;
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
00248 void PtyProcess::writeLine(const QCString &line, bool addnl)
00249 {
00250 if (!line.isEmpty())
00251 write(m_Fd, line, line.length());
00252 if (addnl)
00253 write(m_Fd, "\n", 1);
00254 }
00255
00256
00257 void PtyProcess::unreadLine(const QCString &line, bool addnl)
00258 {
00259 QCString tmp = line;
00260 if (addnl)
00261 tmp += '\n';
00262 if (!tmp.isEmpty())
00263 m_Inbuf.prepend(tmp);
00264 }
00265
00266
00267
00268
00269
00270 int PtyProcess::exec(const QCString &command, const QCStringList &args)
00271 {
00272 kdDebug(900) << k_lineinfo << "Running `" << command << "'\n";
00273
00274 if (init() < 0)
00275 return -1;
00276
00277
00278 int slave = open(m_TTY, O_RDWR);
00279 if (slave < 0)
00280 {
00281 kdError(900) << k_lineinfo << "Could not open slave pty.\n";
00282 return -1;
00283 }
00284
00285 if ((m_Pid = fork()) == -1)
00286 {
00287 kdError(900) << k_lineinfo << "fork(): " << perror << "\n";
00288 return -1;
00289 }
00290
00291
00292 if (m_Pid)
00293 {
00294 close(slave);
00295 return 0;
00296 }
00297
00298
00299 if (SetupTTY(slave) < 0)
00300 _exit(1);
00301
00302 for(QCStringList::ConstIterator it = d->env.begin();
00303 it != d->env.end(); it++)
00304 {
00305 putenv((*it).data());
00306 }
00307 unsetenv("KDE_FULL_SESSION");
00308
00309
00310
00311 QCString path;
00312 if (command.contains('/'))
00313 path = command;
00314 else
00315 {
00316 QString file = KStandardDirs::findExe(command);
00317 if (file.isEmpty())
00318 {
00319 kdError(900) << k_lineinfo << command << " not found\n";
00320 _exit(1);
00321 }
00322 path = QFile::encodeName(file);
00323 }
00324
00325 const char **argp = (const char **)malloc((args.count()+2)*sizeof(char *));
00326 int i = 0;
00327 argp[i++] = path;
00328 for (QCStringList::ConstIterator it=args.begin(); it!=args.end(); ++it)
00329 argp[i++] = *it;
00330
00331 argp[i] = 0L;
00332
00333 execv(path, (char * const *)argp);
00334 kdError(900) << k_lineinfo << "execv(\"" << path << "\"): " << perror << "\n";
00335 _exit(1);
00336 return -1;
00337 }
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350 int PtyProcess::WaitSlave()
00351 {
00352 int slave = open(m_TTY, O_RDWR);
00353 if (slave < 0)
00354 {
00355 kdError(900) << k_lineinfo << "Could not open slave tty.\n";
00356 return -1;
00357 }
00358
00359 kdDebug(900) << k_lineinfo << "Child pid " << m_Pid << endl;
00360
00361 struct termios tio;
00362 while (1)
00363 {
00364 if (!checkPid(m_Pid))
00365 {
00366 close(slave);
00367 return -1;
00368 }
00369 if (tcgetattr(slave, &tio) < 0)
00370 {
00371 kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n";
00372 close(slave);
00373 return -1;
00374 }
00375 if (tio.c_lflag & ECHO)
00376 {
00377 kdDebug(900) << k_lineinfo << "Echo mode still on.\n";
00378 waitMS(slave,100);
00379 continue;
00380 }
00381 break;
00382 }
00383 close(slave);
00384 return 0;
00385 }
00386
00387
00388 int PtyProcess::enableLocalEcho(bool enable)
00389 {
00390 int slave = open(m_TTY, O_RDWR);
00391 if (slave < 0)
00392 {
00393 kdError(900) << k_lineinfo << "Could not open slave tty.\n";
00394 return -1;
00395 }
00396 struct termios tio;
00397 if (tcgetattr(slave, &tio) < 0)
00398 {
00399 kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n";
00400 close(slave); return -1;
00401 }
00402 if (enable)
00403 tio.c_lflag |= ECHO;
00404 else
00405 tio.c_lflag &= ~ECHO;
00406 if (tcsetattr(slave, TCSANOW, &tio) < 0)
00407 {
00408 kdError(900) << k_lineinfo << "tcsetattr(): " << perror << "\n";
00409 close(slave); return -1;
00410 }
00411 close(slave);
00412 return 0;
00413 }
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424 int PtyProcess::waitForChild()
00425 {
00426 int retval = 1;
00427
00428 fd_set fds;
00429 FD_ZERO(&fds);
00430
00431 while (1)
00432 {
00433 FD_SET(m_Fd, &fds);
00434 int ret = select(m_Fd+1, &fds, 0L, 0L, 0L);
00435 if (ret == -1)
00436 {
00437 if (errno != EINTR)
00438 {
00439 kdError(900) << k_lineinfo << "select(): " << perror << "\n";
00440 return -1;
00441 }
00442 ret = 0;
00443 }
00444
00445 if (ret)
00446 {
00447 QCString line = readLine(false);
00448 while (!line.isNull())
00449 {
00450 if (!m_Exit.isEmpty() && !qstrnicmp(line, m_Exit, m_Exit.length()))
00451 kill(m_Pid, SIGTERM);
00452 if (m_bTerminal)
00453 {
00454 fputs(line, stdout);
00455 fputc('\n', stdout);
00456 }
00457 line = readLine(false);
00458 }
00459 }
00460
00461 ret = checkPidExited(m_Pid);
00462 if (ret == Error)
00463 {
00464 if (errno == ECHILD) retval = 0;
00465 else retval = 1;
00466 break;
00467 }
00468 else if (ret == Killed)
00469 {
00470 retval = 0;
00471 break;
00472 }
00473 else if (ret == NotExited)
00474 {
00475
00476 }
00477 else
00478 {
00479 retval = ret;
00480 break;
00481 }
00482 }
00483 return retval;
00484 }
00485
00486
00487
00488
00489
00490
00491
00492
00493 int PtyProcess::SetupTTY(int fd)
00494 {
00495
00496 for (int sig = 1; sig < NSIG; sig++)
00497 signal(sig, SIG_DFL);
00498 signal(SIGHUP, SIG_IGN);
00499
00500
00501 struct rlimit rlp;
00502 getrlimit(RLIMIT_NOFILE, &rlp);
00503 for (int i = 0; i < (int)rlp.rlim_cur; i++)
00504 if (i != fd) close(i);
00505
00506
00507 setsid();
00508
00509
00510 int slave = open(m_TTY, O_RDWR);
00511 if (slave < 0)
00512 {
00513 kdError(900) << k_lineinfo << "Could not open slave side: " << perror << "\n";
00514 return -1;
00515 }
00516 close(fd);
00517
00518 #if defined(__SVR4) && defined(sun)
00519
00520
00521
00522 ioctl(slave, I_PUSH, "ptem");
00523 ioctl(slave, I_PUSH, "ldterm");
00524
00525 #endif
00526
00527 #ifdef TIOCSCTTY
00528 ioctl(slave, TIOCSCTTY, NULL);
00529 #endif
00530
00531
00532 dup2(slave, 0); dup2(slave, 1); dup2(slave, 2);
00533 if (slave > 2)
00534 close(slave);
00535
00536
00537
00538 struct termios tio;
00539 if (tcgetattr(0, &tio) < 0)
00540 {
00541 kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n";
00542 return -1;
00543 }
00544 tio.c_oflag &= ~OPOST;
00545 if (tcsetattr(0, TCSANOW, &tio) < 0)
00546 {
00547 kdError(900) << k_lineinfo << "tcsetattr(): " << perror << "\n";
00548 return -1;
00549 }
00550
00551 return 0;
00552 }
00553
00554 void PtyProcess::virtual_hook( int, void* )
00555 { }