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