00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include "process.h"
00018 #include "kcookie.h"
00019
00020 #include <config.h>
00021
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <unistd.h>
00025 #include <fcntl.h>
00026 #include <signal.h>
00027 #include <errno.h>
00028 #include <string.h>
00029 #include <termios.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
00037 #ifdef HAVE_SYS_SELECT_H
00038 #include <sys/select.h>
00039 #endif
00040
00041 #include <QtCore/QBool>
00042 #include <QtCore/QFile>
00043
00044 #include <ksharedconfig.h>
00045 #include <kconfiggroup.h>
00046 #include <kdebug.h>
00047 #include <kstandarddirs.h>
00048 #include <kde_file.h>
00049
00050
00051 namespace KDESu {
00052
00053 using namespace KDESuPrivate;
00054
00055
00056
00057
00058
00059
00060
00061 int PtyProcess::waitMS(int fd,int ms)
00062 {
00063 struct timeval tv;
00064 tv.tv_sec = 0;
00065 tv.tv_usec = 1000*ms;
00066
00067 fd_set fds;
00068 FD_ZERO(&fds);
00069 FD_SET(fd,&fds);
00070 return select(fd+1, &fds, 0L, 0L, &tv);
00071 }
00072
00073
00074
00075
00076
00077
00078
00079
00080 bool PtyProcess::checkPid(pid_t pid)
00081 {
00082 KSharedConfig::Ptr config = KGlobal::config();
00083 KConfigGroup cg(config, "super-user-command");
00084 QString superUserCommand = cg.readEntry("super-user-command", "sudo");
00085
00086 if (superUserCommand == "sudo") {
00087 return true;
00088 } else {
00089 return kill(pid, 0) == 0;
00090 }
00091 }
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101 int PtyProcess::checkPidExited(pid_t pid)
00102 {
00103 int state, ret;
00104 ret = waitpid(pid, &state, WNOHANG);
00105
00106 if (ret < 0)
00107 {
00108 kError(900) << k_lineinfo << "waitpid(): " << perror << "\n";
00109 return Error;
00110 }
00111 if (ret == pid)
00112 {
00113 if (WIFEXITED(state))
00114 return WEXITSTATUS(state);
00115 return Killed;
00116 }
00117
00118 return NotExited;
00119 }
00120
00121
00122 class PtyProcess::PtyProcessPrivate
00123 {
00124 public:
00125 PtyProcessPrivate() : m_pPTY(0L) {}
00126 ~PtyProcessPrivate()
00127 {
00128 delete m_pPTY;
00129 }
00130 QList<QByteArray> env;
00131 KPty *m_pPTY;
00132 QByteArray m_Inbuf;
00133 };
00134
00135
00136 PtyProcess::PtyProcess()
00137 :d(new PtyProcessPrivate)
00138 {
00139 m_bTerminal = false;
00140 m_bErase = false;
00141 }
00142
00143
00144 int PtyProcess::init()
00145 {
00146 delete d->m_pPTY;
00147 d->m_pPTY = new KPty();
00148 if (!d->m_pPTY->open())
00149 {
00150 kError(900) << k_lineinfo << "Failed to open PTY.\n";
00151 return -1;
00152 }
00153 d->m_Inbuf.resize(0);
00154 return 0;
00155 }
00156
00157
00158 PtyProcess::~PtyProcess()
00159 {
00160 delete d;
00161 }
00162
00164 void PtyProcess::setEnvironment( const QList<QByteArray> &env )
00165 {
00166 d->env = env;
00167 }
00168
00169 int PtyProcess::fd() const
00170 {
00171 return d->m_pPTY ? d->m_pPTY->masterFd() : -1;
00172 }
00173
00174 int PtyProcess::pid() const
00175 {
00176 return m_Pid;
00177 }
00178
00180 QList<QByteArray> PtyProcess::environment() const
00181 {
00182 return d->env;
00183 }
00184
00185
00186 QByteArray PtyProcess::readAll(bool block)
00187 {
00188 QByteArray ret;
00189 if (!d->m_Inbuf.isEmpty())
00190 {
00191
00192
00193 block = false;
00194 ret = d->m_Inbuf;
00195 d->m_Inbuf.resize(0);
00196 }
00197
00198 int flags = fcntl(fd(), F_GETFL);
00199 if (flags < 0)
00200 {
00201 kError(900) << k_lineinfo << "fcntl(F_GETFL): " << perror << "\n";
00202 return ret;
00203 }
00204 int oflags = flags;
00205 if (block)
00206 flags &= ~O_NONBLOCK;
00207 else
00208 flags |= O_NONBLOCK;
00209
00210 if ((flags != oflags) && (fcntl(fd(), F_SETFL, flags) < 0))
00211 {
00212
00213
00214 return ret;
00215 }
00216
00217 int nbytes;
00218 char buf[256];
00219 while (1)
00220 {
00221 nbytes = read(fd(), buf, 255);
00222 if (nbytes == -1)
00223 {
00224 if (errno == EINTR)
00225 continue;
00226 else break;
00227 }
00228 if (nbytes == 0)
00229 break;
00230
00231 buf[nbytes] = '\000';
00232 ret += buf;
00233 break;
00234 }
00235
00236 return ret;
00237 }
00238
00239
00240 QByteArray PtyProcess::readLine(bool block)
00241 {
00242 d->m_Inbuf = readAll(block);
00243
00244 int pos;
00245 QByteArray ret;
00246 if (!d->m_Inbuf.isEmpty())
00247 {
00248 pos = d->m_Inbuf.indexOf('\n');
00249 if (pos == -1)
00250 {
00251
00252 ret = d->m_Inbuf;
00253 d->m_Inbuf.resize(0);
00254 } else
00255 {
00256 ret = d->m_Inbuf.left(pos);
00257 d->m_Inbuf = d->m_Inbuf.mid(pos+1);
00258 }
00259 }
00260
00261 return ret;
00262 }
00263
00264
00265 void PtyProcess::writeLine(const QByteArray &line, bool addnl)
00266 {
00267 if (!line.isEmpty())
00268 write(fd(), line, line.length());
00269 if (addnl)
00270 write(fd(), "\n", 1);
00271 }
00272
00273
00274 void PtyProcess::unreadLine(const QByteArray &line, bool addnl)
00275 {
00276 QByteArray tmp = line;
00277 if (addnl)
00278 tmp += '\n';
00279 if (!tmp.isEmpty())
00280 d->m_Inbuf.prepend(tmp);
00281 }
00282
00283 void PtyProcess::setExitString(const QByteArray &exit)
00284 {
00285 m_Exit = exit;
00286 }
00287
00288
00289
00290
00291
00292 int PtyProcess::exec(const QByteArray &command, const QList<QByteArray> &args)
00293 {
00294 kDebug(900) << k_lineinfo << "Running `" << command << "'\n";
00295 int i;
00296
00297 if (init() < 0)
00298 return -1;
00299
00300 if ((m_Pid = fork()) == -1)
00301 {
00302 kError(900) << k_lineinfo << "fork(): " << perror << "\n";
00303 return -1;
00304 }
00305
00306
00307 if (m_Pid)
00308 {
00309 d->m_pPTY->closeSlave();
00310 return 0;
00311 }
00312
00313
00314 if (setupTTY() < 0)
00315 _exit(1);
00316
00317 for (i = 0; i < d->env.count(); ++i)
00318 {
00319 putenv(const_cast<char *>(d->env.at(i).constData()));
00320 }
00321 unsetenv("KDE_FULL_SESSION");
00322
00323 unsetenv("SESSION_MANAGER");
00324
00325
00326 unsetenv("DBUS_SESSION_BUS_ADDRESS");
00327
00328
00329 const QByteArray old_lc_all = qgetenv( "LC_ALL" );
00330 if( !old_lc_all.isEmpty() )
00331 qputenv( "KDESU_LC_ALL", old_lc_all );
00332 else
00333 unsetenv( "KDESU_LC_ALL" );
00334 qputenv("LC_ALL", "C");
00335
00336
00337
00338 QByteArray path;
00339 if (command.contains('/'))
00340 path = command;
00341 else
00342 {
00343 QString file = KStandardDirs::findExe(command);
00344 if (file.isEmpty())
00345 {
00346 kError(900) << k_lineinfo << command << " not found\n";
00347 _exit(1);
00348 }
00349 path = QFile::encodeName(file);
00350 }
00351
00352 const char **argp = (const char **)malloc((args.count()+2)*sizeof(char *));
00353
00354 i = 0;
00355 argp[i++] = path;
00356 for (QList<QByteArray>::ConstIterator it=args.begin(); it!=args.end(); ++it, ++i)
00357 argp[i] = *it;
00358
00359 argp[i] = NULL;
00360
00361 execv(path, const_cast<char **>(argp));
00362 kError(900) << k_lineinfo << "execv(\"" << path << "\"): " << perror << "\n";
00363 _exit(1);
00364 return -1;
00365 }
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378 int PtyProcess::WaitSlave()
00379 {
00380 kDebug(900) << k_lineinfo << "Child pid " << m_Pid;
00381
00382 struct termios tio;
00383 while (1)
00384 {
00385 if (!checkPid(m_Pid))
00386 {
00387 return -1;
00388 }
00389 if (!d->m_pPTY->tcGetAttr(&tio))
00390 {
00391 kError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n";
00392 return -1;
00393 }
00394 if (tio.c_lflag & ECHO)
00395 {
00396 kDebug(900) << k_lineinfo << "Echo mode still on.\n";
00397 usleep(10000);
00398 continue;
00399 }
00400 break;
00401 }
00402 return 0;
00403 }
00404
00405
00406 int PtyProcess::enableLocalEcho(bool enable)
00407 {
00408 return d->m_pPTY->setEcho(enable) ? 0 : -1;
00409 }
00410
00411
00412 void PtyProcess::setTerminal(bool terminal)
00413 {
00414 m_bTerminal = terminal;
00415 }
00416
00417 void PtyProcess::setErase(bool erase)
00418 {
00419 m_bErase = erase;
00420 }
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430 int PtyProcess::waitForChild()
00431 {
00432 fd_set fds;
00433 FD_ZERO(&fds);
00434
00435 while (1)
00436 {
00437 FD_SET(fd(), &fds);
00438
00439
00440
00441
00442
00443 timeval timeout;
00444 timeout.tv_sec = 0;
00445 timeout.tv_usec = 100000;
00446 int ret = select(fd()+1, &fds, 0L, 0L, &timeout);
00447 if (ret == -1)
00448 {
00449 if (errno != EINTR)
00450 {
00451 kError(900) << k_lineinfo << "select(): " << perror << "\n";
00452 return -1;
00453 }
00454 ret = 0;
00455 }
00456
00457 if (ret)
00458 {
00459 QByteArray output = readAll(false);
00460 bool lineStart = true;
00461 while (!output.isNull())
00462 {
00463 if (!m_Exit.isEmpty())
00464 {
00465
00466 int pos = output.indexOf(m_Exit);
00467 if ((pos >= 0) && ((pos == 0 && lineStart) || (output.at (pos - 1) == '\n')))
00468 {
00469 kill(m_Pid, SIGTERM);
00470 }
00471 }
00472 if (m_bTerminal)
00473 {
00474 fputs(output, stdout);
00475 fflush(stdout);
00476 }
00477 lineStart = output.endsWith( '\n' );
00478 output = readAll(false);
00479 }
00480 }
00481
00482 ret = checkPidExited(m_Pid);
00483 if (ret == Error)
00484 {
00485 if (errno == ECHILD) return 0;
00486 else return 1;
00487 }
00488 else if (ret == Killed)
00489 {
00490 return 0;
00491 }
00492 else if (ret == NotExited)
00493 {
00494
00495 }
00496 else
00497 {
00498 return ret;
00499 }
00500 }
00501 }
00502
00503
00504
00505
00506
00507
00508
00509
00510 int PtyProcess::setupTTY()
00511 {
00512
00513 for (int sig = 1; sig < NSIG; sig++)
00514 KDE_signal(sig, SIG_DFL);
00515 KDE_signal(SIGHUP, SIG_IGN);
00516
00517 d->m_pPTY->setCTty();
00518
00519
00520 int slave = d->m_pPTY->slaveFd();
00521 dup2(slave, 0); dup2(slave, 1); dup2(slave, 2);
00522
00523
00524
00525
00526 struct rlimit rlp;
00527 getrlimit(RLIMIT_NOFILE, &rlp);
00528 for (int i = 3; i < (int)rlp.rlim_cur; i++)
00529 close(i);
00530
00531
00532
00533 struct ::termios tio;
00534 if (tcgetattr(0, &tio) < 0)
00535 {
00536 kError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n";
00537 return -1;
00538 }
00539 tio.c_oflag &= ~OPOST;
00540 if (tcsetattr(0, TCSANOW, &tio) < 0)
00541 {
00542 kError(900) << k_lineinfo << "tcsetattr(): " << perror << "\n";
00543 return -1;
00544 }
00545
00546 return 0;
00547 }
00548
00549 void PtyProcess::virtual_hook( int, void* )
00550 { }
00551
00552 }