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", DEFAULT_SUPER_USER_COMMAND);
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 QCString PtyProcess::readAll(bool block)
00248 {
00249 QCString ret;
00250
00251 if (!m_Inbuf.isEmpty())
00252 {
00253
00254
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
00275
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;
00292
00293 buf[nbytes] = '\000';
00294 ret += buf;
00295 break;
00296 }
00297
00298 return ret;
00299 }
00300
00301
00302 void PtyProcess::writeLine(const QCString &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 QCString &line, bool addnl)
00312 {
00313 QCString tmp = line;
00314 if (addnl)
00315 tmp += '\n';
00316 if (!tmp.isEmpty())
00317 m_Inbuf.prepend(tmp);
00318 }
00319
00320
00321
00322
00323
00324 int PtyProcess::exec(const QCString &command, const QCStringList &args)
00325 {
00326 kdDebug(900) << k_lineinfo << "Running `" << command << "'\n";
00327
00328 if (init() < 0)
00329 return -1;
00330
00331
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
00346 if (m_Pid)
00347 {
00348 close(slave);
00349 return 0;
00350 }
00351
00352
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((*it).data());
00360 }
00361 unsetenv("KDE_FULL_SESSION");
00362
00363
00364 const char* old_lc_all = getenv( "LC_ALL" );
00365 if( old_lc_all != NULL )
00366 setenv( "KDESU_LC_ALL", old_lc_all, 1 );
00367 else
00368 unsetenv( "KDESU_LC_ALL" );
00369 setenv("LC_ALL", "C", 1);
00370
00371
00372
00373 QCString path;
00374 if (command.contains('/'))
00375 path = command;
00376 else
00377 {
00378 QString file = KStandardDirs::findExe(command);
00379 if (file.isEmpty())
00380 {
00381 kdError(900) << k_lineinfo << command << " not found\n";
00382 _exit(1);
00383 }
00384 path = QFile::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;
00399 }
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
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
00480
00481
00482
00483
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 FD_SET(m_Fd, &fds);
00496 int ret = select(m_Fd+1, &fds, 0L, 0L, 0L);
00497 if (ret == -1)
00498 {
00499 if (errno != EINTR)
00500 {
00501 kdError(900) << k_lineinfo << "select(): " << perror << "\n";
00502 return -1;
00503 }
00504 ret = 0;
00505 }
00506
00507 if (ret)
00508 {
00509 QCString output = readAll(false);
00510 bool lineStart = true;
00511 while (!output.isNull())
00512 {
00513 if (!m_Exit.isEmpty())
00514 {
00515
00516 int pos = output.find(m_Exit);
00517 if ((pos >= 0) && ((pos == 0 && lineStart) || (output.at (pos - 1) == '\n')))
00518 {
00519 kill(m_Pid, SIGTERM);
00520 }
00521 }
00522 if (m_bTerminal)
00523 {
00524 fputs(output, stdout);
00525 fflush(stdout);
00526 }
00527 lineStart = output.at( output.length() - 1 ) == '\n';
00528 output = readAll(false);
00529 }
00530 }
00531
00532 ret = checkPidExited(m_Pid);
00533 if (ret == Error)
00534 {
00535 if (errno == ECHILD) retval = 0;
00536 else retval = 1;
00537 break;
00538 }
00539 else if (ret == Killed)
00540 {
00541 retval = 0;
00542 break;
00543 }
00544 else if (ret == NotExited)
00545 {
00546
00547 }
00548 else
00549 {
00550 retval = ret;
00551 break;
00552 }
00553 }
00554 return retval;
00555 }
00556
00557
00558
00559
00560
00561
00562
00563
00564 int PtyProcess::SetupTTY(int fd)
00565 {
00566
00567 for (int sig = 1; sig < NSIG; sig++)
00568 signal(sig, SIG_DFL);
00569 signal(SIGHUP, SIG_IGN);
00570
00571
00572 struct rlimit rlp;
00573 getrlimit(RLIMIT_NOFILE, &rlp);
00574 for (int i = 0; i < (int)rlp.rlim_cur; i++)
00575 if (i != fd) close(i);
00576
00577
00578 setsid();
00579
00580
00581 int slave = open(m_TTY, O_RDWR);
00582 if (slave < 0)
00583 {
00584 kdError(900) << k_lineinfo << "Could not open slave side: " << perror << "\n";
00585 return -1;
00586 }
00587 close(fd);
00588
00589 #if defined(__SVR4) && defined(sun)
00590
00591
00592
00593 ioctl(slave, I_PUSH, "ptem");
00594 ioctl(slave, I_PUSH, "ldterm");
00595
00596 #endif
00597
00598 #ifdef TIOCSCTTY
00599 ioctl(slave, TIOCSCTTY, NULL);
00600 #endif
00601
00602
00603 dup2(slave, 0); dup2(slave, 1); dup2(slave, 2);
00604 if (slave > 2)
00605 close(slave);
00606
00607
00608
00609 struct termios tio;
00610 if (tcgetattr(0, &tio) < 0)
00611 {
00612 kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n";
00613 return -1;
00614 }
00615 tio.c_oflag &= ~OPOST;
00616 if (tcsetattr(0, TCSANOW, &tio) < 0)
00617 {
00618 kdError(900) << k_lineinfo << "tcsetattr(): " << perror << "\n";
00619 return -1;
00620 }
00621
00622 return 0;
00623 }
00624
00625 void PtyProcess::virtual_hook( int, void* )
00626 { }