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 slotSendData(0);
00463 innot->setEnabled(true);
00464 rv = true;
00465 } else
00466 rv = false;
00467 return rv;
00468 }
00469
00470 void KProcess::suspend()
00471 {
00472 if ((communication & Stdout) && outnot)
00473 outnot->setEnabled(false);
00474 }
00475
00476 void KProcess::resume()
00477 {
00478 if ((communication & Stdout) && outnot)
00479 outnot->setEnabled(true);
00480 }
00481
00482 bool KProcess::closeStdin()
00483 {
00484 bool rv;
00485
00486 if (communication & Stdin) {
00487 communication = (Communication) (communication & ~Stdin);
00488 delete innot;
00489 innot = 0;
00490 close(in[1]);
00491 rv = true;
00492 } else
00493 rv = false;
00494 return rv;
00495 }
00496
00497 bool KProcess::closeStdout()
00498 {
00499 bool rv;
00500
00501 if (communication & Stdout) {
00502 communication = (Communication) (communication & ~Stdout);
00503 delete outnot;
00504 outnot = 0;
00505 close(out[0]);
00506 rv = true;
00507 } else
00508 rv = false;
00509 return rv;
00510 }
00511
00512 bool KProcess::closeStderr()
00513 {
00514 bool rv;
00515
00516 if (communication & Stderr) {
00517 communication = static_cast<Communication>(communication & ~Stderr);
00518 delete errnot;
00519 errnot = 0;
00520 close(err[0]);
00521 rv = true;
00522 } else
00523 rv = false;
00524 return rv;
00525 }
00526
00527
00529
00531
00532
00533
00534 void KProcess::slotChildOutput(int fdno)
00535 {
00536 if (!childOutput(fdno))
00537 closeStdout();
00538 }
00539
00540
00541 void KProcess::slotChildError(int fdno)
00542 {
00543 if (!childError(fdno))
00544 closeStderr();
00545 }
00546
00547
00548 void KProcess::slotSendData(int)
00549 {
00550 if (input_sent == input_total) {
00551 innot->setEnabled(false);
00552 input_data = 0;
00553 emit wroteStdin(this);
00554 } else
00555 input_sent += ::write(in[1], input_data+input_sent, input_total-input_sent);
00556 }
00557
00558
00559
00561
00563
00564
00565
00566 void KProcess::processHasExited(int state)
00567 {
00568 if (runs)
00569 {
00570 runs = false;
00571 status = state;
00572
00573 commClose();
00574
00575
00576 if (DontCare != run_mode)
00577 {
00578 emit processExited(this);
00579 }
00580 }
00581 }
00582
00583
00584
00585 int KProcess::childOutput(int fdno)
00586 {
00587 if (communication & NoRead) {
00588 int len = -1;
00589 emit receivedStdout(fdno, len);
00590 errno = 0;
00591 return len;
00592 }
00593 else
00594 {
00595 char buffer[1025];
00596 int len;
00597
00598 len = ::read(fdno, buffer, 1024);
00599
00600 if ( 0 < len) {
00601 buffer[len] = 0;
00602 emit receivedStdout(this, buffer, len);
00603 }
00604 return len;
00605 }
00606 }
00607
00608
00609
00610 int KProcess::childError(int fdno)
00611 {
00612 char buffer[1024];
00613 int len;
00614
00615 len = ::read(fdno, buffer, 1024);
00616
00617 if ( 0 < len)
00618 emit receivedStderr(this, buffer, len);
00619 return len;
00620 }
00621
00622
00623
00624 int KProcess::setupCommunication(Communication comm)
00625 {
00626 communication = comm;
00627
00628 if ((comm & Stdin) && (socketpair(AF_UNIX, SOCK_STREAM, 0, in) < 0))
00629 comm = (Communication) (comm & ~Stdin);
00630
00631 if ((comm & Stdout) && (socketpair(AF_UNIX, SOCK_STREAM, 0, out) < 0))
00632 comm = (Communication) (comm & ~Stdout);
00633
00634 if ((comm & Stderr) && (socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0))
00635 comm = (Communication) (comm & ~Stderr);
00636
00637 if (communication != comm)
00638 {
00639 if (comm & Stdin)
00640 {
00641 close(in[0]);
00642 close(in[1]);
00643 }
00644 if (comm & Stdout)
00645 {
00646 close(out[0]);
00647 close(out[1]);
00648 }
00649 if (comm & Stderr)
00650 {
00651 close(err[0]);
00652 close(err[1]);
00653 }
00654 communication = NoCommunication;
00655 return 0;
00656 }
00657
00658 return 1;
00659 }
00660
00661
00662
00663 int KProcess::commSetupDoneP()
00664 {
00665 int ok = 1;
00666
00667 if (communication != NoCommunication) {
00668 if (communication & Stdin)
00669 close(in[0]);
00670 if (communication & Stdout)
00671 close(out[1]);
00672 if (communication & Stderr)
00673 close(err[1]);
00674
00675
00676
00677 if (run_mode == Block) return ok;
00678
00679 if (communication & Stdin) {
00680
00681 innot = new QSocketNotifier(in[1], QSocketNotifier::Write, this);
00682 Q_CHECK_PTR(innot);
00683 innot->setEnabled(false);
00684 QObject::connect(innot, SIGNAL(activated(int)),
00685 this, SLOT(slotSendData(int)));
00686 }
00687
00688 if (communication & Stdout) {
00689
00690 outnot = new QSocketNotifier(out[0], QSocketNotifier::Read, this);
00691 Q_CHECK_PTR(outnot);
00692 QObject::connect(outnot, SIGNAL(activated(int)),
00693 this, SLOT(slotChildOutput(int)));
00694 if (communication & NoRead)
00695 suspend();
00696 }
00697
00698 if (communication & Stderr) {
00699
00700 errnot = new QSocketNotifier(err[0], QSocketNotifier::Read, this );
00701 Q_CHECK_PTR(errnot);
00702 QObject::connect(errnot, SIGNAL(activated(int)),
00703 this, SLOT(slotChildError(int)));
00704 }
00705 }
00706 return ok;
00707 }
00708
00709
00710
00711 int KProcess::commSetupDoneC()
00712 {
00713 int ok = 1;
00714 struct linger so;
00715 memset(&so, 0, sizeof(so));
00716
00717 if (communication & Stdin)
00718 close(in[1]);
00719 if (communication & Stdout)
00720 close(out[0]);
00721 if (communication & Stderr)
00722 close(err[0]);
00723
00724 if (communication & Stdin)
00725 ok &= dup2(in[0], STDIN_FILENO) != -1;
00726 else {
00727 int null_fd = open( "/dev/null", O_RDONLY );
00728 ok &= dup2( null_fd, STDIN_FILENO ) != -1;
00729 close( null_fd );
00730 }
00731 if (communication & Stdout) {
00732 ok &= dup2(out[1], STDOUT_FILENO) != -1;
00733 ok &= !setsockopt(out[1], SOL_SOCKET, SO_LINGER, (char*)&so, sizeof(so));
00734 }
00735 else {
00736 int null_fd = open( "/dev/null", O_WRONLY );
00737 ok &= dup2( null_fd, STDOUT_FILENO ) != -1;
00738 close( null_fd );
00739 }
00740 if (communication & Stderr) {
00741 ok &= dup2(err[1], STDERR_FILENO) != -1;
00742 ok &= !setsockopt(err[1], SOL_SOCKET, SO_LINGER, reinterpret_cast<char *>(&so), sizeof(so));
00743 }
00744 else {
00745 int null_fd = open( "/dev/null", O_WRONLY );
00746 ok &= dup2( null_fd, STDERR_FILENO ) != -1;
00747 close( null_fd );
00748 }
00749 return ok;
00750 }
00751
00752
00753
00754 void KProcess::commClose()
00755 {
00756 if (NoCommunication != communication) {
00757 bool b_in = (communication & Stdin);
00758 bool b_out = (communication & Stdout);
00759 bool b_err = (communication & Stderr);
00760 if (b_in)
00761 delete innot;
00762
00763 if (b_out || b_err) {
00764
00765
00766
00767
00768
00769
00770
00771 int fds_ready = 1;
00772 fd_set rfds;
00773
00774 int max_fd = 0;
00775 if (b_out) {
00776 fcntl(out[0], F_SETFL, O_NONBLOCK);
00777 if (out[0] > max_fd)
00778 max_fd = out[0];
00779 delete outnot;
00780 outnot = 0;
00781 }
00782 if (b_err) {
00783 fcntl(err[0], F_SETFL, O_NONBLOCK);
00784 if (err[0] > max_fd)
00785 max_fd = err[0];
00786 delete errnot;
00787 errnot = 0;
00788 }
00789
00790
00791 while (b_out || b_err) {
00792
00793
00794
00795
00796
00797 struct timeval timeout;
00798 timeout.tv_sec = 0;
00799 timeout.tv_usec = 0;
00800 struct timeval *p_timeout = runs ? 0 : &timeout;
00801
00802 FD_ZERO(&rfds);
00803 if (b_out)
00804 FD_SET(out[0], &rfds);
00805
00806 if (b_err)
00807 FD_SET(err[0], &rfds);
00808
00809 fds_ready = select(max_fd+1, &rfds, 0, 0, p_timeout);
00810 if (fds_ready <= 0) break;
00811
00812 if (b_out && FD_ISSET(out[0], &rfds)) {
00813 int ret = 1;
00814 while (ret > 0) ret = childOutput(out[0]);
00815 if ((ret == -1 && errno != EAGAIN) || ret == 0)
00816 b_out = false;
00817 }
00818
00819 if (b_err && FD_ISSET(err[0], &rfds)) {
00820 int ret = 1;
00821 while (ret > 0) ret = childError(err[0]);
00822 if ((ret == -1 && errno != EAGAIN) || ret == 0)
00823 b_err = false;
00824 }
00825 }
00826 }
00827
00828 if (communication & Stdin) {
00829 communication = (Communication) (communication & ~Stdin);
00830 close(in[1]);
00831 }
00832 if (communication & Stdout) {
00833 communication = (Communication) (communication & ~Stdout);
00834 close(out[0]);
00835 }
00836 if (communication & Stderr) {
00837 communication = (Communication) (communication & ~Stderr);
00838 close(err[0]);
00839 }
00840 }
00841 }
00842
00843 void KProcess::setUseShell(bool useShell, const char *shell)
00844 {
00845 if (!d)
00846 d = new KProcessPrivate;
00847 d->useShell = useShell;
00848 d->shell = shell;
00849 if (d->shell.isEmpty())
00850 d->shell = searchShell();
00851 }
00852
00853 QString KProcess::quote(const QString &arg)
00854 {
00855 QString res = arg;
00856 res.replace(QRegExp(QString::fromLatin1("\'")),
00857 QString::fromLatin1("'\"'\"'"));
00858 res.prepend('\'');
00859 res.append('\'');
00860 return res;
00861 }
00862
00863 QCString KProcess::searchShell()
00864 {
00865 QCString tmpShell = QCString(getenv("SHELL")).stripWhiteSpace();
00866 if (!isExecutable(tmpShell))
00867 {
00868 tmpShell = "/bin/sh";
00869 }
00870
00871 return tmpShell;
00872 }
00873
00874 bool KProcess::isExecutable(const QCString &filename)
00875 {
00876 struct stat fileinfo;
00877
00878 if (filename.isEmpty()) return false;
00879
00880
00881
00882 if (-1 == stat(filename.data(), &fileinfo)) return false;
00883
00884
00885
00886 if ( (S_ISDIR(fileinfo.st_mode)) ||
00887 (S_ISCHR(fileinfo.st_mode)) ||
00888 (S_ISBLK(fileinfo.st_mode)) ||
00889 #ifdef S_ISSOCK
00890
00891 (S_ISSOCK(fileinfo.st_mode)) ||
00892 #endif
00893 (S_ISFIFO(fileinfo.st_mode)) ||
00894 (S_ISDIR(fileinfo.st_mode)) ) {
00895 return false;
00896 }
00897
00898
00899 if (access(filename.data(), X_OK) != 0) return false;
00900
00901
00902 return true;
00903 }
00904
00905 void KProcess::virtual_hook( int, void* )
00906 { }
00907
00908
00910
00912
00913 KShellProcess::KShellProcess(const char *shellname):
00914 KProcess()
00915 {
00916 setUseShell(true, shellname);
00917 }
00918
00919
00920 KShellProcess::~KShellProcess() {
00921 }
00922
00923 QString KShellProcess::quote(const QString &arg)
00924 {
00925 return KProcess::quote(arg);
00926 }
00927
00928 bool KShellProcess::start(RunMode runmode, Communication comm)
00929 {
00930 return KProcess::start(runmode, comm);
00931 }
00932
00933 void KShellProcess::virtual_hook( int id, void* data )
00934 { KProcess::virtual_hook( id, data ); }
00935
00936 #include "kprocess.moc"