00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048 #include "oprocess.h"
00049 #define _MAY_INCLUDE_KPROCESSCONTROLLER_
00050 #include "oprocctrl.h"
00051
00052
00053
00054 #include <qfile.h>
00055 #include <qsocketnotifier.h>
00056
00057 #include <sys/time.h>
00058 #include <sys/types.h>
00059 #include <sys/stat.h>
00060 #include <sys/socket.h>
00061
00062 #include <errno.h>
00063 #include <fcntl.h>
00064 #include <stdlib.h>
00065 #include <signal.h>
00066 #include <stdio.h>
00067 #include <string.h>
00068 #include <unistd.h>
00069 #ifdef HAVE_SYS_SELECT_H
00070 #include <sys/select.h>
00071 #endif
00072 #ifdef HAVE_INITGROUPS
00073 #include <grp.h>
00074 #endif
00075 #include <pwd.h>
00076
00077 #include <qapplication.h>
00078 #include <qmap.h>
00079
00080
00082
00084
00085 class OProcessPrivate {
00086 public:
00087 OProcessPrivate() : useShell(false) { }
00088
00089 bool useShell;
00090 QMap<QString,QString> env;
00091 QString wd;
00092 QCString shell;
00093 };
00094
00095
00096 OProcess::OProcess(QObject *parent, const char *name)
00097 : QObject(parent, name)
00098 {
00099 init ( );
00100 }
00101
00102 OProcess::OProcess(const QString &arg0, QObject *parent, const char *name)
00103 : QObject(parent, name)
00104 {
00105 init ( );
00106 *this << arg0;
00107 }
00108
00109 OProcess::OProcess(const QStringList &args, QObject *parent, const char *name)
00110 : QObject(parent, name)
00111 {
00112 init ( );
00113 *this << args;
00114 }
00115
00116 void OProcess::init ( )
00117 {
00118 run_mode = NotifyOnExit;
00119 runs = false;
00120 pid_ = 0;
00121 status = 0;
00122 keepPrivs = false;
00123 innot = 0;
00124 outnot = 0;
00125 errnot = 0;
00126 communication = NoCommunication;
00127 input_data = 0;
00128 input_sent = 0;
00129 input_total = 0;
00130 d = 0;
00131
00132 if (0 == OProcessController::theOProcessController) {
00133 (void) new OProcessController();
00134 CHECK_PTR(OProcessController::theOProcessController);
00135 }
00136
00137 OProcessController::theOProcessController->addOProcess(this);
00138 out[0] = out[1] = -1;
00139 in[0] = in[1] = -1;
00140 err[0] = err[1] = -1;
00141 }
00142
00143 void
00144 OProcess::setEnvironment(const QString &name, const QString &value)
00145 {
00146 if (!d)
00147 d = new OProcessPrivate;
00148 d->env.insert(name, value);
00149 }
00150
00151 void
00152 OProcess::setWorkingDirectory(const QString &dir)
00153 {
00154 if (!d)
00155 d = new OProcessPrivate;
00156 d->wd = dir;
00157 }
00158
00159 void
00160 OProcess::setupEnvironment()
00161 {
00162 if (d)
00163 {
00164 QMap<QString,QString>::Iterator it;
00165 for(it = d->env.begin(); it != d->env.end(); ++it)
00166 setenv(QFile::encodeName(it.key()).data(),
00167 QFile::encodeName(it.data()).data(), 1);
00168 if (!d->wd.isEmpty())
00169 chdir(QFile::encodeName(d->wd).data());
00170 }
00171 }
00172
00173 void
00174 OProcess::setRunPrivileged(bool keepPrivileges)
00175 {
00176 keepPrivs = keepPrivileges;
00177 }
00178
00179 bool
00180 OProcess::runPrivileged() const
00181 {
00182 return keepPrivs;
00183 }
00184
00185
00186 OProcess::~OProcess()
00187 {
00188
00189
00190
00191
00192
00193 OProcessController::theOProcessController->removeOProcess(this);
00194
00195
00196
00197 if (runs && (run_mode != DontCare))
00198 kill(SIGKILL);
00199
00200
00201 closeStdin();
00202 closeStdout();
00203 closeStderr();
00204
00205
00206 delete d;
00207 }
00208
00209 void OProcess::detach()
00210 {
00211 OProcessController::theOProcessController->removeOProcess(this);
00212
00213 runs = false;
00214 pid_ = 0;
00215
00216
00217 closeStdin();
00218 closeStdout();
00219 closeStderr();
00220 }
00221
00222 bool OProcess::setExecutable(const QString& proc)
00223 {
00224 if (runs) return false;
00225
00226 if (proc.isEmpty()) return false;
00227
00228 if (!arguments.isEmpty())
00229 arguments.remove(arguments.begin());
00230 arguments.prepend(QFile::encodeName(proc));
00231
00232 return true;
00233 }
00234
00235 OProcess &OProcess::operator<<(const QStringList& args)
00236 {
00237 QStringList::ConstIterator it = args.begin();
00238 for ( ; it != args.end() ; ++it )
00239 arguments.append(QFile::encodeName(*it));
00240 return *this;
00241 }
00242
00243 OProcess &OProcess::operator<<(const QCString& arg)
00244 {
00245 return operator<< (arg.data());
00246 }
00247
00248 OProcess &OProcess::operator<<(const char* arg)
00249 {
00250 arguments.append(arg);
00251 return *this;
00252 }
00253
00254 OProcess &OProcess::operator<<(const QString& arg)
00255 {
00256 arguments.append(QFile::encodeName(arg));
00257 return *this;
00258 }
00259
00260 void OProcess::clearArguments()
00261 {
00262 arguments.clear();
00263 }
00264
00265 bool OProcess::start(RunMode runmode, Communication comm)
00266 {
00267 uint i;
00268 uint n = arguments.count();
00269 char **arglist;
00270
00271 if (runs || (0 == n)) {
00272 return false;
00273
00274 }
00275 run_mode = runmode;
00276 status = 0;
00277
00278 QCString shellCmd;
00279 if (d && d->useShell)
00280 {
00281 if (d->shell.isEmpty())
00282 {
00283 qWarning( "Could not find a valid shell" );
00284 return false;
00285 }
00286
00287 arglist = static_cast<char **>(malloc( (4)*sizeof(char *)));
00288 for (i=0; i < n; i++) {
00289 shellCmd += arguments[i];
00290 shellCmd += " ";
00291 }
00292
00293 arglist[0] = d->shell.data();
00294 arglist[1] = (char *) "-c";
00295 arglist[2] = shellCmd.data();
00296 arglist[3] = 0;
00297 }
00298 else
00299 {
00300 arglist = static_cast<char **>(malloc( (n+1)*sizeof(char *)));
00301 for (i=0; i < n; i++)
00302 arglist[i] = arguments[i].data();
00303 arglist[n]= 0;
00304 }
00305
00306 if (!setupCommunication(comm))
00307 qWarning( "Could not setup Communication!");
00308
00309
00310
00311 uid_t uid = getuid();
00312 gid_t gid = getgid();
00313 #ifdef HAVE_INITGROUPS
00314 struct passwd *pw = getpwuid(uid);
00315 #endif
00316
00317 int fd[2];
00318 if (0 > pipe(fd))
00319 {
00320 fd[0] = fd[1] = 0;
00321 }
00322
00323 runs = true;
00324
00325 QApplication::flushX();
00326
00327
00328
00329 pid_ = fork();
00330
00331 if (0 == pid_) {
00332 if (fd[0])
00333 close(fd[0]);
00334 if (!runPrivileged())
00335 {
00336 setgid(gid);
00337 #if defined( HAVE_INITGROUPS)
00338 if(pw)
00339 initgroups(pw->pw_name, pw->pw_gid);
00340 #endif
00341 setuid(uid);
00342 }
00343
00344 if(!commSetupDoneC())
00345 qWarning( "Could not finish comm setup in child!" );
00346
00347 setupEnvironment();
00348
00349
00350 if (run_mode == DontCare)
00351 setpgid(0,0);
00352
00353 struct sigaction act;
00354 sigemptyset(&(act.sa_mask));
00355 sigaddset(&(act.sa_mask), SIGPIPE);
00356 act.sa_handler = SIG_DFL;
00357 act.sa_flags = 0;
00358 sigaction(SIGPIPE, &act, 0L);
00359
00360
00361
00362 if (fd[1])
00363 fcntl(fd[1], F_SETFD, FD_CLOEXEC);
00364 execvp(arglist[0], arglist);
00365 char resultByte = 1;
00366 if (fd[1])
00367 write(fd[1], &resultByte, 1);
00368 _exit(-1);
00369 } else if (-1 == pid_) {
00370
00371
00372 runs = false;
00373 free(arglist);
00374 return false;
00375 } else {
00376 if (fd[1])
00377 close(fd[1]);
00378
00379
00380
00381 input_data = 0;
00382
00383
00384 if (fd[0]) for(;;)
00385 {
00386 char resultByte;
00387 int n = ::read(fd[0], &resultByte, 1);
00388 if (n == 1)
00389 {
00390
00391 runs = false;
00392 close(fd[0]);
00393 free(arglist);
00394 pid_ = 0;
00395 return false;
00396 }
00397 if (n == -1)
00398 {
00399 if ((errno == ECHILD) || (errno == EINTR))
00400 continue;
00401 }
00402 break;
00403 }
00404 if (fd[0])
00405 close(fd[0]);
00406
00407 if (!commSetupDoneP())
00408 qWarning( "Could not finish comm setup in parent!" );
00409
00410 if (run_mode == Block) {
00411 commClose();
00412
00413
00414
00415 while(runs)
00416 {
00417 OProcessController::theOProcessController->
00418 slotDoHousekeeping(0);
00419 }
00420 runs = FALSE;
00421 emit processExited(this);
00422 }
00423 }
00424 free(arglist);
00425 return true;
00426 }
00427
00428
00429
00430 bool OProcess::kill(int signo)
00431 {
00432 bool rv=false;
00433
00434 if (0 != pid_)
00435 rv= (-1 != ::kill(pid_, signo));
00436
00437 return rv;
00438 }
00439
00440
00441
00442 bool OProcess::isRunning() const
00443 {
00444 return runs;
00445 }
00446
00447
00448
00449 pid_t OProcess::pid() const
00450 {
00451 return pid_;
00452 }
00453
00454
00455
00456 bool OProcess::normalExit() const
00457 {
00458 int _status = status;
00459 return (pid_ != 0) && (!runs) && (WIFEXITED((_status)));
00460 }
00461
00462
00463
00464 int OProcess::exitStatus() const
00465 {
00466 int _status = status;
00467 return WEXITSTATUS((_status));
00468 }
00469
00470
00471
00472 bool OProcess::writeStdin(const char *buffer, int buflen)
00473 {
00474 bool rv;
00475
00476
00477
00478
00479 if (0 != input_data)
00480 return false;
00481
00482 if (runs && (communication & Stdin)) {
00483 input_data = buffer;
00484 input_sent = 0;
00485 input_total = buflen;
00486 slotSendData(0);
00487 innot->setEnabled(true);
00488 rv = true;
00489 } else
00490 rv = false;
00491 return rv;
00492 }
00493
00494 void OProcess::flushStdin ( )
00495 {
00496 if ( !input_data || ( input_sent == input_total ))
00497 return;
00498
00499 int d1, d2;
00500
00501 do {
00502 d1 = input_total - input_sent;
00503 slotSendData ( 0 );
00504 d2 = input_total - input_sent;
00505 } while ( d2 <= d1 );
00506 }
00507
00508 void OProcess::suspend()
00509 {
00510 if ((communication & Stdout) && outnot)
00511 outnot->setEnabled(false);
00512 }
00513
00514 void OProcess::resume()
00515 {
00516 if ((communication & Stdout) && outnot)
00517 outnot->setEnabled(true);
00518 }
00519
00520 bool OProcess::closeStdin()
00521 {
00522 bool rv;
00523
00524 if (communication & Stdin) {
00525 communication = (Communication) (communication & ~Stdin);
00526 delete innot;
00527 innot = 0;
00528 close(in[1]);
00529 rv = true;
00530 } else
00531 rv = false;
00532 return rv;
00533 }
00534
00535 bool OProcess::closeStdout()
00536 {
00537 bool rv;
00538
00539 if (communication & Stdout) {
00540 communication = (Communication) (communication & ~Stdout);
00541 delete outnot;
00542 outnot = 0;
00543 close(out[0]);
00544 rv = true;
00545 } else
00546 rv = false;
00547 return rv;
00548 }
00549
00550 bool OProcess::closeStderr()
00551 {
00552 bool rv;
00553
00554 if (communication & Stderr) {
00555 communication = static_cast<Communication>(communication & ~Stderr);
00556 delete errnot;
00557 errnot = 0;
00558 close(err[0]);
00559 rv = true;
00560 } else
00561 rv = false;
00562 return rv;
00563 }
00564
00565
00567
00569
00570
00571
00572 void OProcess::slotChildOutput(int fdno)
00573 {
00574 if (!childOutput(fdno))
00575 closeStdout();
00576 }
00577
00578
00579 void OProcess::slotChildError(int fdno)
00580 {
00581 if (!childError(fdno))
00582 closeStderr();
00583 }
00584
00585
00586 void OProcess::slotSendData(int)
00587 {
00588 if (input_sent == input_total) {
00589 innot->setEnabled(false);
00590 input_data = 0;
00591 emit wroteStdin(this);
00592 } else
00593 input_sent += ::write(in[1], input_data+input_sent, input_total-input_sent);
00594 }
00595
00596
00597
00599
00601
00602
00603
00604 void OProcess::processHasExited(int state)
00605 {
00606 if (runs)
00607 {
00608 runs = false;
00609 status = state;
00610
00611 commClose();
00612
00613
00614 if (DontCare != run_mode)
00615 {
00616 emit processExited(this);
00617 }
00618 }
00619 }
00620
00621
00622
00623 int OProcess::childOutput(int fdno)
00624 {
00625 if (communication & NoRead) {
00626 int len = -1;
00627 emit receivedStdout(fdno, len);
00628 errno = 0;
00629 return len;
00630 }
00631 else
00632 {
00633 char buffer[1024];
00634 int len;
00635
00636 len = ::read(fdno, buffer, 1024);
00637
00638 if ( 0 < len) {
00639 emit receivedStdout(this, buffer, len);
00640 }
00641 return len;
00642 }
00643 }
00644
00645
00646
00647 int OProcess::childError(int fdno)
00648 {
00649 char buffer[1024];
00650 int len;
00651
00652 len = ::read(fdno, buffer, 1024);
00653
00654 if ( 0 < len)
00655 emit receivedStderr(this, buffer, len);
00656 return len;
00657 }
00658
00659
00660
00661 int OProcess::setupCommunication(Communication comm)
00662 {
00663 int ok;
00664
00665 communication = comm;
00666
00667 ok = 1;
00668 if (comm & Stdin)
00669 ok &= socketpair(AF_UNIX, SOCK_STREAM, 0, in) >= 0;
00670
00671 if (comm & Stdout)
00672 ok &= socketpair(AF_UNIX, SOCK_STREAM, 0, out) >= 0;
00673
00674 if (comm & Stderr)
00675 ok &= socketpair(AF_UNIX, SOCK_STREAM, 0, err) >= 0;
00676
00677 return ok;
00678 }
00679
00680
00681
00682 int OProcess::commSetupDoneP()
00683 {
00684 int ok = 1;
00685
00686 if (communication != NoCommunication) {
00687 if (communication & Stdin)
00688 close(in[0]);
00689 if (communication & Stdout)
00690 close(out[1]);
00691 if (communication & Stderr)
00692 close(err[1]);
00693
00694
00695
00696 if (run_mode == Block) return ok;
00697
00698 if (communication & Stdin) {
00699
00700 innot = new QSocketNotifier(in[1], QSocketNotifier::Write, this);
00701 CHECK_PTR(innot);
00702 innot->setEnabled(false);
00703 QObject::connect(innot, SIGNAL(activated(int)),
00704 this, SLOT(slotSendData(int)));
00705 }
00706
00707 if (communication & Stdout) {
00708
00709 outnot = new QSocketNotifier(out[0], QSocketNotifier::Read, this);
00710 CHECK_PTR(outnot);
00711 QObject::connect(outnot, SIGNAL(activated(int)),
00712 this, SLOT(slotChildOutput(int)));
00713 if (communication & NoRead)
00714 suspend();
00715 }
00716
00717 if (communication & Stderr) {
00718
00719 errnot = new QSocketNotifier(err[0], QSocketNotifier::Read, this );
00720 CHECK_PTR(errnot);
00721 QObject::connect(errnot, SIGNAL(activated(int)),
00722 this, SLOT(slotChildError(int)));
00723 }
00724 }
00725 return ok;
00726 }
00727
00728
00729
00730 int OProcess::commSetupDoneC()
00731 {
00732 int ok = 1;
00733 struct linger so;
00734 memset(&so, 0, sizeof(so));
00735
00736 if (communication & Stdin)
00737 close(in[1]);
00738 if (communication & Stdout)
00739 close(out[0]);
00740 if (communication & Stderr)
00741 close(err[0]);
00742
00743 if (communication & Stdin)
00744 ok &= dup2(in[0], STDIN_FILENO) != -1;
00745 else {
00746 int null_fd = open( "/dev/null", O_RDONLY );
00747 ok &= dup2( null_fd, STDIN_FILENO ) != -1;
00748 close( null_fd );
00749 }
00750 if (communication & Stdout) {
00751 ok &= dup2(out[1], STDOUT_FILENO) != -1;
00752 ok &= !setsockopt(out[1], SOL_SOCKET, SO_LINGER, (char*)&so, sizeof(so));
00753 }
00754 else {
00755 int null_fd = open( "/dev/null", O_WRONLY );
00756 ok &= dup2( null_fd, STDOUT_FILENO ) != -1;
00757 close( null_fd );
00758 }
00759 if (communication & Stderr) {
00760 ok &= dup2(err[1], STDERR_FILENO) != -1;
00761 ok &= !setsockopt(err[1], SOL_SOCKET, SO_LINGER, reinterpret_cast<char *>(&so), sizeof(so));
00762 }
00763 else {
00764 int null_fd = open( "/dev/null", O_WRONLY );
00765 ok &= dup2( null_fd, STDERR_FILENO ) != -1;
00766 close( null_fd );
00767 }
00768 return ok;
00769 }
00770
00771
00772
00773 void OProcess::commClose()
00774 {
00775 if (NoCommunication != communication) {
00776 bool b_in = (communication & Stdin);
00777 bool b_out = (communication & Stdout);
00778 bool b_err = (communication & Stderr);
00779 if (b_in)
00780 delete innot;
00781
00782 if (b_out || b_err) {
00783
00784
00785
00786
00787
00788
00789
00790 int fds_ready = 1;
00791 fd_set rfds;
00792
00793 int max_fd = 0;
00794 if (b_out) {
00795 fcntl(out[0], F_SETFL, O_NONBLOCK);
00796 if (out[0] > max_fd)
00797 max_fd = out[0];
00798 delete outnot;
00799 outnot = 0;
00800 }
00801 if (b_err) {
00802 fcntl(err[0], F_SETFL, O_NONBLOCK);
00803 if (err[0] > max_fd)
00804 max_fd = err[0];
00805 delete errnot;
00806 errnot = 0;
00807 }
00808
00809
00810 while (b_out || b_err) {
00811
00812
00813
00814
00815
00816 struct timeval timeout;
00817 timeout.tv_sec = 0;
00818 timeout.tv_usec = 0;
00819 struct timeval *p_timeout = runs ? 0 : &timeout;
00820
00821 FD_ZERO(&rfds);
00822 if (b_out)
00823 FD_SET(out[0], &rfds);
00824
00825 if (b_err)
00826 FD_SET(err[0], &rfds);
00827
00828 fds_ready = select(max_fd+1, &rfds, 0, 0, p_timeout);
00829 if (fds_ready <= 0) break;
00830
00831 if (b_out && FD_ISSET(out[0], &rfds)) {
00832 int ret = 1;
00833 while (ret > 0) ret = childOutput(out[0]);
00834 if ((ret == -1 && errno != EAGAIN) || ret == 0)
00835 b_out = false;
00836 }
00837
00838 if (b_err && FD_ISSET(err[0], &rfds)) {
00839 int ret = 1;
00840 while (ret > 0) ret = childError(err[0]);
00841 if ((ret == -1 && errno != EAGAIN) || ret == 0)
00842 b_err = false;
00843 }
00844 }
00845 }
00846
00847 if (b_in) {
00848 communication = (Communication) (communication & ~Stdin);
00849 close(in[1]);
00850 }
00851 if (b_out) {
00852 communication = (Communication) (communication & ~Stdout);
00853 close(out[0]);
00854 }
00855 if (b_err) {
00856 communication = (Communication) (communication & ~Stderr);
00857 close(err[0]);
00858 }
00859 }
00860 }
00861
00862 void OProcess::setUseShell(bool useShell, const char *shell)
00863 {
00864 if (!d)
00865 d = new OProcessPrivate;
00866 d->useShell = useShell;
00867 d->shell = shell;
00868 if (d->shell.isEmpty())
00869 d->shell = searchShell();
00870 }
00871
00872 QString OProcess::quote(const QString &arg)
00873 {
00874 QString res = arg;
00875 res.replace(QRegExp(QString::fromLatin1("\'")),
00876 QString::fromLatin1("'\"'\"'"));
00877 res.prepend('\'');
00878 res.append('\'');
00879 return res;
00880 }
00881
00882 QCString OProcess::searchShell()
00883 {
00884 QCString tmpShell = QCString(getenv("SHELL")).stripWhiteSpace();
00885 if (!isExecutable(tmpShell))
00886 {
00887 tmpShell = "/bin/sh";
00888 }
00889
00890 return tmpShell;
00891 }
00892
00893 bool OProcess::isExecutable(const QCString &filename)
00894 {
00895 struct stat fileinfo;
00896
00897 if (filename.isEmpty()) return false;
00898
00899
00900
00901 if (-1 == stat(filename.data(), &fileinfo)) return false;
00902
00903
00904
00905 if ( (S_ISDIR(fileinfo.st_mode)) ||
00906 (S_ISCHR(fileinfo.st_mode)) ||
00907 (S_ISBLK(fileinfo.st_mode)) ||
00908 #ifdef S_ISSOCK
00909
00910 (S_ISSOCK(fileinfo.st_mode)) ||
00911 #endif
00912 (S_ISFIFO(fileinfo.st_mode)) ||
00913 (S_ISDIR(fileinfo.st_mode)) ) {
00914 return false;
00915 }
00916
00917
00918 if (access(filename.data(), X_OK) != 0) return false;
00919
00920
00921 return true;
00922 }
00923
00924
00925