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
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 const char* old_lc_all = getenv( "LC_ALL" );
00311 if( old_lc_all != NULL )
00312 setenv( "KDESU_LC_ALL", old_lc_all, 1 );
00313 else
00314 unsetenv( "KDESU_LC_ALL" );
00315 setenv("LC_ALL", "C", 1);
00316
00317
00318
00319 QCString path;
00320 if (command.contains('/'))
00321 path = command;
00322 else
00323 {
00324 QString file = KStandardDirs::findExe(command);
00325 if (file.isEmpty())
00326 {
00327 kdError(900) << k_lineinfo << command << " not found\n";
00328 _exit(1);
00329 }
00330 path = QFile::encodeName(file);
00331 }
00332
00333 const char **argp = (const char **)malloc((args.count()+2)*sizeof(char *));
00334 int i = 0;
00335 argp[i++] = path;
00336 for (QCStringList::ConstIterator it=args.begin(); it!=args.end(); ++it)
00337 argp[i++] = *it;
00338
00339 argp[i] = 0L;
00340
00341 execv(path, (char * const *)argp);
00342 kdError(900) << k_lineinfo << "execv(\"" << path << "\"): " << perror << "\n";
00343 _exit(1);
00344 return -1;
00345 }
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358 int PtyProcess::WaitSlave()
00359 {
00360 int slave = open(m_TTY, O_RDWR);
00361 if (slave < 0)
00362 {
00363 kdError(900) << k_lineinfo << "Could not open slave tty.\n";
00364 return -1;
00365 }
00366
00367 kdDebug(900) << k_lineinfo << "Child pid " << m_Pid << endl;
00368
00369 struct termios tio;
00370 while (1)
00371 {
00372 if (!checkPid(m_Pid))
00373 {
00374 close(slave);
00375 return -1;
00376 }
00377 if (tcgetattr(slave, &tio) < 0)
00378 {
00379 kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n";
00380 close(slave);
00381 return -1;
00382 }
00383 if (tio.c_lflag & ECHO)
00384 {
00385 kdDebug(900) << k_lineinfo << "Echo mode still on.\n";
00386 waitMS(slave,100);
00387 continue;
00388 }
00389 break;
00390 }
00391 close(slave);
00392 return 0;
00393 }
00394
00395
00396 int PtyProcess::enableLocalEcho(bool enable)
00397 {
00398 int slave = open(m_TTY, O_RDWR);
00399 if (slave < 0)
00400 {
00401 kdError(900) << k_lineinfo << "Could not open slave tty.\n";
00402 return -1;
00403 }
00404 struct termios tio;
00405 if (tcgetattr(slave, &tio) < 0)
00406 {
00407 kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n";
00408 close(slave); return -1;
00409 }
00410 if (enable)
00411 tio.c_lflag |= ECHO;
00412 else
00413 tio.c_lflag &= ~ECHO;
00414 if (tcsetattr(slave, TCSANOW, &tio) < 0)
00415 {
00416 kdError(900) << k_lineinfo << "tcsetattr(): " << perror << "\n";
00417 close(slave); return -1;
00418 }
00419 close(slave);
00420 return 0;
00421 }
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432 int PtyProcess::waitForChild()
00433 {
00434 int retval = 1;
00435
00436 fd_set fds;
00437 FD_ZERO(&fds);
00438
00439 while (1)
00440 {
00441 FD_SET(m_Fd, &fds);
00442 int ret = select(m_Fd+1, &fds, 0L, 0L, 0L);
00443 if (ret == -1)
00444 {
00445 if (errno != EINTR)
00446 {
00447 kdError(900) << k_lineinfo << "select(): " << perror << "\n";
00448 return -1;
00449 }
00450 ret = 0;
00451 }
00452
00453 if (ret)
00454 {
00455 QCString line = readLine(false);
00456 while (!line.isNull())
00457 {
00458 if (!m_Exit.isEmpty() && !qstrnicmp(line, m_Exit, m_Exit.length()))
00459 kill(m_Pid, SIGTERM);
00460 if (m_bTerminal)
00461 {
00462 fputs(line, stdout);
00463 fputc('\n', stdout);
00464 }
00465 line = readLine(false);
00466 }
00467 }
00468
00469 ret = checkPidExited(m_Pid);
00470 if (ret == Error)
00471 {
00472 if (errno == ECHILD) retval = 0;
00473 else retval = 1;
00474 break;
00475 }
00476 else if (ret == Killed)
00477 {
00478 retval = 0;
00479 break;
00480 }
00481 else if (ret == NotExited)
00482 {
00483
00484 }
00485 else
00486 {
00487 retval = ret;
00488 break;
00489 }
00490 }
00491 return retval;
00492 }
00493
00494
00495
00496
00497
00498
00499
00500
00501 int PtyProcess::SetupTTY(int fd)
00502 {
00503
00504 for (int sig = 1; sig < NSIG; sig++)
00505 signal(sig, SIG_DFL);
00506 signal(SIGHUP, SIG_IGN);
00507
00508
00509 struct rlimit rlp;
00510 getrlimit(RLIMIT_NOFILE, &rlp);
00511 for (int i = 0; i < (int)rlp.rlim_cur; i++)
00512 if (i != fd) close(i);
00513
00514
00515 setsid();
00516
00517
00518 int slave = open(m_TTY, O_RDWR);
00519 if (slave < 0)
00520 {
00521 kdError(900) << k_lineinfo << "Could not open slave side: " << perror << "\n";
00522 return -1;
00523 }
00524 close(fd);
00525
00526 #if defined(__SVR4) && defined(sun)
00527
00528
00529
00530 ioctl(slave, I_PUSH, "ptem");
00531 ioctl(slave, I_PUSH, "ldterm");
00532
00533 #endif
00534
00535 #ifdef TIOCSCTTY
00536 ioctl(slave, TIOCSCTTY, NULL);
00537 #endif
00538
00539
00540 dup2(slave, 0); dup2(slave, 1); dup2(slave, 2);
00541 if (slave > 2)
00542 close(slave);
00543
00544
00545
00546 struct termios tio;
00547 if (tcgetattr(0, &tio) < 0)
00548 {
00549 kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n";
00550 return -1;
00551 }
00552 tio.c_oflag &= ~OPOST;
00553 if (tcsetattr(0, TCSANOW, &tio) < 0)
00554 {
00555 kdError(900) << k_lineinfo << "tcsetattr(): " << perror << "\n";
00556 return -1;
00557 }
00558
00559 return 0;
00560 }
00561
00562 void PtyProcess::virtual_hook( int, void* )
00563 { }