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