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