From bdb1b502083e00fe5929f9ca4204bfaa87f15263 Mon Sep 17 00:00:00 2001 From: Michele Calgaro Date: Sun, 1 Dec 2024 22:25:19 +0900 Subject: Rename kprocess.{h,cpp} to tdeprocess.{h.cpp}. Add temporary kprocess.h to keep things building. Signed-off-by: Michele Calgaro --- tdecore/CMakeLists.txt | 4 +- tdecore/MAINTAINERS | 2 +- tdecore/Makefile.am | 4 +- tdecore/kprocctrl.cpp | 2 +- tdecore/kprocctrl.h | 2 +- tdecore/kprocess.cpp | 1137 -------------------------------------- tdecore/kprocess.h | 940 +------------------------------ tdecore/kprocio.h | 2 +- tdecore/kpty.cpp | 2 +- tdecore/ktempdir.cpp | 2 +- tdecore/ktimezones.cpp | 2 +- tdecore/tdeconfigbackend.cpp | 2 +- tdecore/tdeprocess.cpp | 1137 ++++++++++++++++++++++++++++++++++++++ tdecore/tdeprocess.h | 936 +++++++++++++++++++++++++++++++ tdecore/tdesycoca.cpp | 2 +- tdecore/tests/CMakeLists.txt | 2 +- tdecore/tests/Makefile.am | 6 +- tdecore/tests/kprocesstest.cpp | 116 ---- tdecore/tests/kprocesstest.h | 50 -- tdecore/tests/kprociotest.cpp | 2 +- tdecore/tests/tdeprocesstest.cpp | 116 ++++ tdecore/tests/tdeprocesstest.h | 48 ++ 22 files changed, 2256 insertions(+), 2260 deletions(-) delete mode 100644 tdecore/kprocess.cpp create mode 100644 tdecore/tdeprocess.cpp create mode 100644 tdecore/tdeprocess.h delete mode 100644 tdecore/tests/kprocesstest.cpp delete mode 100644 tdecore/tests/kprocesstest.h create mode 100644 tdecore/tests/tdeprocesstest.cpp create mode 100644 tdecore/tests/tdeprocesstest.h (limited to 'tdecore') diff --git a/tdecore/CMakeLists.txt b/tdecore/CMakeLists.txt index 4e36f0978..18ce4eda9 100644 --- a/tdecore/CMakeLists.txt +++ b/tdecore/CMakeLists.txt @@ -53,7 +53,7 @@ install( FILES tdeconfigdialogmanager.h tdeconfigbase.h kdesktopfile.h kurl.h ksock.h tdeaboutdata.h tdecmdlineargs.h tdeconfigbackend.h tdeapplication.h tdeuniqueapplication.h - kcharsets.h tdeversion.h kpty.h kprocess.h kprocctrl.h + kcharsets.h tdeversion.h kpty.h kprocess.h tdeprocess.h kprocctrl.h tdelocale.h kicontheme.h kiconloader.h kdebug.h twinmodule.h twin.h krootprop.h tdeshortcut.h kkeynative.h tdeaccel.h kglobalaccel.h tdestdaccel.h tdeshortcutlist.h kcatalogue.h @@ -108,7 +108,7 @@ set( ${target}_SRCS libintl.cpp tdeapplication.cpp kdebug.cpp netwm.cpp tdeconfigbase.cpp tdeconfig.cpp ksimpleconfig.cpp tdeconfigbackend.cpp kmanagerselection.cpp kdesktopfile.cpp kstandarddirs.cpp - ksock.cpp kpty.cpp kprocess.cpp kprocctrl.cpp tdelocale.cpp + ksock.cpp kpty.cpp tdeprocess.cpp kprocctrl.cpp tdelocale.cpp krfcdate.cpp kiconeffect.cpp kicontheme.cpp kiconloader.cpp twin.cpp twinmodule.cpp krootprop.cpp kcharsets.cpp kckey.cpp tdeshortcut.cpp kkeynative_x11.cpp kkeyserver_x11.cpp diff --git a/tdecore/MAINTAINERS b/tdecore/MAINTAINERS index 441274b09..d7012b321 100644 --- a/tdecore/MAINTAINERS +++ b/tdecore/MAINTAINERS @@ -49,7 +49,7 @@ knotifyclient.cpp kpalette.cpp Waldo Bastian kpixmapprovider.cpp Carsten Pfeiffer kprocctrl.cpp Waldo Bastian -kprocess.cpp Waldo Bastian +tdeprocess.cpp Waldo Bastian kprocio.cpp Waldo Bastian krandomsequence.cpp Waldo Bastian kregexp.cpp diff --git a/tdecore/Makefile.am b/tdecore/Makefile.am index b59c6fd52..ada41910c 100644 --- a/tdecore/Makefile.am +++ b/tdecore/Makefile.am @@ -40,7 +40,7 @@ include_HEADERS = tdeconfig.h tdeconfigskeleton.h \ tdeconfigdata.h ksimpleconfig.h tdeconfigdialogmanager.h \ tdeconfigbase.h kdesktopfile.h kurl.h ksock.h tdeaboutdata.h \ tdecmdlineargs.h tdeconfigbackend.h tdeapplication.h \ - tdeuniqueapplication.h kcharsets.h tdeversion.h kpty.h kprocess.h \ + tdeuniqueapplication.h kcharsets.h tdeversion.h kpty.h kprocess.h tdeprocess.h \ kprocctrl.h tdelocale.h kicontheme.h kiconloader.h kdebug.h \ twinmodule.h twin.h krootprop.h tdeshortcut.h kkeynative.h tdeaccel.h \ kglobalaccel.h tdestdaccel.h tdeshortcutlist.h kcatalogue.h \ @@ -92,7 +92,7 @@ noinst_HEADERS = tdeaccelaction.h tdeaccelbase.h tdeaccelprivate.h kckey.h \ libtdecore_la_SOURCES = libintl.cpp tdeapplication.cpp \ kdebug.cpp netwm.cpp tdeconfigbase.cpp tdeconfig.cpp ksimpleconfig.cpp \ tdeconfigbackend.cpp kmanagerselection.cpp kdesktopfile.cpp \ - kstandarddirs.cpp ksock.cpp kpty.cpp kprocess.cpp kprocctrl.cpp \ + kstandarddirs.cpp ksock.cpp kpty.cpp tdeprocess.cpp kprocctrl.cpp \ tdelocale.cpp krfcdate.cpp kiconeffect.cpp kicontheme.cpp \ kiconloader.cpp twin.cpp twinmodule.cpp krootprop.cpp kcharsets.cpp \ kckey.cpp tdeshortcut.cpp kkeynative_x11.cpp kkeyserver_x11.cpp \ diff --git a/tdecore/kprocctrl.cpp b/tdecore/kprocctrl.cpp index 720778d4e..471642ddb 100644 --- a/tdecore/kprocctrl.cpp +++ b/tdecore/kprocctrl.cpp @@ -17,7 +17,7 @@ Boston, MA 02110-1301, USA. */ -#include "kprocess.h" +#include "tdeprocess.h" #include "kprocctrl.h" #include diff --git a/tdecore/kprocctrl.h b/tdecore/kprocctrl.h index ac5700753..ddd59179b 100644 --- a/tdecore/kprocctrl.h +++ b/tdecore/kprocctrl.h @@ -22,7 +22,7 @@ #include -#include "kprocess.h" +#include "tdeprocess.h" class TQSocketNotifier; diff --git a/tdecore/kprocess.cpp b/tdecore/kprocess.cpp deleted file mode 100644 index 85e7620a4..000000000 --- a/tdecore/kprocess.cpp +++ /dev/null @@ -1,1137 +0,0 @@ -/* - - $Id$ - - This file is part of the KDE libraries - Copyright (C) 1997 Christian Czezatke (e9025461@student.tuwien.ac.at) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - - -#include "kprocess.h" -#include "kprocctrl.h" -#include "kpty.h" - -#include - -#ifdef __sgi -#define __svr4__ -#endif - -#ifdef __osf__ -#define _OSF_SOURCE -#include -#endif - -#ifdef _AIX -#define _ALL_SOURCE -#endif - -#ifdef Q_OS_UNIX -#include -#include -#endif - -#include -#include -#include -#include -#include - -#ifdef HAVE_SYS_STROPTS_H -#include // Defines I_PUSH -#define _NEW_TTY_CTRL -#endif -#ifdef HAVE_SYS_SELECT_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - - -////////////////// -// private data // -////////////////// - -class TDEProcessPrivate { -public: - TDEProcessPrivate() : - usePty(TDEProcess::NoCommunication), - addUtmp(false), useShell(false), -#ifdef Q_OS_UNIX - pty(0), -#endif - priority(0) - { - } - - TDEProcess::Communication usePty; - bool addUtmp : 1; - bool useShell : 1; - -#ifdef Q_OS_UNIX - KPty *pty; -#endif - - int priority; - - TQMap env; - TQString wd; - TQCString shell; - TQCString executable; -}; - -///////////////////////////// -// public member functions // -///////////////////////////// - -TDEProcess::TDEProcess( TQObject* parent, const char *name ) - : TQObject( parent, name ), - run_mode(NotifyOnExit), - runs(false), - pid_(0), - status(0), - keepPrivs(false), - innot(0), - outnot(0), - errnot(0), - communication(NoCommunication), - input_data(0), - input_sent(0), - input_total(0) -{ - TDEProcessController::ref(); - TDEProcessController::theTDEProcessController->addTDEProcess(this); - - d = new TDEProcessPrivate; - - out[0] = out[1] = -1; - in[0] = in[1] = -1; - err[0] = err[1] = -1; -} - -TDEProcess::TDEProcess() - : TQObject(), - run_mode(NotifyOnExit), - runs(false), - pid_(0), - status(0), - keepPrivs(false), - innot(0), - outnot(0), - errnot(0), - communication(NoCommunication), - input_data(0), - input_sent(0), - input_total(0) -{ - TDEProcessController::ref(); - TDEProcessController::theTDEProcessController->addTDEProcess(this); - - d = new TDEProcessPrivate; - - out[0] = out[1] = -1; - in[0] = in[1] = -1; - err[0] = err[1] = -1; -} - -void -TDEProcess::setEnvironment(const TQString &name, const TQString &value) -{ - d->env.insert(name, value); -} - -void -TDEProcess::setWorkingDirectory(const TQString &dir) -{ - d->wd = dir; -} - -void -TDEProcess::setupEnvironment() -{ - TQMap::Iterator it; - for(it = d->env.begin(); it != d->env.end(); ++it) - { - setenv(TQFile::encodeName(it.key()).data(), - TQFile::encodeName(it.data()).data(), 1); - } - if (!d->wd.isEmpty()) - { - chdir(TQFile::encodeName(d->wd).data()); - } -} - -void -TDEProcess::setRunPrivileged(bool keepPrivileges) -{ - keepPrivs = keepPrivileges; -} - -bool -TDEProcess::runPrivileged() const -{ - return keepPrivs; -} - -bool -TDEProcess::setPriority(int prio) -{ -#ifdef Q_OS_UNIX - if (runs) { - if (setpriority(PRIO_PROCESS, pid_, prio)) - return false; - } else { - if (prio > 19 || prio < (geteuid() ? getpriority(PRIO_PROCESS, 0) : -20)) - return false; - } -#endif - d->priority = prio; - return true; -} - -TDEProcess::~TDEProcess() -{ - if (run_mode != DontCare) - kill(SIGKILL); - detach(); - -#ifdef Q_OS_UNIX - delete d->pty; -#endif - delete d; - - TDEProcessController::theTDEProcessController->removeTDEProcess(this); - TDEProcessController::deref(); -} - -void TDEProcess::detach() -{ - if (runs) { - TDEProcessController::theTDEProcessController->addProcess(pid_); - runs = false; - pid_ = 0; // close without draining - commClose(); // Clean up open fd's and socket notifiers. - } -} - -void TDEProcess::setBinaryExecutable(const char *filename) -{ - d->executable = filename; -} - -bool TDEProcess::setExecutable(const TQString& proc) -{ - if (runs) return false; - - if (proc.isEmpty()) return false; - - if (!arguments.isEmpty()) - arguments.remove(arguments.begin()); - arguments.prepend(TQFile::encodeName(proc)); - - return true; -} - -TDEProcess &TDEProcess::operator<<(const TQStringList& args) -{ - TQStringList::ConstIterator it = args.begin(); - for ( ; it != args.end() ; ++it ) - arguments.append(TQFile::encodeName(*it)); - return *this; -} - -TDEProcess &TDEProcess::operator<<(const TQCString& arg) -{ - return operator<< (arg.data()); -} - -TDEProcess &TDEProcess::operator<<(const char* arg) -{ - arguments.append(arg); - return *this; -} - -TDEProcess &TDEProcess::operator<<(const TQString& arg) -{ - arguments.append(TQFile::encodeName(arg)); - return *this; -} - -void TDEProcess::clearArguments() -{ - arguments.clear(); -} - -bool TDEProcess::start(RunMode runmode, Communication comm) -{ - if (runs) { - kdDebug(175) << "Attempted to start an already running process" << endl; - return false; - } - - uint n = arguments.count(); - if (n == 0) { - kdDebug(175) << "Attempted to start a process without arguments" << endl; - return false; - } -#ifdef Q_OS_UNIX - char **arglist; - TQCString shellCmd; - if (d->useShell) - { - if (d->shell.isEmpty()) { - kdDebug(175) << "Invalid shell specified" << endl; - return false; - } - - for (uint i = 0; i < n; i++) { - shellCmd += arguments[i]; - shellCmd += " "; // CC: to separate the arguments - } - - arglist = static_cast(malloc( 4 * sizeof(char *))); - arglist[0] = d->shell.data(); - arglist[1] = (char *) "-c"; - arglist[2] = shellCmd.data(); - arglist[3] = 0; - } - else - { - arglist = static_cast(malloc( (n + 1) * sizeof(char *))); - for (uint i = 0; i < n; i++) - arglist[i] = arguments[i].data(); - arglist[n] = 0; - } - - run_mode = runmode; - - if (!setupCommunication(comm)) - { - kdDebug(175) << "Could not setup Communication!" << endl; - free(arglist); - return false; - } - - // We do this in the parent because if we do it in the child process - // gdb gets confused when the application runs from gdb. -#ifdef HAVE_INITGROUPS - struct passwd *pw = geteuid() ? 0 : getpwuid(getuid()); -#endif - - int fd[2]; - if (pipe(fd)) - fd[0] = fd[1] = -1; // Pipe failed.. continue - - // we don't use vfork() because - // - it has unclear semantics and is not standardized - // - we do way too much magic in the child - pid_ = fork(); - if (pid_ == 0) { - // The child process - - close(fd[0]); - // Closing of fd[1] indicates that the execvp() succeeded! - fcntl(fd[1], F_SETFD, FD_CLOEXEC); - - if (!commSetupDoneC()) - kdDebug(175) << "Could not finish comm setup in child!" << endl; - - // reset all signal handlers - struct sigaction act; - sigemptyset(&act.sa_mask); - act.sa_handler = SIG_DFL; - act.sa_flags = 0; - for (int sig = 1; sig < NSIG; sig++) - sigaction(sig, &act, 0L); - - if (d->priority) - setpriority(PRIO_PROCESS, 0, d->priority); - - if (!runPrivileged()) - { - setgid(getgid()); -#ifdef HAVE_INITGROUPS - if (pw) - initgroups(pw->pw_name, pw->pw_gid); -#endif - if (geteuid() != getuid()) - setuid(getuid()); - if (geteuid() != getuid()) - _exit(1); - } - - setupEnvironment(); - - if (runmode == DontCare || runmode == OwnGroup) - setsid(); - - const char *executable = arglist[0]; - if (!d->executable.isEmpty()) - executable = d->executable.data(); - execvp(executable, arglist); - - char resultByte = 1; - write(fd[1], &resultByte, 1); - _exit(-1); - } else if (pid_ == -1) { - // forking failed - - // commAbort(); - pid_ = 0; - free(arglist); - return false; - } - // the parent continues here - free(arglist); - - if (!commSetupDoneP()) - kdDebug(175) << "Could not finish comm setup in parent!" << endl; - - // Check whether client could be started. - close(fd[1]); - for(;;) - { - char resultByte; - int n = ::read(fd[0], &resultByte, 1); - if (n == 1) - { - // exec() failed - close(fd[0]); - waitpid(pid_, 0, 0); - pid_ = 0; - commClose(); - return false; - } - if (n == -1) - { - if (errno == EINTR) - continue; // Ignore - } - break; // success - } - close(fd[0]); - - runs = true; - switch (runmode) - { - case Block: - for (;;) - { - commClose(); // drain only, unless obsolete reimplementation - if (!runs) - { - // commClose detected data on the process exit notifification pipe - TDEProcessController::theTDEProcessController->unscheduleCheck(); - if (waitpid(pid_, &status, WNOHANG) != 0) // error finishes, too - { - commClose(); // this time for real (runs is false) - TDEProcessController::theTDEProcessController->rescheduleCheck(); - break; - } - runs = true; // for next commClose() iteration - } - else - { - // commClose is an obsolete reimplementation and waited until - // all output channels were closed (or it was interrupted). - // there is a chance that it never gets here ... - waitpid(pid_, &status, 0); - runs = false; - break; - } - } - // why do we do this? i think this signal should be emitted _only_ - // after the process has successfully run _asynchronously_ --ossi - emit processExited(this); - break; - default: // NotifyOnExit & OwnGroup - input_data = 0; // Discard any data for stdin that might still be there - break; - } - return true; -#else - //TODO - return false; -#endif -} - - - -bool TDEProcess::kill(int signo) -{ -#ifdef Q_OS_UNIX - if (runs && pid_ > 0 && !::kill(run_mode == OwnGroup ? -pid_ : pid_, signo)) - return true; -#endif - return false; -} - - - -bool TDEProcess::isRunning() const -{ - return runs; -} - - - -pid_t TDEProcess::pid() const -{ - return pid_; -} - -#ifndef timersub -# define timersub(a, b, result) \ - do { \ - (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ - (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ - if ((result)->tv_usec < 0) { \ - --(result)->tv_sec; \ - (result)->tv_usec += 1000000; \ - } \ - } while (0) -#endif - -bool TDEProcess::wait(int timeout) -{ - if (!runs) - return true; - -#ifndef __linux__ - struct timeval etv; -#endif - struct timeval tv, *tvp; - if (timeout < 0) - tvp = 0; - else - { -#ifndef __linux__ - gettimeofday(&etv, 0); - etv.tv_sec += timeout; -#else - tv.tv_sec = timeout; - tv.tv_usec = 0; -#endif - tvp = &tv; - } - -#ifdef Q_OS_UNIX - int fd = TDEProcessController::theTDEProcessController->notifierFd(); - for(;;) - { - fd_set fds; - FD_ZERO( &fds ); - FD_SET( fd, &fds ); - -#ifndef __linux__ - if (tvp) - { - gettimeofday(&tv, 0); - timersub(&etv, &tv, &tv); - if (tv.tv_sec < 0) - tv.tv_sec = tv.tv_usec = 0; - } -#endif - - switch( select( fd+1, &fds, 0, 0, tvp ) ) - { - case -1: - if( errno == EINTR ) - break; - // fall through; should happen if tvp->tv_sec < 0 - case 0: - TDEProcessController::theTDEProcessController->rescheduleCheck(); - return false; - default: - TDEProcessController::theTDEProcessController->unscheduleCheck(); - if (waitpid(pid_, &status, WNOHANG) != 0) // error finishes, too - { - processHasExited(status); - TDEProcessController::theTDEProcessController->rescheduleCheck(); - return true; - } - } - } -#endif //Q_OS_UNIX - return false; -} - - - -bool TDEProcess::normalExit() const -{ - return (pid_ != 0) && !runs && WIFEXITED(status); -} - - -bool TDEProcess::signalled() const -{ - return (pid_ != 0) && !runs && WIFSIGNALED(status); -} - - -bool TDEProcess::coreDumped() const -{ -#ifdef WCOREDUMP - return signalled() && WCOREDUMP(status); -#else - return false; -#endif -} - - -int TDEProcess::exitStatus() const -{ - return WEXITSTATUS(status); -} - - -int TDEProcess::exitSignal() const -{ - return WTERMSIG(status); -} - - -bool TDEProcess::writeStdin(const char *buffer, int buflen) -{ - // if there is still data pending, writing new data - // to stdout is not allowed (since it could also confuse - // kprocess ...) - if (input_data != 0) - return false; - - if (communication & Stdin) { - input_data = buffer; - input_sent = 0; - input_total = buflen; - innot->setEnabled(true); - if (input_total) - slotSendData(0); - return true; - } else - return false; -} - -void TDEProcess::suspend() -{ - if (outnot) - outnot->setEnabled(false); -} - -void TDEProcess::resume() -{ - if (outnot) - outnot->setEnabled(true); -} - -bool TDEProcess::closeStdin() -{ - if (communication & Stdin) { - communication = (Communication) (communication & ~Stdin); - delete innot; - innot = 0; - if (!(d->usePty & Stdin)) - close(in[1]); - in[1] = -1; - return true; - } else - return false; -} - -bool TDEProcess::closeStdout() -{ - if (communication & Stdout) { - communication = (Communication) (communication & ~Stdout); - delete outnot; - outnot = 0; - if (!(d->usePty & Stdout)) - close(out[0]); - out[0] = -1; - return true; - } else - return false; -} - -bool TDEProcess::closeStderr() -{ - if (communication & Stderr) { - communication = (Communication) (communication & ~Stderr); - delete errnot; - errnot = 0; - if (!(d->usePty & Stderr)) - close(err[0]); - err[0] = -1; - return true; - } else - return false; -} - -bool TDEProcess::closePty() -{ -#ifdef Q_OS_UNIX - if (d->pty && d->pty->masterFd() >= 0) { - if (d->addUtmp) - d->pty->logout(); - d->pty->close(); - return true; - } else - return false; -#else - return false; -#endif -} - -void TDEProcess::closeAll() -{ - closeStdin(); - closeStdout(); - closeStderr(); - closePty(); -} - -///////////////////////////// -// protected slots // -///////////////////////////// - - - -void TDEProcess::slotChildOutput(int fdno) -{ - if (!childOutput(fdno)) - closeStdout(); -} - - -void TDEProcess::slotChildError(int fdno) -{ - if (!childError(fdno)) - closeStderr(); -} - - -void TDEProcess::slotSendData(int) -{ - if (input_sent == input_total) { - innot->setEnabled(false); - input_data = 0; - emit wroteStdin(this); - } else { - int result = ::write(in[1], input_data+input_sent, input_total-input_sent); - if (result >= 0) - { - input_sent += result; - } - else if ((errno != EAGAIN) && (errno != EINTR)) - { - kdDebug(175) << "Error writing to stdin of child process" << endl; - closeStdin(); - } - } -} - -void TDEProcess::setUseShell(bool useShell, const char *shell) -{ - d->useShell = useShell; - if (shell && *shell) - d->shell = shell; - else -// #ifdef NON_FREE // ... as they ship non-POSIX /bin/sh -#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__GNU__) && !defined(__DragonFly__) - // Solaris POSIX ... - if (!access( "/usr/xpg4/bin/sh", X_OK )) - d->shell = "/usr/xpg4/bin/sh"; - else - // ... which links here anyway - if (!access( "/bin/ksh", X_OK )) - d->shell = "/bin/ksh"; - else - // dunno, maybe superfluous? - if (!access( "/usr/ucb/sh", X_OK )) - d->shell = "/usr/ucb/sh"; - else -#endif - d->shell = "/bin/sh"; -} - -#ifdef Q_OS_UNIX -void TDEProcess::setUsePty(Communication usePty, bool addUtmp) -{ - d->usePty = usePty; - d->addUtmp = addUtmp; - if (usePty) { - if (!d->pty) - d->pty = new KPty; - } else { - delete d->pty; - d->pty = 0; - } -} - -KPty *TDEProcess::pty() const -{ - return d->pty; -} -#endif //Q_OS_UNIX - -TQString TDEProcess::quote(const TQString &arg) -{ - TQChar q('\''); - return TQString(arg).replace(q, "'\\''").prepend(q).append(q); -} - - -////////////////////////////// -// private member functions // -////////////////////////////// - - -void TDEProcess::processHasExited(int state) -{ - // only successfully run NotifyOnExit processes ever get here - - status = state; - runs = false; // do this before commClose, so it knows we're dead - - commClose(); // cleanup communication sockets - - if (run_mode != DontCare) - emit processExited(this); -} - - - -int TDEProcess::childOutput(int fdno) -{ - if (communication & NoRead) { - int len = -1; - emit receivedStdout(fdno, len); - errno = 0; // Make sure errno doesn't read "EAGAIN" - return len; - } - else - { - char buffer[1025]; - int len; - - len = ::read(fdno, buffer, 1024); - - if (len > 0) { - buffer[len] = 0; // Just in case. - emit receivedStdout(this, buffer, len); - } - return len; - } -} - -int TDEProcess::childError(int fdno) -{ - char buffer[1025]; - int len; - - len = ::read(fdno, buffer, 1024); - - if (len > 0) { - buffer[len] = 0; // Just in case. - emit receivedStderr(this, buffer, len); - } - return len; -} - - -int TDEProcess::setupCommunication(Communication comm) -{ -#ifdef Q_OS_UNIX - // PTY stuff // - if (d->usePty) - { - // cannot communicate on both stderr and stdout if they are both on the pty - if (!(~(comm & d->usePty) & (Stdout | Stderr))) { - kdWarning(175) << "Invalid usePty/communication combination (" << d->usePty << "/" << comm << ")" << endl; - return 0; - } - if (!d->pty->open()) - return 0; - - int rcomm = comm & d->usePty; - int mfd = d->pty->masterFd(); - if (rcomm & Stdin) - in[1] = mfd; - if (rcomm & Stdout) - out[0] = mfd; - if (rcomm & Stderr) - err[0] = mfd; - } - - communication = comm; - - comm = (Communication) (comm & ~d->usePty); - if (comm & Stdin) { - if (socketpair(AF_UNIX, SOCK_STREAM, 0, in)) - goto fail0; - fcntl(in[0], F_SETFD, FD_CLOEXEC); - fcntl(in[1], F_SETFD, FD_CLOEXEC); - } - if (comm & Stdout) { - if (socketpair(AF_UNIX, SOCK_STREAM, 0, out)) - goto fail1; - fcntl(out[0], F_SETFD, FD_CLOEXEC); - fcntl(out[1], F_SETFD, FD_CLOEXEC); - } - if (comm & Stderr) { - if (socketpair(AF_UNIX, SOCK_STREAM, 0, err)) - goto fail2; - fcntl(err[0], F_SETFD, FD_CLOEXEC); - fcntl(err[1], F_SETFD, FD_CLOEXEC); - } - return 1; // Ok - fail2: - if (comm & Stdout) - { - close(out[0]); - close(out[1]); - out[0] = out[1] = -1; - } - fail1: - if (comm & Stdin) - { - close(in[0]); - close(in[1]); - in[0] = in[1] = -1; - } - fail0: - communication = NoCommunication; -#endif //Q_OS_UNIX - return 0; // Error -} - - - -int TDEProcess::commSetupDoneP() -{ - int rcomm = communication & ~d->usePty; - if (rcomm & Stdin) - close(in[0]); - if (rcomm & Stdout) - close(out[1]); - if (rcomm & Stderr) - close(err[1]); - in[0] = out[1] = err[1] = -1; - - // Don't create socket notifiers if no interactive comm is to be expected - if (run_mode != NotifyOnExit && run_mode != OwnGroup) - return 1; - - if (communication & Stdin) { - fcntl(in[1], F_SETFL, O_NONBLOCK | fcntl(in[1], F_GETFL)); - innot = new TQSocketNotifier(in[1], TQSocketNotifier::Write, this); - TQ_CHECK_PTR(innot); - innot->setEnabled(false); // will be enabled when data has to be sent - TQObject::connect(innot, TQ_SIGNAL(activated(int)), - this, TQ_SLOT(slotSendData(int))); - } - - if (communication & Stdout) { - outnot = new TQSocketNotifier(out[0], TQSocketNotifier::Read, this); - TQ_CHECK_PTR(outnot); - TQObject::connect(outnot, TQ_SIGNAL(activated(int)), - this, TQ_SLOT(slotChildOutput(int))); - if (communication & NoRead) - suspend(); - } - - if (communication & Stderr) { - errnot = new TQSocketNotifier(err[0], TQSocketNotifier::Read, this ); - TQ_CHECK_PTR(errnot); - TQObject::connect(errnot, TQ_SIGNAL(activated(int)), - this, TQ_SLOT(slotChildError(int))); - } - - return 1; -} - - - -int TDEProcess::commSetupDoneC() -{ - int ok = 1; -#ifdef Q_OS_UNIX - - if (d->usePty & Stdin) { - if (dup2(d->pty->slaveFd(), STDIN_FILENO) < 0) ok = 0; - } else if (communication & Stdin) { - if (dup2(in[0], STDIN_FILENO) < 0) ok = 0; - } else { - int null_fd = open( "/dev/null", O_RDONLY ); - if (dup2( null_fd, STDIN_FILENO ) < 0) ok = 0; - close( null_fd ); - } - struct linger so; - memset(&so, 0, sizeof(so)); - if (d->usePty & Stdout) { - if (dup2(d->pty->slaveFd(), STDOUT_FILENO) < 0) ok = 0; - } else if (communication & Stdout) { - if (dup2(out[1], STDOUT_FILENO) < 0 || - setsockopt(out[1], SOL_SOCKET, SO_LINGER, (char *)&so, sizeof(so))) - ok = 0; - if (communication & MergedStderr) { - if (dup2(out[1], STDERR_FILENO) < 0) - ok = 0; - } - } - if (d->usePty & Stderr) { - if (dup2(d->pty->slaveFd(), STDERR_FILENO) < 0) ok = 0; - } else if (communication & Stderr) { - if (dup2(err[1], STDERR_FILENO) < 0 || - setsockopt(err[1], SOL_SOCKET, SO_LINGER, (char *)&so, sizeof(so))) - ok = 0; - } - - // don't even think about closing all open fds here or anywhere else - - // PTY stuff // - if (d->usePty) { - d->pty->setCTty(); - if (d->addUtmp) - d->pty->login(KUser(KUser::UseRealUserID).loginName().local8Bit().data(), getenv("DISPLAY")); - } -#endif //Q_OS_UNIX - - return ok; -} - - - -void TDEProcess::commClose() -{ - closeStdin(); - -#ifdef Q_OS_UNIX - if (pid_) { // detached, failed, and killed processes have no output. basta. :) - // If both channels are being read we need to make sure that one socket - // buffer doesn't fill up whilst we are waiting for data on the other - // (causing a deadlock). Hence we need to use select. - - int notfd = TDEProcessController::theTDEProcessController->notifierFd(); - - while ((communication & (Stdout | Stderr)) || runs) { - fd_set rfds; - FD_ZERO(&rfds); - struct timeval timeout, *p_timeout; - - int max_fd = 0; - if (communication & Stdout) { - FD_SET(out[0], &rfds); - max_fd = out[0]; - } - if (communication & Stderr) { - FD_SET(err[0], &rfds); - if (err[0] > max_fd) - max_fd = err[0]; - } - if (runs) { - FD_SET(notfd, &rfds); - if (notfd > max_fd) - max_fd = notfd; - // If the process is still running we block until we - // receive data or the process exits. - p_timeout = 0; // no timeout - } else { - // If the process has already exited, we only check - // the available data, we don't wait for more. - timeout.tv_sec = timeout.tv_usec = 0; // timeout immediately - p_timeout = &timeout; - } - - int fds_ready = select(max_fd+1, &rfds, 0, 0, p_timeout); - if (fds_ready < 0) { - if (errno == EINTR) - continue; - break; - } else if (!fds_ready) - break; - - if ((communication & Stdout) && FD_ISSET(out[0], &rfds)) - slotChildOutput(out[0]); - - if ((communication & Stderr) && FD_ISSET(err[0], &rfds)) - slotChildError(err[0]); - - if (runs && FD_ISSET(notfd, &rfds)) { - runs = false; // hack: signal potential exit - return; // don't close anything, we will be called again - } - } - } -#endif //Q_OS_UNIX - - closeStdout(); - closeStderr(); - - closePty(); -} - - -void TDEProcess::virtual_hook( int, void* ) -{ /*BASE::virtual_hook( id, data );*/ } - - -/////////////////////////// -// CC: Class KShellProcess -/////////////////////////// - -KShellProcess::KShellProcess(const char *shellname): - TDEProcess() -{ - setUseShell( true, shellname ? shellname : getenv("SHELL") ); -} - -KShellProcess::~KShellProcess() { -} - -TQString KShellProcess::quote(const TQString &arg) -{ - return TDEProcess::quote(arg); -} - -bool KShellProcess::start(RunMode runmode, Communication comm) -{ - return TDEProcess::start(runmode, comm); -} - -void KShellProcess::virtual_hook( int id, void* data ) -{ TDEProcess::virtual_hook( id, data ); } - -#include "kprocess.moc" diff --git a/tdecore/kprocess.h b/tdecore/kprocess.h index 211e3cc7f..216868501 100644 --- a/tdecore/kprocess.h +++ b/tdecore/kprocess.h @@ -1,939 +1 @@ -/* This file is part of the KDE libraries - Copyright (C) 1997 Christian Czezakte (e9025461@student.tuwien.ac.at) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#ifndef __kprocess_h__ -#define __kprocess_h__ - -#include // for pid_t -#include -#include -#include -#include -#include -#include -#include "tdelibs_export.h" - -class TQSocketNotifier; -class TDEProcessPrivate; - -#ifdef Q_OS_UNIX -#include -#else -class KPty; -#endif - -/** - * Child process invocation, monitoring and control. - * This class works only in the application's main thread. - * - * General usage and features:\n - * - * This class allows a KDE application to start child processes without having - * to worry about UN*X signal handling issues and zombie process reaping. - * - * @see KProcIO - * - * Basically, this class distinguishes three different ways of running - * child processes: - * - * @li DontCare -- The child process is invoked and both the child - * process and the parent process continue concurrently. - * - * The process is started in an own session (see setsid(2)). - * - * @li NotifyOnExit -- The child process is invoked and both the - * child and the parent process run concurrently. - * - * When the child process exits, the TDEProcess instance - * corresponding to it emits the Qt signal processExited(). - * Since this signal is @em not emitted from within a UN*X - * signal handler, arbitrary function calls can be made. - * - * Be aware: When the TDEProcess object gets destructed, the child - * process will be killed if it is still running! - * This means in particular, that it usually makes no sense to use - * a TDEProcess on the stack with NotifyOnExit. - * - * @li OwnGroup -- like NotifyOnExit, but the child process is started - * in an own process group (and an own session, FWIW). The behavior of - * kill() changes to killing the whole process group - this makes - * this mode useful for implementing primitive job management. It can be - * used to work around broken wrapper scripts that don't propagate signals - * to the "real" program. However, use this with care, as you disturb the - * shell's job management if your program is started from the command line. - * - * @li Block -- The child process starts and the parent process - * is suspended until the child process exits. (@em Really not recommended - * for programs with a GUI.) - * In this mode the parent can read the child's output, but can't send it any - * input. - * - * TDEProcess also provides several functions for determining the exit status - * and the pid of the child process it represents. - * - * Furthermore it is possible to supply command-line arguments to the process - * in a clean fashion (no null-terminated stringlists and such...) - * - * A small usage example: - * \code - * TDEProcess *proc = new TDEProcess; - * - * *proc << "my_executable"; - * *proc << "These" << "are" << "the" << "command" << "line" << "args"; - * TQApplication::connect(proc, TQ_SIGNAL(processExited(TDEProcess *)), - * pointer_to_my_object, TQ_SLOT(my_objects_slot(TDEProcess *))); - * proc->start(); - * \endcode - * - * This will start "my_executable" with the commandline arguments "These"... - * - * When the child process exits, the slot will be invoked. - * - * Communication with the child process:\n - * - * TDEProcess supports communication with the child process through - * stdin/stdout/stderr. - * - * The following functions are provided for getting data from the child - * process or sending data to the child's stdin (For more information, - * have a look at the documentation of each function): - * - * @li writeStdin() - * -- Transmit data to the child process' stdin. When all data was sent, the - * signal wroteStdin() is emitted. - * - * @li When data arrives at stdout or stderr, the signal receivedStdout() - * resp. receivedStderr() is emitted. - * - * @li You can shut down individual communication channels with - * closeStdin(), closeStdout(), and closeStderr(), resp. - * - * @author Christian Czezatke e9025461@student.tuwien.ac.at - * - **/ -class TDECORE_EXPORT TDEProcess : public TQObject -{ - TQ_OBJECT - -public: - - /** - * Modes in which the communication channel can be opened. - * - * If communication for more than one channel is required, - * the values have to be or'ed together, for example to get - * communication with stdout as well as with stdin, you would - * specify @p Stdin | @p Stdout - * - * If @p NoRead is specified in conjunction with @p Stdout, - * no data is actually read from @p Stdout but only - * the signal receivedStdout(int fd, int &len) is emitted. - * - * @p CTtyOnly tells setUsePty() to create a PTY for the process - * and make it the process' controlling TTY, but does not redirect - * any I/O channel to the PTY. - * - * If @p MergedStderr is specified in conjunction with @p Stdout, - * Stderr will be redirected onto the same file handle as Stdout, - * i.e., all error output will be signalled with receivedStdout(). - * Don't specify @p Stderr if you specify @p MergedStderr. - */ - enum Communication { - NoCommunication = 0, - Stdin = 1, Stdout = 2, Stderr = 4, - AllOutput = 6, All = 7, - NoRead = 8, - CTtyOnly = NoRead, - MergedStderr = 16 - }; - - /** - * Run-modes for a child process. - */ - enum RunMode { - /** - * The application does not receive notifications from the subprocess when - * it is finished or aborted. - */ - DontCare, - /** - * The application is notified when the subprocess dies. - */ - NotifyOnExit, - /** - * The application is suspended until the started process is finished. - */ - Block, - /** - * Same as NotifyOnExit, but the process is run in an own session, - * just like with DontCare. - */ - OwnGroup - }; - - /** - * Constructor - * @since 3.2 - */ - TDEProcess( TQObject* parent, const char *name = 0 ); - - /** - * Constructor - */ // KDE4 merge with the above - TDEProcess(); - - /** - *Destructor: - * - * If the process is running when the destructor for this class - * is called, the child process is killed with a SIGKILL, but - * only if the run mode is not of type @p DontCare. - * Processes started as @p DontCare keep running anyway. - */ - virtual ~TDEProcess(); - - /** - @deprecated - Use operator<<() instead. - - Sets the executable to be started with this TDEProcess object. - Returns false if the process is currently running (in that - case the executable remains unchanged). - - @see operator<<() - - */ - bool setExecutable(const TQString& proc) TDE_DEPRECATED; - - - /** - * Sets the executable and the command line argument list for this process. - * - * For example, doing an "ls -l /usr/local/bin" can be achieved by: - * \code - * TDEProcess p; - * ... - * p << "ls" << "-l" << "/usr/local/bin" - * \endcode - * - * @param arg the argument to add - * @return a reference to this TDEProcess - **/ - TDEProcess &operator<<(const TQString& arg); - /** - * Similar to previous method, takes a char *, supposed to be in locale 8 bit already. - */ - TDEProcess &operator<<(const char * arg); - /** - * Similar to previous method, takes a TQCString, supposed to be in locale 8 bit already. - * @param arg the argument to add - * @return a reference to this TDEProcess - */ - TDEProcess &operator<<(const TQCString & arg); - - /** - * Sets the executable and the command line argument list for this process, - * in a single method call, or add a list of arguments. - * @param args the arguments to add - * @return a reference to this TDEProcess - **/ - TDEProcess &operator<<(const TQStringList& args); - - /** - * Clear a command line argument list that has been set by using - * operator<<. - */ - void clearArguments(); - - /** - * Starts the process. - * For a detailed description of the - * various run modes and communication semantics, have a look at the - * general description of the TDEProcess class. Note that if you use - * setUsePty( Stdout | Stderr, \ ), you cannot use Stdout | Stderr - * here - instead, use Stdout only to receive the mixed output. - * - * The following problems could cause this function to - * return false: - * - * @li The process is already running. - * @li The command line argument list is empty. - * @li The the @p comm parameter is incompatible with the selected pty usage. - * @li The starting of the process failed (could not fork). - * @li The executable was not found. - * - * @param runmode The Run-mode for the process. - * @param comm Specifies which communication links should be - * established to the child process (stdin/stdout/stderr). By default, - * no communication takes place and the respective communication - * signals will never get emitted. - * - * @return true on success, false on error - * (see above for error conditions) - **/ - virtual bool start(RunMode runmode = NotifyOnExit, - Communication comm = NoCommunication); - - /** - * Stop the process (by sending it a signal). - * - * @param signo The signal to send. The default is SIGTERM. - * @return true if the signal was delivered successfully. - */ - virtual bool kill(int signo = SIGTERM); - - /** - * Checks whether the process is running. - * @return true if the process is (still) considered to be running - */ - bool isRunning() const; - - /** Returns the process id of the process. - * - * If it is called after - * the process has exited, it returns the process id of the last - * child process that was created by this instance of TDEProcess. - * - * Calling it before any child process has been started by this - * TDEProcess instance causes pid() to return 0. - * @return the pid of the process or 0 if no process has been started yet. - **/ - pid_t pid() const; - - /** - * @deprecated - * Use pid() instead. - */ - TDE_DEPRECATED pid_t getPid() const { return pid(); } - - /** - * Suspend processing of data from stdout of the child process. - */ - void suspend(); - - /** - * Resume processing of data from stdout of the child process. - */ - void resume(); - - /** - * Suspend execution of the current thread until the child process dies - * or the timeout hits. This function is not recommended for programs - * with a GUI. - * @param timeout timeout in seconds. -1 means wait indefinitely. - * @return true if the process exited, false if the timeout hit. - * @since 3.2 - */ - bool wait(int timeout = -1); - - /** - * Checks whether the process exited cleanly. - * - * @return true if the process has already finished and has exited - * "voluntarily", ie: it has not been killed by a signal. - */ - bool normalExit() const; - - /** - * Checks whether the process was killed by a signal. - * - * @return true if the process has already finished and has not exited - * "voluntarily", ie: it has been killed by a signal. - * - * @since 3.2 - */ - bool signalled() const; - - /** - * Checks whether a killed process dumped core. - * - * @return true if signalled() returns true and the process - * dumped core. Note that on systems that don't define the - * WCOREDUMP macro, the return value is always false. - * - * @since 3.2 - */ - bool coreDumped() const; - - /** - * Returns the exit status of the process. - * - * @return the exit status of the process. Note that this value - * is not valid if normalExit() returns false. - */ - int exitStatus() const; - - /** - * Returns the signal the process was killed by. - * - * @return the signal number that caused the process to exit. - * Note that this value is not valid if signalled() returns false. - * - * @since 3.2 - */ - int exitSignal() const; - - /** - * Transmit data to the child process' stdin. - * - * This function may return false in the following cases: - * - * @li The process is not currently running. - * This implies that you cannot use this function in Block mode. - * - * @li Communication to stdin has not been requested in the start() call. - * - * @li Transmission of data to the child process by a previous call to - * writeStdin() is still in progress. - * - * Please note that the data is sent to the client asynchronously, - * so when this function returns, the data might not have been - * processed by the child process. - * That means that you must not free @p buffer or call writeStdin() - * again until either a wroteStdin() signal indicates that the - * data has been sent or a processExited() signal shows that - * the child process is no longer alive. - * - * If all the data has been sent to the client, the signal - * wroteStdin() will be emitted. - * - * This function does not work when the process is start()ed in Block mode. - * - * @param buffer the buffer to write - * @param buflen the length of the buffer - * @return false if an error has occurred - **/ - bool writeStdin(const char *buffer, int buflen); - - /** - * Shuts down the Stdin communication link. If no pty is used, this - * causes "EOF" to be indicated on the child's stdin file descriptor. - * - * @return false if no Stdin communication link exists (any more). - */ - bool closeStdin(); - - /** - * Shuts down the Stdout communication link. If no pty is used, any further - * attempts by the child to write to its stdout file descriptor will cause - * it to receive a SIGPIPE. - * - * @return false if no Stdout communication link exists (any more). - */ - bool closeStdout(); - - /** - * Shuts down the Stderr communication link. If no pty is used, any further - * attempts by the child to write to its stderr file descriptor will cause - * it to receive a SIGPIPE. - * - * @return false if no Stderr communication link exists (any more). - */ - bool closeStderr(); - - /** - * Deletes the optional utmp entry and closes the pty. - * - * Make sure to shut down any communication links that are using the pty - * before calling this function. - * - * @return false if the pty is not open (any more). - */ - bool closePty(); - - /** - * @brief Close stdin, stdout, stderr and the pty - * - * This is the same that calling all close* functions in a row: - * @see closeStdin, @see closeStdout, @see closeStderr and @see closePty - */ - void closeAll(); - - /** - * Lets you see what your arguments are for debugging. - * @return the list of arguments - */ - const TQValueList &args() /* const */ { return arguments; } - - /** - * Controls whether the started process should drop any - * setuid/setgid privileges or whether it should keep them. - * Note that this function is mostly a dummy, as the KDE libraries - * currently refuse to run with setuid/setgid privileges. - * - * The default is false: drop privileges - * @param keepPrivileges true to keep the privileges - */ - void setRunPrivileged(bool keepPrivileges); - - /** - * Returns whether the started process will drop any - * setuid/setgid privileges or whether it will keep them. - * @return true if the process runs privileged - */ - bool runPrivileged() const; - - /** - * Adds the variable @p name to the process' environment. - * This function must be called before starting the process. - * @param name the name of the environment variable - * @param value the new value for the environment variable - */ - void setEnvironment(const TQString &name, const TQString &value); - - /** - * Changes the current working directory (CWD) of the process - * to be started. - * This function must be called before starting the process. - * @param dir the new directory - */ - void setWorkingDirectory(const TQString &dir); - - /** - * Specify whether to start the command via a shell or directly. - * The default is to start the command directly. - * If @p useShell is true @p shell will be used as shell, or - * if shell is empty, /bin/sh will be used. - * - * When using a shell, the caller should make sure that all filenames etc. - * are properly quoted when passed as argument. - * @see quote() - * @param useShell true if the command should be started via a shell - * @param shell the path to the shell that will execute the process, or - * 0 to use /bin/sh. Use getenv("SHELL") to use the user's - * default shell, but note that doing so is usually a bad idea - * for shell compatibility reasons. - * @since 3.1 - */ - void setUseShell(bool useShell, const char *shell = 0); - - /** - * This function can be used to quote an argument string such that - * the shell processes it properly. This is e. g. necessary for - * user-provided file names which may contain spaces or quotes. - * It also prevents expansion of wild cards and environment variables. - * @param arg the argument to quote - * @return the quoted argument - * @since 3.1 - */ - static TQString quote(const TQString &arg); - - /** - * Detaches TDEProcess from child process. All communication is closed. - * No exit notification is emitted any more for the child process. - * Deleting the TDEProcess will no longer kill the child process. - * Note that the current process remains the parent process of the - * child process. - */ - void detach(); - -#ifdef Q_OS_UNIX - /** - * Specify whether to create a pty (pseudo-terminal) for running the - * command. - * This function should be called before starting the process. - * - * @param comm for which stdio handles to use a pty. Note that it is not - * allowed to specify Stdout and Stderr at the same time both here and to - * start (there is only one pty, so they cannot be distinguished). - * @param addUtmp true if a utmp entry should be created for the pty - * @since 3.2 - */ - void setUsePty(Communication comm, bool addUtmp); - - /** - * Obtains the pty object used by this process. The return value is - * valid only after setUsePty() was used with a non-zero argument. - * The pty is open only while the process is running. - * @return a pointer to the pty object - * @since 3.2 - */ - KPty *pty() const; -#endif - - /** - * More or less intuitive constants for use with setPriority(). - */ - enum { PrioLowest = 20, PrioLow = 10, PrioLower = 5, PrioNormal = 0, - PrioHigher = -5, PrioHigh = -10, PrioHighest = -19 }; - - /** - * Sets the scheduling priority of the process. - * @param prio the new priority in the range -20 (high) to 19 (low). - * @return false on error; see setpriority(2) for possible reasons. - * @since 3.2 - */ - bool setPriority(int prio); - -signals: - /** - * Emitted after the process has terminated when - * the process was run in the @p NotifyOnExit (==default option to - * start() ) or the Block mode. - * @param proc a pointer to the process that has exited - **/ - void processExited(TDEProcess *proc); - - - /** - * Emitted, when output from the child process has - * been received on stdout. - * - * To actually get this signal, the Stdout communication link - * has to be turned on in start(). - * - * @param proc a pointer to the process that has received the output - * @param buffer The data received. - * @param buflen The number of bytes that are available. - * - * You should copy the information contained in @p buffer to your private - * data structures before returning from the slot. - * Example: - * \code - * TQString myBuf = TQString::fromLatin1(buffer, buflen); - * \endcode - **/ - void receivedStdout(TDEProcess *proc, char *buffer, int buflen); - - /** - * Emitted when output from the child process has - * been received on stdout. - * - * To actually get this signal, the Stdout communication link - * has to be turned on in start() and the - * NoRead flag must have been passed. - * - * You will need to explicitly call resume() after your call to start() - * to begin processing data from the child process' stdout. This is - * to ensure that this signal is not emitted when no one is connected - * to it, otherwise this signal will not be emitted. - * - * The data still has to be read from file descriptor @p fd. - * @param fd the file descriptor that provides the data - * @param len the number of bytes that have been read from @p fd must - * be written here - **/ - void receivedStdout(int fd, int &len); // KDE4: change, broken API - - - /** - * Emitted, when output from the child process has - * been received on stderr. - * - * To actually get this signal, the Stderr communication link - * has to be turned on in start(). - * - * You should copy the information contained in @p buffer to your private - * data structures before returning from the slot. - * - * @param proc a pointer to the process that has received the data - * @param buffer The data received. - * @param buflen The number of bytes that are available. - **/ - void receivedStderr(TDEProcess *proc, char *buffer, int buflen); - - /** - * Emitted after all the data that has been - * specified by a prior call to writeStdin() has actually been - * written to the child process. - * @param proc a pointer to the process - **/ - void wroteStdin(TDEProcess *proc); - - -protected slots: - - /** - * This slot gets activated when data from the child's stdout arrives. - * It usually calls childOutput(). - * @param fdno the file descriptor for the output - */ - void slotChildOutput(int fdno); - - /** - * This slot gets activated when data from the child's stderr arrives. - * It usually calls childError(). - * @param fdno the file descriptor for the output - */ - void slotChildError(int fdno); - - /** - * Called when another bulk of data can be sent to the child's - * stdin. If there is no more data to be sent to stdin currently - * available, this function must disable the TQSocketNotifier innot. - * @param dummy ignore this argument - */ - void slotSendData(int dummy); // KDE 4: remove dummy - -protected: - - /** - * Sets up the environment according to the data passed via - * setEnvironment() - */ - void setupEnvironment(); - - /** - * The list of the process' command line arguments. The first entry - * in this list is the executable itself. - */ - TQValueList arguments; - /** - * How to run the process (Block, NotifyOnExit, DontCare). You should - * not modify this data member directly from derived classes. - */ - RunMode run_mode; - /** - * true if the process is currently running. You should not - * modify this data member directly from derived classes. Please use - * isRunning() for reading the value of this data member since it - * will probably be made private in later versions of TDEProcess. - */ - bool runs; - - /** - * The PID of the currently running process. - * You should not modify this data member in derived classes. - * Please use pid() instead of directly accessing this - * member since it will probably be made private in - * later versions of TDEProcess. - */ - pid_t pid_; - - /** - * The process' exit status as returned by waitpid(). You should not - * modify the value of this data member from derived classes. You should - * rather use exitStatus() than accessing this data member directly - * since it will probably be made private in further versions of - * TDEProcess. - */ - int status; - - - /** - * If false, the child process' effective uid & gid will be reset to the - * real values. - * @see setRunPrivileged() - */ - bool keepPrivs; - - /** - * This function is called from start() right before a fork() takes - * place. According to the @p comm parameter this function has to initialize - * the in, out and err data members of TDEProcess. - * - * This function should return 1 if setting the needed communication channels - * was successful. - * - * The default implementation is to create UNIX STREAM sockets for the - * communication, but you could reimplement this function to establish a - * TCP/IP communication for network communication, for example. - */ - virtual int setupCommunication(Communication comm); - - /** - * Called right after a (successful) fork() on the parent side. This function - * will usually do some communications cleanup, like closing in[0], - * out[1] and out[1]. - * - * Furthermore, it must also create the TQSocketNotifiers innot, - * outnot and errnot and connect their Qt signals to the respective - * TDEProcess slots. - * - * For a more detailed explanation, it is best to have a look at the default - * implementation in kprocess.cpp. - */ - virtual int commSetupDoneP(); - - /** - * Called right after a (successful) fork(), but before an exec() on the child - * process' side. It usually duplicates the in[0], out[1] and - * err[1] file handles to the respective standard I/O handles. - */ - virtual int commSetupDoneC(); - - - /** - * Immediately called after a successfully started process in NotifyOnExit - * mode has exited. This function normally calls commClose() - * and emits the processExited() signal. - * @param state the exit code of the process as returned by waitpid() - */ - virtual void processHasExited(int state); - - /** - * Cleans up the communication links to the child after it has exited. - * This function should act upon the values of pid() and runs. - * See the kprocess.cpp source for details. - * @li If pid() returns zero, the communication links should be closed - * only. - * @li if pid() returns non-zero and runs is false, all data - * immediately available from the communication links should be processed - * before closing them. - * @li if pid() returns non-zero and runs is true, the communication - * links should be monitored for data until the file handle returned by - * TDEProcessController::theTDEProcessController->notifierFd() becomes ready - * for reading - when it triggers, runs should be reset to false, and - * the function should be immediately left without closing anything. - * - * The previous semantics of this function are forward-compatible, but should - * be avoided, as they are prone to race conditions and can cause TDEProcess - * (and thus the whole program) to lock up under certain circumstances. At the - * end the function closes the communication links in any case. Additionally - * @li if runs is true, the communication links are monitored for data - * until all of them have returned EOF. Note that if any system function is - * interrupted (errno == EINTR) the polling loop should be aborted. - * @li if runs is false, all data immediately available from the - * communication links is processed. - */ - virtual void commClose(); - - /* KDE 4 - commClose will be changed to perform cleanup only in all cases * - * If @p notfd is -1, all data immediately available from the - * communication links should be processed. - * If @p notfd is not -1, the communication links should be monitored - * for data until the file handle @p notfd becomes ready for reading. - */ -// virtual void commDrain(int notfd); - - /** - * Specify the actual executable that should be started (first argument to execve) - * Normally the the first argument is the executable but you can - * override that with this function. - */ - void setBinaryExecutable(const char *filename); - - /** - * The socket descriptors for stdout. - */ - int out[2]; - /** - * The socket descriptors for stdin. - */ - int in[2]; - /** - * The socket descriptors for stderr. - */ - int err[2]; - - /** - * The socket notifier for in[1]. - */ - TQSocketNotifier *innot; - /** - * The socket notifier for out[0]. - */ - TQSocketNotifier *outnot; - /** - * The socket notifier for err[0]. - */ - TQSocketNotifier *errnot; - - /** - * Lists the communication links that are activated for the child - * process. Should not be modified from derived classes. - */ - Communication communication; - - /** - * Called by slotChildOutput() this function copies data arriving from - * the child process' stdout to the respective buffer and emits the signal - * receivedStdout(). - */ - int childOutput(int fdno); - - /** - * Called by slotChildError() this function copies data arriving from - * the child process' stderr to the respective buffer and emits the signal - * receivedStderr(). - */ - int childError(int fdno); - - /** - * The buffer holding the data that has to be sent to the child - */ - const char *input_data; - /** - * The number of bytes already transmitted - */ - int input_sent; - /** - * The total length of input_data - */ - int input_total; - - /** - * TDEProcessController is a friend of TDEProcess because it has to have - * access to various data members. - */ - friend class TDEProcessController; - -protected: - virtual void virtual_hook( int id, void* data ); -private: - TDEProcessPrivate *d; -}; - -class KShellProcessPrivate; - -/** -* @obsolete -* -* Use TDEProcess and TDEProcess::setUseShell(true) instead. -* -* @short A class derived from TDEProcess to start child -* processes through a shell. -* @author Christian Czezatke -*/ -class TDECORE_EXPORT KShellProcess: public TDEProcess -{ - TQ_OBJECT - -public: - - /** - * Constructor - * - * If no shellname is specified, the user's default shell is used. - */ - KShellProcess(const char *shellname=0); - - /** - * Destructor. - */ - ~KShellProcess(); - - virtual bool start(RunMode runmode = NotifyOnExit, - Communication comm = NoCommunication); - - static TQString quote(const TQString &arg); - -private: - TQCString shell; - -protected: - virtual void virtual_hook( int id, void* data ); -private: - KShellProcessPrivate *d; -}; - - - -#endif - +#include diff --git a/tdecore/kprocio.h b/tdecore/kprocio.h index eda8c6eab..02a3257d5 100644 --- a/tdecore/kprocio.h +++ b/tdecore/kprocio.h @@ -19,7 +19,7 @@ #define KPROCIO_H_ #include -#include +#include #include #include "tdelibs_export.h" diff --git a/tdecore/kpty.cpp b/tdecore/kpty.cpp index a3ff34aec..a66705f4d 100644 --- a/tdecore/kpty.cpp +++ b/tdecore/kpty.cpp @@ -24,7 +24,7 @@ #include #include "kpty.h" -#include "kprocess.h" +#include "tdeprocess.h" #ifdef __sgi #define __svr4__ diff --git a/tdecore/ktempdir.cpp b/tdecore/ktempdir.cpp index d16d2e185..a11e582f2 100644 --- a/tdecore/ktempdir.cpp +++ b/tdecore/ktempdir.cpp @@ -52,7 +52,7 @@ #include "kinstance.h" #include "ktempdir.h" #include "kstandarddirs.h" -#include "kprocess.h" +#include "tdeprocess.h" #include #include "kde_file.h" diff --git a/tdecore/ktimezones.cpp b/tdecore/ktimezones.cpp index 999446b7c..bf8a370fb 100644 --- a/tdecore/ktimezones.cpp +++ b/tdecore/ktimezones.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include diff --git a/tdecore/tdeconfigbackend.cpp b/tdecore/tdeconfigbackend.cpp index a3e8e2c8f..4f83e7716 100644 --- a/tdecore/tdeconfigbackend.cpp +++ b/tdecore/tdeconfigbackend.cpp @@ -43,7 +43,7 @@ #include "tdeconfigbase.h" #include #include -#include +#include #include #include #include diff --git a/tdecore/tdeprocess.cpp b/tdecore/tdeprocess.cpp new file mode 100644 index 000000000..767ddcd2d --- /dev/null +++ b/tdecore/tdeprocess.cpp @@ -0,0 +1,1137 @@ +/* + + $Id$ + + This file is part of the KDE libraries + Copyright (C) 1997 Christian Czezatke (e9025461@student.tuwien.ac.at) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#include "tdeprocess.h" +#include "kprocctrl.h" +#include "kpty.h" + +#include + +#ifdef __sgi +#define __svr4__ +#endif + +#ifdef __osf__ +#define _OSF_SOURCE +#include +#endif + +#ifdef _AIX +#define _ALL_SOURCE +#endif + +#ifdef Q_OS_UNIX +#include +#include +#endif + +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_STROPTS_H +#include // Defines I_PUSH +#define _NEW_TTY_CTRL +#endif +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + + +////////////////// +// private data // +////////////////// + +class TDEProcessPrivate { +public: + TDEProcessPrivate() : + usePty(TDEProcess::NoCommunication), + addUtmp(false), useShell(false), +#ifdef Q_OS_UNIX + pty(0), +#endif + priority(0) + { + } + + TDEProcess::Communication usePty; + bool addUtmp : 1; + bool useShell : 1; + +#ifdef Q_OS_UNIX + KPty *pty; +#endif + + int priority; + + TQMap env; + TQString wd; + TQCString shell; + TQCString executable; +}; + +///////////////////////////// +// public member functions // +///////////////////////////// + +TDEProcess::TDEProcess( TQObject* parent, const char *name ) + : TQObject( parent, name ), + run_mode(NotifyOnExit), + runs(false), + pid_(0), + status(0), + keepPrivs(false), + innot(0), + outnot(0), + errnot(0), + communication(NoCommunication), + input_data(0), + input_sent(0), + input_total(0) +{ + TDEProcessController::ref(); + TDEProcessController::theTDEProcessController->addTDEProcess(this); + + d = new TDEProcessPrivate; + + out[0] = out[1] = -1; + in[0] = in[1] = -1; + err[0] = err[1] = -1; +} + +TDEProcess::TDEProcess() + : TQObject(), + run_mode(NotifyOnExit), + runs(false), + pid_(0), + status(0), + keepPrivs(false), + innot(0), + outnot(0), + errnot(0), + communication(NoCommunication), + input_data(0), + input_sent(0), + input_total(0) +{ + TDEProcessController::ref(); + TDEProcessController::theTDEProcessController->addTDEProcess(this); + + d = new TDEProcessPrivate; + + out[0] = out[1] = -1; + in[0] = in[1] = -1; + err[0] = err[1] = -1; +} + +void +TDEProcess::setEnvironment(const TQString &name, const TQString &value) +{ + d->env.insert(name, value); +} + +void +TDEProcess::setWorkingDirectory(const TQString &dir) +{ + d->wd = dir; +} + +void +TDEProcess::setupEnvironment() +{ + TQMap::Iterator it; + for(it = d->env.begin(); it != d->env.end(); ++it) + { + setenv(TQFile::encodeName(it.key()).data(), + TQFile::encodeName(it.data()).data(), 1); + } + if (!d->wd.isEmpty()) + { + chdir(TQFile::encodeName(d->wd).data()); + } +} + +void +TDEProcess::setRunPrivileged(bool keepPrivileges) +{ + keepPrivs = keepPrivileges; +} + +bool +TDEProcess::runPrivileged() const +{ + return keepPrivs; +} + +bool +TDEProcess::setPriority(int prio) +{ +#ifdef Q_OS_UNIX + if (runs) { + if (setpriority(PRIO_PROCESS, pid_, prio)) + return false; + } else { + if (prio > 19 || prio < (geteuid() ? getpriority(PRIO_PROCESS, 0) : -20)) + return false; + } +#endif + d->priority = prio; + return true; +} + +TDEProcess::~TDEProcess() +{ + if (run_mode != DontCare) + kill(SIGKILL); + detach(); + +#ifdef Q_OS_UNIX + delete d->pty; +#endif + delete d; + + TDEProcessController::theTDEProcessController->removeTDEProcess(this); + TDEProcessController::deref(); +} + +void TDEProcess::detach() +{ + if (runs) { + TDEProcessController::theTDEProcessController->addProcess(pid_); + runs = false; + pid_ = 0; // close without draining + commClose(); // Clean up open fd's and socket notifiers. + } +} + +void TDEProcess::setBinaryExecutable(const char *filename) +{ + d->executable = filename; +} + +bool TDEProcess::setExecutable(const TQString& proc) +{ + if (runs) return false; + + if (proc.isEmpty()) return false; + + if (!arguments.isEmpty()) + arguments.remove(arguments.begin()); + arguments.prepend(TQFile::encodeName(proc)); + + return true; +} + +TDEProcess &TDEProcess::operator<<(const TQStringList& args) +{ + TQStringList::ConstIterator it = args.begin(); + for ( ; it != args.end() ; ++it ) + arguments.append(TQFile::encodeName(*it)); + return *this; +} + +TDEProcess &TDEProcess::operator<<(const TQCString& arg) +{ + return operator<< (arg.data()); +} + +TDEProcess &TDEProcess::operator<<(const char* arg) +{ + arguments.append(arg); + return *this; +} + +TDEProcess &TDEProcess::operator<<(const TQString& arg) +{ + arguments.append(TQFile::encodeName(arg)); + return *this; +} + +void TDEProcess::clearArguments() +{ + arguments.clear(); +} + +bool TDEProcess::start(RunMode runmode, Communication comm) +{ + if (runs) { + kdDebug(175) << "Attempted to start an already running process" << endl; + return false; + } + + uint n = arguments.count(); + if (n == 0) { + kdDebug(175) << "Attempted to start a process without arguments" << endl; + return false; + } +#ifdef Q_OS_UNIX + char **arglist; + TQCString shellCmd; + if (d->useShell) + { + if (d->shell.isEmpty()) { + kdDebug(175) << "Invalid shell specified" << endl; + return false; + } + + for (uint i = 0; i < n; i++) { + shellCmd += arguments[i]; + shellCmd += " "; // CC: to separate the arguments + } + + arglist = static_cast(malloc( 4 * sizeof(char *))); + arglist[0] = d->shell.data(); + arglist[1] = (char *) "-c"; + arglist[2] = shellCmd.data(); + arglist[3] = 0; + } + else + { + arglist = static_cast(malloc( (n + 1) * sizeof(char *))); + for (uint i = 0; i < n; i++) + arglist[i] = arguments[i].data(); + arglist[n] = 0; + } + + run_mode = runmode; + + if (!setupCommunication(comm)) + { + kdDebug(175) << "Could not setup Communication!" << endl; + free(arglist); + return false; + } + + // We do this in the parent because if we do it in the child process + // gdb gets confused when the application runs from gdb. +#ifdef HAVE_INITGROUPS + struct passwd *pw = geteuid() ? 0 : getpwuid(getuid()); +#endif + + int fd[2]; + if (pipe(fd)) + fd[0] = fd[1] = -1; // Pipe failed.. continue + + // we don't use vfork() because + // - it has unclear semantics and is not standardized + // - we do way too much magic in the child + pid_ = fork(); + if (pid_ == 0) { + // The child process + + close(fd[0]); + // Closing of fd[1] indicates that the execvp() succeeded! + fcntl(fd[1], F_SETFD, FD_CLOEXEC); + + if (!commSetupDoneC()) + kdDebug(175) << "Could not finish comm setup in child!" << endl; + + // reset all signal handlers + struct sigaction act; + sigemptyset(&act.sa_mask); + act.sa_handler = SIG_DFL; + act.sa_flags = 0; + for (int sig = 1; sig < NSIG; sig++) + sigaction(sig, &act, 0L); + + if (d->priority) + setpriority(PRIO_PROCESS, 0, d->priority); + + if (!runPrivileged()) + { + setgid(getgid()); +#ifdef HAVE_INITGROUPS + if (pw) + initgroups(pw->pw_name, pw->pw_gid); +#endif + if (geteuid() != getuid()) + setuid(getuid()); + if (geteuid() != getuid()) + _exit(1); + } + + setupEnvironment(); + + if (runmode == DontCare || runmode == OwnGroup) + setsid(); + + const char *executable = arglist[0]; + if (!d->executable.isEmpty()) + executable = d->executable.data(); + execvp(executable, arglist); + + char resultByte = 1; + write(fd[1], &resultByte, 1); + _exit(-1); + } else if (pid_ == -1) { + // forking failed + + // commAbort(); + pid_ = 0; + free(arglist); + return false; + } + // the parent continues here + free(arglist); + + if (!commSetupDoneP()) + kdDebug(175) << "Could not finish comm setup in parent!" << endl; + + // Check whether client could be started. + close(fd[1]); + for(;;) + { + char resultByte; + int n = ::read(fd[0], &resultByte, 1); + if (n == 1) + { + // exec() failed + close(fd[0]); + waitpid(pid_, 0, 0); + pid_ = 0; + commClose(); + return false; + } + if (n == -1) + { + if (errno == EINTR) + continue; // Ignore + } + break; // success + } + close(fd[0]); + + runs = true; + switch (runmode) + { + case Block: + for (;;) + { + commClose(); // drain only, unless obsolete reimplementation + if (!runs) + { + // commClose detected data on the process exit notifification pipe + TDEProcessController::theTDEProcessController->unscheduleCheck(); + if (waitpid(pid_, &status, WNOHANG) != 0) // error finishes, too + { + commClose(); // this time for real (runs is false) + TDEProcessController::theTDEProcessController->rescheduleCheck(); + break; + } + runs = true; // for next commClose() iteration + } + else + { + // commClose is an obsolete reimplementation and waited until + // all output channels were closed (or it was interrupted). + // there is a chance that it never gets here ... + waitpid(pid_, &status, 0); + runs = false; + break; + } + } + // why do we do this? i think this signal should be emitted _only_ + // after the process has successfully run _asynchronously_ --ossi + emit processExited(this); + break; + default: // NotifyOnExit & OwnGroup + input_data = 0; // Discard any data for stdin that might still be there + break; + } + return true; +#else + //TODO + return false; +#endif +} + + + +bool TDEProcess::kill(int signo) +{ +#ifdef Q_OS_UNIX + if (runs && pid_ > 0 && !::kill(run_mode == OwnGroup ? -pid_ : pid_, signo)) + return true; +#endif + return false; +} + + + +bool TDEProcess::isRunning() const +{ + return runs; +} + + + +pid_t TDEProcess::pid() const +{ + return pid_; +} + +#ifndef timersub +# define timersub(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((result)->tv_usec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_usec += 1000000; \ + } \ + } while (0) +#endif + +bool TDEProcess::wait(int timeout) +{ + if (!runs) + return true; + +#ifndef __linux__ + struct timeval etv; +#endif + struct timeval tv, *tvp; + if (timeout < 0) + tvp = 0; + else + { +#ifndef __linux__ + gettimeofday(&etv, 0); + etv.tv_sec += timeout; +#else + tv.tv_sec = timeout; + tv.tv_usec = 0; +#endif + tvp = &tv; + } + +#ifdef Q_OS_UNIX + int fd = TDEProcessController::theTDEProcessController->notifierFd(); + for(;;) + { + fd_set fds; + FD_ZERO( &fds ); + FD_SET( fd, &fds ); + +#ifndef __linux__ + if (tvp) + { + gettimeofday(&tv, 0); + timersub(&etv, &tv, &tv); + if (tv.tv_sec < 0) + tv.tv_sec = tv.tv_usec = 0; + } +#endif + + switch( select( fd+1, &fds, 0, 0, tvp ) ) + { + case -1: + if( errno == EINTR ) + break; + // fall through; should happen if tvp->tv_sec < 0 + case 0: + TDEProcessController::theTDEProcessController->rescheduleCheck(); + return false; + default: + TDEProcessController::theTDEProcessController->unscheduleCheck(); + if (waitpid(pid_, &status, WNOHANG) != 0) // error finishes, too + { + processHasExited(status); + TDEProcessController::theTDEProcessController->rescheduleCheck(); + return true; + } + } + } +#endif //Q_OS_UNIX + return false; +} + + + +bool TDEProcess::normalExit() const +{ + return (pid_ != 0) && !runs && WIFEXITED(status); +} + + +bool TDEProcess::signalled() const +{ + return (pid_ != 0) && !runs && WIFSIGNALED(status); +} + + +bool TDEProcess::coreDumped() const +{ +#ifdef WCOREDUMP + return signalled() && WCOREDUMP(status); +#else + return false; +#endif +} + + +int TDEProcess::exitStatus() const +{ + return WEXITSTATUS(status); +} + + +int TDEProcess::exitSignal() const +{ + return WTERMSIG(status); +} + + +bool TDEProcess::writeStdin(const char *buffer, int buflen) +{ + // if there is still data pending, writing new data + // to stdout is not allowed (since it could also confuse + // tdeprocess ...) + if (input_data != 0) + return false; + + if (communication & Stdin) { + input_data = buffer; + input_sent = 0; + input_total = buflen; + innot->setEnabled(true); + if (input_total) + slotSendData(0); + return true; + } else + return false; +} + +void TDEProcess::suspend() +{ + if (outnot) + outnot->setEnabled(false); +} + +void TDEProcess::resume() +{ + if (outnot) + outnot->setEnabled(true); +} + +bool TDEProcess::closeStdin() +{ + if (communication & Stdin) { + communication = (Communication) (communication & ~Stdin); + delete innot; + innot = 0; + if (!(d->usePty & Stdin)) + close(in[1]); + in[1] = -1; + return true; + } else + return false; +} + +bool TDEProcess::closeStdout() +{ + if (communication & Stdout) { + communication = (Communication) (communication & ~Stdout); + delete outnot; + outnot = 0; + if (!(d->usePty & Stdout)) + close(out[0]); + out[0] = -1; + return true; + } else + return false; +} + +bool TDEProcess::closeStderr() +{ + if (communication & Stderr) { + communication = (Communication) (communication & ~Stderr); + delete errnot; + errnot = 0; + if (!(d->usePty & Stderr)) + close(err[0]); + err[0] = -1; + return true; + } else + return false; +} + +bool TDEProcess::closePty() +{ +#ifdef Q_OS_UNIX + if (d->pty && d->pty->masterFd() >= 0) { + if (d->addUtmp) + d->pty->logout(); + d->pty->close(); + return true; + } else + return false; +#else + return false; +#endif +} + +void TDEProcess::closeAll() +{ + closeStdin(); + closeStdout(); + closeStderr(); + closePty(); +} + +///////////////////////////// +// protected slots // +///////////////////////////// + + + +void TDEProcess::slotChildOutput(int fdno) +{ + if (!childOutput(fdno)) + closeStdout(); +} + + +void TDEProcess::slotChildError(int fdno) +{ + if (!childError(fdno)) + closeStderr(); +} + + +void TDEProcess::slotSendData(int) +{ + if (input_sent == input_total) { + innot->setEnabled(false); + input_data = 0; + emit wroteStdin(this); + } else { + int result = ::write(in[1], input_data+input_sent, input_total-input_sent); + if (result >= 0) + { + input_sent += result; + } + else if ((errno != EAGAIN) && (errno != EINTR)) + { + kdDebug(175) << "Error writing to stdin of child process" << endl; + closeStdin(); + } + } +} + +void TDEProcess::setUseShell(bool useShell, const char *shell) +{ + d->useShell = useShell; + if (shell && *shell) + d->shell = shell; + else +// #ifdef NON_FREE // ... as they ship non-POSIX /bin/sh +#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__GNU__) && !defined(__DragonFly__) + // Solaris POSIX ... + if (!access( "/usr/xpg4/bin/sh", X_OK )) + d->shell = "/usr/xpg4/bin/sh"; + else + // ... which links here anyway + if (!access( "/bin/ksh", X_OK )) + d->shell = "/bin/ksh"; + else + // dunno, maybe superfluous? + if (!access( "/usr/ucb/sh", X_OK )) + d->shell = "/usr/ucb/sh"; + else +#endif + d->shell = "/bin/sh"; +} + +#ifdef Q_OS_UNIX +void TDEProcess::setUsePty(Communication usePty, bool addUtmp) +{ + d->usePty = usePty; + d->addUtmp = addUtmp; + if (usePty) { + if (!d->pty) + d->pty = new KPty; + } else { + delete d->pty; + d->pty = 0; + } +} + +KPty *TDEProcess::pty() const +{ + return d->pty; +} +#endif //Q_OS_UNIX + +TQString TDEProcess::quote(const TQString &arg) +{ + TQChar q('\''); + return TQString(arg).replace(q, "'\\''").prepend(q).append(q); +} + + +////////////////////////////// +// private member functions // +////////////////////////////// + + +void TDEProcess::processHasExited(int state) +{ + // only successfully run NotifyOnExit processes ever get here + + status = state; + runs = false; // do this before commClose, so it knows we're dead + + commClose(); // cleanup communication sockets + + if (run_mode != DontCare) + emit processExited(this); +} + + + +int TDEProcess::childOutput(int fdno) +{ + if (communication & NoRead) { + int len = -1; + emit receivedStdout(fdno, len); + errno = 0; // Make sure errno doesn't read "EAGAIN" + return len; + } + else + { + char buffer[1025]; + int len; + + len = ::read(fdno, buffer, 1024); + + if (len > 0) { + buffer[len] = 0; // Just in case. + emit receivedStdout(this, buffer, len); + } + return len; + } +} + +int TDEProcess::childError(int fdno) +{ + char buffer[1025]; + int len; + + len = ::read(fdno, buffer, 1024); + + if (len > 0) { + buffer[len] = 0; // Just in case. + emit receivedStderr(this, buffer, len); + } + return len; +} + + +int TDEProcess::setupCommunication(Communication comm) +{ +#ifdef Q_OS_UNIX + // PTY stuff // + if (d->usePty) + { + // cannot communicate on both stderr and stdout if they are both on the pty + if (!(~(comm & d->usePty) & (Stdout | Stderr))) { + kdWarning(175) << "Invalid usePty/communication combination (" << d->usePty << "/" << comm << ")" << endl; + return 0; + } + if (!d->pty->open()) + return 0; + + int rcomm = comm & d->usePty; + int mfd = d->pty->masterFd(); + if (rcomm & Stdin) + in[1] = mfd; + if (rcomm & Stdout) + out[0] = mfd; + if (rcomm & Stderr) + err[0] = mfd; + } + + communication = comm; + + comm = (Communication) (comm & ~d->usePty); + if (comm & Stdin) { + if (socketpair(AF_UNIX, SOCK_STREAM, 0, in)) + goto fail0; + fcntl(in[0], F_SETFD, FD_CLOEXEC); + fcntl(in[1], F_SETFD, FD_CLOEXEC); + } + if (comm & Stdout) { + if (socketpair(AF_UNIX, SOCK_STREAM, 0, out)) + goto fail1; + fcntl(out[0], F_SETFD, FD_CLOEXEC); + fcntl(out[1], F_SETFD, FD_CLOEXEC); + } + if (comm & Stderr) { + if (socketpair(AF_UNIX, SOCK_STREAM, 0, err)) + goto fail2; + fcntl(err[0], F_SETFD, FD_CLOEXEC); + fcntl(err[1], F_SETFD, FD_CLOEXEC); + } + return 1; // Ok + fail2: + if (comm & Stdout) + { + close(out[0]); + close(out[1]); + out[0] = out[1] = -1; + } + fail1: + if (comm & Stdin) + { + close(in[0]); + close(in[1]); + in[0] = in[1] = -1; + } + fail0: + communication = NoCommunication; +#endif //Q_OS_UNIX + return 0; // Error +} + + + +int TDEProcess::commSetupDoneP() +{ + int rcomm = communication & ~d->usePty; + if (rcomm & Stdin) + close(in[0]); + if (rcomm & Stdout) + close(out[1]); + if (rcomm & Stderr) + close(err[1]); + in[0] = out[1] = err[1] = -1; + + // Don't create socket notifiers if no interactive comm is to be expected + if (run_mode != NotifyOnExit && run_mode != OwnGroup) + return 1; + + if (communication & Stdin) { + fcntl(in[1], F_SETFL, O_NONBLOCK | fcntl(in[1], F_GETFL)); + innot = new TQSocketNotifier(in[1], TQSocketNotifier::Write, this); + TQ_CHECK_PTR(innot); + innot->setEnabled(false); // will be enabled when data has to be sent + TQObject::connect(innot, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotSendData(int))); + } + + if (communication & Stdout) { + outnot = new TQSocketNotifier(out[0], TQSocketNotifier::Read, this); + TQ_CHECK_PTR(outnot); + TQObject::connect(outnot, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChildOutput(int))); + if (communication & NoRead) + suspend(); + } + + if (communication & Stderr) { + errnot = new TQSocketNotifier(err[0], TQSocketNotifier::Read, this ); + TQ_CHECK_PTR(errnot); + TQObject::connect(errnot, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChildError(int))); + } + + return 1; +} + + + +int TDEProcess::commSetupDoneC() +{ + int ok = 1; +#ifdef Q_OS_UNIX + + if (d->usePty & Stdin) { + if (dup2(d->pty->slaveFd(), STDIN_FILENO) < 0) ok = 0; + } else if (communication & Stdin) { + if (dup2(in[0], STDIN_FILENO) < 0) ok = 0; + } else { + int null_fd = open( "/dev/null", O_RDONLY ); + if (dup2( null_fd, STDIN_FILENO ) < 0) ok = 0; + close( null_fd ); + } + struct linger so; + memset(&so, 0, sizeof(so)); + if (d->usePty & Stdout) { + if (dup2(d->pty->slaveFd(), STDOUT_FILENO) < 0) ok = 0; + } else if (communication & Stdout) { + if (dup2(out[1], STDOUT_FILENO) < 0 || + setsockopt(out[1], SOL_SOCKET, SO_LINGER, (char *)&so, sizeof(so))) + ok = 0; + if (communication & MergedStderr) { + if (dup2(out[1], STDERR_FILENO) < 0) + ok = 0; + } + } + if (d->usePty & Stderr) { + if (dup2(d->pty->slaveFd(), STDERR_FILENO) < 0) ok = 0; + } else if (communication & Stderr) { + if (dup2(err[1], STDERR_FILENO) < 0 || + setsockopt(err[1], SOL_SOCKET, SO_LINGER, (char *)&so, sizeof(so))) + ok = 0; + } + + // don't even think about closing all open fds here or anywhere else + + // PTY stuff // + if (d->usePty) { + d->pty->setCTty(); + if (d->addUtmp) + d->pty->login(KUser(KUser::UseRealUserID).loginName().local8Bit().data(), getenv("DISPLAY")); + } +#endif //Q_OS_UNIX + + return ok; +} + + + +void TDEProcess::commClose() +{ + closeStdin(); + +#ifdef Q_OS_UNIX + if (pid_) { // detached, failed, and killed processes have no output. basta. :) + // If both channels are being read we need to make sure that one socket + // buffer doesn't fill up whilst we are waiting for data on the other + // (causing a deadlock). Hence we need to use select. + + int notfd = TDEProcessController::theTDEProcessController->notifierFd(); + + while ((communication & (Stdout | Stderr)) || runs) { + fd_set rfds; + FD_ZERO(&rfds); + struct timeval timeout, *p_timeout; + + int max_fd = 0; + if (communication & Stdout) { + FD_SET(out[0], &rfds); + max_fd = out[0]; + } + if (communication & Stderr) { + FD_SET(err[0], &rfds); + if (err[0] > max_fd) + max_fd = err[0]; + } + if (runs) { + FD_SET(notfd, &rfds); + if (notfd > max_fd) + max_fd = notfd; + // If the process is still running we block until we + // receive data or the process exits. + p_timeout = 0; // no timeout + } else { + // If the process has already exited, we only check + // the available data, we don't wait for more. + timeout.tv_sec = timeout.tv_usec = 0; // timeout immediately + p_timeout = &timeout; + } + + int fds_ready = select(max_fd+1, &rfds, 0, 0, p_timeout); + if (fds_ready < 0) { + if (errno == EINTR) + continue; + break; + } else if (!fds_ready) + break; + + if ((communication & Stdout) && FD_ISSET(out[0], &rfds)) + slotChildOutput(out[0]); + + if ((communication & Stderr) && FD_ISSET(err[0], &rfds)) + slotChildError(err[0]); + + if (runs && FD_ISSET(notfd, &rfds)) { + runs = false; // hack: signal potential exit + return; // don't close anything, we will be called again + } + } + } +#endif //Q_OS_UNIX + + closeStdout(); + closeStderr(); + + closePty(); +} + + +void TDEProcess::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + + +/////////////////////////// +// CC: Class KShellProcess +/////////////////////////// + +KShellProcess::KShellProcess(const char *shellname): + TDEProcess() +{ + setUseShell( true, shellname ? shellname : getenv("SHELL") ); +} + +KShellProcess::~KShellProcess() { +} + +TQString KShellProcess::quote(const TQString &arg) +{ + return TDEProcess::quote(arg); +} + +bool KShellProcess::start(RunMode runmode, Communication comm) +{ + return TDEProcess::start(runmode, comm); +} + +void KShellProcess::virtual_hook( int id, void* data ) +{ TDEProcess::virtual_hook( id, data ); } + +#include "tdeprocess.moc" diff --git a/tdecore/tdeprocess.h b/tdecore/tdeprocess.h new file mode 100644 index 000000000..658571ce7 --- /dev/null +++ b/tdecore/tdeprocess.h @@ -0,0 +1,936 @@ +/* This file is part of the KDE libraries + Copyright (C) 1997 Christian Czezakte (e9025461@student.tuwien.ac.at) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __TDEPROCESS_H__ +#define __TDEPROCESS_H__ + +#include // for pid_t +#include +#include +#include +#include +#include +#include +#include "tdelibs_export.h" + +class TQSocketNotifier; +class TDEProcessPrivate; + +#ifdef Q_OS_UNIX +#include +#else +class KPty; +#endif + +/** + * Child process invocation, monitoring and control. + * This class works only in the application's main thread. + * + * General usage and features:\n + * + * This class allows a KDE application to start child processes without having + * to worry about UN*X signal handling issues and zombie process reaping. + * + * @see KProcIO + * + * Basically, this class distinguishes three different ways of running + * child processes: + * + * @li DontCare -- The child process is invoked and both the child + * process and the parent process continue concurrently. + * + * The process is started in an own session (see setsid(2)). + * + * @li NotifyOnExit -- The child process is invoked and both the + * child and the parent process run concurrently. + * + * When the child process exits, the TDEProcess instance + * corresponding to it emits the Qt signal processExited(). + * Since this signal is @em not emitted from within a UN*X + * signal handler, arbitrary function calls can be made. + * + * Be aware: When the TDEProcess object gets destructed, the child + * process will be killed if it is still running! + * This means in particular, that it usually makes no sense to use + * a TDEProcess on the stack with NotifyOnExit. + * + * @li OwnGroup -- like NotifyOnExit, but the child process is started + * in an own process group (and an own session, FWIW). The behavior of + * kill() changes to killing the whole process group - this makes + * this mode useful for implementing primitive job management. It can be + * used to work around broken wrapper scripts that don't propagate signals + * to the "real" program. However, use this with care, as you disturb the + * shell's job management if your program is started from the command line. + * + * @li Block -- The child process starts and the parent process + * is suspended until the child process exits. (@em Really not recommended + * for programs with a GUI.) + * In this mode the parent can read the child's output, but can't send it any + * input. + * + * TDEProcess also provides several functions for determining the exit status + * and the pid of the child process it represents. + * + * Furthermore it is possible to supply command-line arguments to the process + * in a clean fashion (no null-terminated stringlists and such...) + * + * A small usage example: + * \code + * TDEProcess *proc = new TDEProcess; + * + * *proc << "my_executable"; + * *proc << "These" << "are" << "the" << "command" << "line" << "args"; + * TQApplication::connect(proc, TQ_SIGNAL(processExited(TDEProcess *)), + * pointer_to_my_object, TQ_SLOT(my_objects_slot(TDEProcess *))); + * proc->start(); + * \endcode + * + * This will start "my_executable" with the commandline arguments "These"... + * + * When the child process exits, the slot will be invoked. + * + * Communication with the child process:\n + * + * TDEProcess supports communication with the child process through + * stdin/stdout/stderr. + * + * The following functions are provided for getting data from the child + * process or sending data to the child's stdin (For more information, + * have a look at the documentation of each function): + * + * @li writeStdin() + * -- Transmit data to the child process' stdin. When all data was sent, the + * signal wroteStdin() is emitted. + * + * @li When data arrives at stdout or stderr, the signal receivedStdout() + * resp. receivedStderr() is emitted. + * + * @li You can shut down individual communication channels with + * closeStdin(), closeStdout(), and closeStderr(), resp. + * + * @author Christian Czezatke e9025461@student.tuwien.ac.at + * + **/ +class TDECORE_EXPORT TDEProcess : public TQObject +{ + TQ_OBJECT + +public: + + /** + * Modes in which the communication channel can be opened. + * + * If communication for more than one channel is required, + * the values have to be or'ed together, for example to get + * communication with stdout as well as with stdin, you would + * specify @p Stdin | @p Stdout + * + * If @p NoRead is specified in conjunction with @p Stdout, + * no data is actually read from @p Stdout but only + * the signal receivedStdout(int fd, int &len) is emitted. + * + * @p CTtyOnly tells setUsePty() to create a PTY for the process + * and make it the process' controlling TTY, but does not redirect + * any I/O channel to the PTY. + * + * If @p MergedStderr is specified in conjunction with @p Stdout, + * Stderr will be redirected onto the same file handle as Stdout, + * i.e., all error output will be signalled with receivedStdout(). + * Don't specify @p Stderr if you specify @p MergedStderr. + */ + enum Communication { + NoCommunication = 0, + Stdin = 1, Stdout = 2, Stderr = 4, + AllOutput = 6, All = 7, + NoRead = 8, + CTtyOnly = NoRead, + MergedStderr = 16 + }; + + /** + * Run-modes for a child process. + */ + enum RunMode { + /** + * The application does not receive notifications from the subprocess when + * it is finished or aborted. + */ + DontCare, + /** + * The application is notified when the subprocess dies. + */ + NotifyOnExit, + /** + * The application is suspended until the started process is finished. + */ + Block, + /** + * Same as NotifyOnExit, but the process is run in an own session, + * just like with DontCare. + */ + OwnGroup + }; + + /** + * Constructor + * @since 3.2 + */ + TDEProcess( TQObject* parent, const char *name = 0 ); + + /** + * Constructor + */ // KDE4 merge with the above + TDEProcess(); + + /** + *Destructor: + * + * If the process is running when the destructor for this class + * is called, the child process is killed with a SIGKILL, but + * only if the run mode is not of type @p DontCare. + * Processes started as @p DontCare keep running anyway. + */ + virtual ~TDEProcess(); + + /** + @deprecated + Use operator<<() instead. + + Sets the executable to be started with this TDEProcess object. + Returns false if the process is currently running (in that + case the executable remains unchanged). + + @see operator<<() + + */ + bool setExecutable(const TQString& proc) TDE_DEPRECATED; + + + /** + * Sets the executable and the command line argument list for this process. + * + * For example, doing an "ls -l /usr/local/bin" can be achieved by: + * \code + * TDEProcess p; + * ... + * p << "ls" << "-l" << "/usr/local/bin" + * \endcode + * + * @param arg the argument to add + * @return a reference to this TDEProcess + **/ + TDEProcess &operator<<(const TQString& arg); + /** + * Similar to previous method, takes a char *, supposed to be in locale 8 bit already. + */ + TDEProcess &operator<<(const char * arg); + /** + * Similar to previous method, takes a TQCString, supposed to be in locale 8 bit already. + * @param arg the argument to add + * @return a reference to this TDEProcess + */ + TDEProcess &operator<<(const TQCString & arg); + + /** + * Sets the executable and the command line argument list for this process, + * in a single method call, or add a list of arguments. + * @param args the arguments to add + * @return a reference to this TDEProcess + **/ + TDEProcess &operator<<(const TQStringList& args); + + /** + * Clear a command line argument list that has been set by using + * operator<<. + */ + void clearArguments(); + + /** + * Starts the process. + * For a detailed description of the + * various run modes and communication semantics, have a look at the + * general description of the TDEProcess class. Note that if you use + * setUsePty( Stdout | Stderr, \ ), you cannot use Stdout | Stderr + * here - instead, use Stdout only to receive the mixed output. + * + * The following problems could cause this function to + * return false: + * + * @li The process is already running. + * @li The command line argument list is empty. + * @li The the @p comm parameter is incompatible with the selected pty usage. + * @li The starting of the process failed (could not fork). + * @li The executable was not found. + * + * @param runmode The Run-mode for the process. + * @param comm Specifies which communication links should be + * established to the child process (stdin/stdout/stderr). By default, + * no communication takes place and the respective communication + * signals will never get emitted. + * + * @return true on success, false on error + * (see above for error conditions) + **/ + virtual bool start(RunMode runmode = NotifyOnExit, + Communication comm = NoCommunication); + + /** + * Stop the process (by sending it a signal). + * + * @param signo The signal to send. The default is SIGTERM. + * @return true if the signal was delivered successfully. + */ + virtual bool kill(int signo = SIGTERM); + + /** + * Checks whether the process is running. + * @return true if the process is (still) considered to be running + */ + bool isRunning() const; + + /** Returns the process id of the process. + * + * If it is called after + * the process has exited, it returns the process id of the last + * child process that was created by this instance of TDEProcess. + * + * Calling it before any child process has been started by this + * TDEProcess instance causes pid() to return 0. + * @return the pid of the process or 0 if no process has been started yet. + **/ + pid_t pid() const; + + /** + * @deprecated + * Use pid() instead. + */ + TDE_DEPRECATED pid_t getPid() const { return pid(); } + + /** + * Suspend processing of data from stdout of the child process. + */ + void suspend(); + + /** + * Resume processing of data from stdout of the child process. + */ + void resume(); + + /** + * Suspend execution of the current thread until the child process dies + * or the timeout hits. This function is not recommended for programs + * with a GUI. + * @param timeout timeout in seconds. -1 means wait indefinitely. + * @return true if the process exited, false if the timeout hit. + * @since 3.2 + */ + bool wait(int timeout = -1); + + /** + * Checks whether the process exited cleanly. + * + * @return true if the process has already finished and has exited + * "voluntarily", ie: it has not been killed by a signal. + */ + bool normalExit() const; + + /** + * Checks whether the process was killed by a signal. + * + * @return true if the process has already finished and has not exited + * "voluntarily", ie: it has been killed by a signal. + * + * @since 3.2 + */ + bool signalled() const; + + /** + * Checks whether a killed process dumped core. + * + * @return true if signalled() returns true and the process + * dumped core. Note that on systems that don't define the + * WCOREDUMP macro, the return value is always false. + * + * @since 3.2 + */ + bool coreDumped() const; + + /** + * Returns the exit status of the process. + * + * @return the exit status of the process. Note that this value + * is not valid if normalExit() returns false. + */ + int exitStatus() const; + + /** + * Returns the signal the process was killed by. + * + * @return the signal number that caused the process to exit. + * Note that this value is not valid if signalled() returns false. + * + * @since 3.2 + */ + int exitSignal() const; + + /** + * Transmit data to the child process' stdin. + * + * This function may return false in the following cases: + * + * @li The process is not currently running. + * This implies that you cannot use this function in Block mode. + * + * @li Communication to stdin has not been requested in the start() call. + * + * @li Transmission of data to the child process by a previous call to + * writeStdin() is still in progress. + * + * Please note that the data is sent to the client asynchronously, + * so when this function returns, the data might not have been + * processed by the child process. + * That means that you must not free @p buffer or call writeStdin() + * again until either a wroteStdin() signal indicates that the + * data has been sent or a processExited() signal shows that + * the child process is no longer alive. + * + * If all the data has been sent to the client, the signal + * wroteStdin() will be emitted. + * + * This function does not work when the process is start()ed in Block mode. + * + * @param buffer the buffer to write + * @param buflen the length of the buffer + * @return false if an error has occurred + **/ + bool writeStdin(const char *buffer, int buflen); + + /** + * Shuts down the Stdin communication link. If no pty is used, this + * causes "EOF" to be indicated on the child's stdin file descriptor. + * + * @return false if no Stdin communication link exists (any more). + */ + bool closeStdin(); + + /** + * Shuts down the Stdout communication link. If no pty is used, any further + * attempts by the child to write to its stdout file descriptor will cause + * it to receive a SIGPIPE. + * + * @return false if no Stdout communication link exists (any more). + */ + bool closeStdout(); + + /** + * Shuts down the Stderr communication link. If no pty is used, any further + * attempts by the child to write to its stderr file descriptor will cause + * it to receive a SIGPIPE. + * + * @return false if no Stderr communication link exists (any more). + */ + bool closeStderr(); + + /** + * Deletes the optional utmp entry and closes the pty. + * + * Make sure to shut down any communication links that are using the pty + * before calling this function. + * + * @return false if the pty is not open (any more). + */ + bool closePty(); + + /** + * @brief Close stdin, stdout, stderr and the pty + * + * This is the same that calling all close* functions in a row: + * @see closeStdin, @see closeStdout, @see closeStderr and @see closePty + */ + void closeAll(); + + /** + * Lets you see what your arguments are for debugging. + * @return the list of arguments + */ + const TQValueList &args() /* const */ { return arguments; } + + /** + * Controls whether the started process should drop any + * setuid/setgid privileges or whether it should keep them. + * Note that this function is mostly a dummy, as the KDE libraries + * currently refuse to run with setuid/setgid privileges. + * + * The default is false: drop privileges + * @param keepPrivileges true to keep the privileges + */ + void setRunPrivileged(bool keepPrivileges); + + /** + * Returns whether the started process will drop any + * setuid/setgid privileges or whether it will keep them. + * @return true if the process runs privileged + */ + bool runPrivileged() const; + + /** + * Adds the variable @p name to the process' environment. + * This function must be called before starting the process. + * @param name the name of the environment variable + * @param value the new value for the environment variable + */ + void setEnvironment(const TQString &name, const TQString &value); + + /** + * Changes the current working directory (CWD) of the process + * to be started. + * This function must be called before starting the process. + * @param dir the new directory + */ + void setWorkingDirectory(const TQString &dir); + + /** + * Specify whether to start the command via a shell or directly. + * The default is to start the command directly. + * If @p useShell is true @p shell will be used as shell, or + * if shell is empty, /bin/sh will be used. + * + * When using a shell, the caller should make sure that all filenames etc. + * are properly quoted when passed as argument. + * @see quote() + * @param useShell true if the command should be started via a shell + * @param shell the path to the shell that will execute the process, or + * 0 to use /bin/sh. Use getenv("SHELL") to use the user's + * default shell, but note that doing so is usually a bad idea + * for shell compatibility reasons. + * @since 3.1 + */ + void setUseShell(bool useShell, const char *shell = 0); + + /** + * This function can be used to quote an argument string such that + * the shell processes it properly. This is e. g. necessary for + * user-provided file names which may contain spaces or quotes. + * It also prevents expansion of wild cards and environment variables. + * @param arg the argument to quote + * @return the quoted argument + * @since 3.1 + */ + static TQString quote(const TQString &arg); + + /** + * Detaches TDEProcess from child process. All communication is closed. + * No exit notification is emitted any more for the child process. + * Deleting the TDEProcess will no longer kill the child process. + * Note that the current process remains the parent process of the + * child process. + */ + void detach(); + +#ifdef Q_OS_UNIX + /** + * Specify whether to create a pty (pseudo-terminal) for running the + * command. + * This function should be called before starting the process. + * + * @param comm for which stdio handles to use a pty. Note that it is not + * allowed to specify Stdout and Stderr at the same time both here and to + * start (there is only one pty, so they cannot be distinguished). + * @param addUtmp true if a utmp entry should be created for the pty + * @since 3.2 + */ + void setUsePty(Communication comm, bool addUtmp); + + /** + * Obtains the pty object used by this process. The return value is + * valid only after setUsePty() was used with a non-zero argument. + * The pty is open only while the process is running. + * @return a pointer to the pty object + * @since 3.2 + */ + KPty *pty() const; +#endif + + /** + * More or less intuitive constants for use with setPriority(). + */ + enum { PrioLowest = 20, PrioLow = 10, PrioLower = 5, PrioNormal = 0, + PrioHigher = -5, PrioHigh = -10, PrioHighest = -19 }; + + /** + * Sets the scheduling priority of the process. + * @param prio the new priority in the range -20 (high) to 19 (low). + * @return false on error; see setpriority(2) for possible reasons. + * @since 3.2 + */ + bool setPriority(int prio); + +signals: + /** + * Emitted after the process has terminated when + * the process was run in the @p NotifyOnExit (==default option to + * start() ) or the Block mode. + * @param proc a pointer to the process that has exited + **/ + void processExited(TDEProcess *proc); + + + /** + * Emitted, when output from the child process has + * been received on stdout. + * + * To actually get this signal, the Stdout communication link + * has to be turned on in start(). + * + * @param proc a pointer to the process that has received the output + * @param buffer The data received. + * @param buflen The number of bytes that are available. + * + * You should copy the information contained in @p buffer to your private + * data structures before returning from the slot. + * Example: + * \code + * TQString myBuf = TQString::fromLatin1(buffer, buflen); + * \endcode + **/ + void receivedStdout(TDEProcess *proc, char *buffer, int buflen); + + /** + * Emitted when output from the child process has + * been received on stdout. + * + * To actually get this signal, the Stdout communication link + * has to be turned on in start() and the + * NoRead flag must have been passed. + * + * You will need to explicitly call resume() after your call to start() + * to begin processing data from the child process' stdout. This is + * to ensure that this signal is not emitted when no one is connected + * to it, otherwise this signal will not be emitted. + * + * The data still has to be read from file descriptor @p fd. + * @param fd the file descriptor that provides the data + * @param len the number of bytes that have been read from @p fd must + * be written here + **/ + void receivedStdout(int fd, int &len); // KDE4: change, broken API + + + /** + * Emitted, when output from the child process has + * been received on stderr. + * + * To actually get this signal, the Stderr communication link + * has to be turned on in start(). + * + * You should copy the information contained in @p buffer to your private + * data structures before returning from the slot. + * + * @param proc a pointer to the process that has received the data + * @param buffer The data received. + * @param buflen The number of bytes that are available. + **/ + void receivedStderr(TDEProcess *proc, char *buffer, int buflen); + + /** + * Emitted after all the data that has been + * specified by a prior call to writeStdin() has actually been + * written to the child process. + * @param proc a pointer to the process + **/ + void wroteStdin(TDEProcess *proc); + + +protected slots: + + /** + * This slot gets activated when data from the child's stdout arrives. + * It usually calls childOutput(). + * @param fdno the file descriptor for the output + */ + void slotChildOutput(int fdno); + + /** + * This slot gets activated when data from the child's stderr arrives. + * It usually calls childError(). + * @param fdno the file descriptor for the output + */ + void slotChildError(int fdno); + + /** + * Called when another bulk of data can be sent to the child's + * stdin. If there is no more data to be sent to stdin currently + * available, this function must disable the TQSocketNotifier innot. + * @param dummy ignore this argument + */ + void slotSendData(int dummy); // KDE 4: remove dummy + +protected: + + /** + * Sets up the environment according to the data passed via + * setEnvironment() + */ + void setupEnvironment(); + + /** + * The list of the process' command line arguments. The first entry + * in this list is the executable itself. + */ + TQValueList arguments; + /** + * How to run the process (Block, NotifyOnExit, DontCare). You should + * not modify this data member directly from derived classes. + */ + RunMode run_mode; + /** + * true if the process is currently running. You should not + * modify this data member directly from derived classes. Please use + * isRunning() for reading the value of this data member since it + * will probably be made private in later versions of TDEProcess. + */ + bool runs; + + /** + * The PID of the currently running process. + * You should not modify this data member in derived classes. + * Please use pid() instead of directly accessing this + * member since it will probably be made private in + * later versions of TDEProcess. + */ + pid_t pid_; + + /** + * The process' exit status as returned by waitpid(). You should not + * modify the value of this data member from derived classes. You should + * rather use exitStatus() than accessing this data member directly + * since it will probably be made private in further versions of + * TDEProcess. + */ + int status; + + + /** + * If false, the child process' effective uid & gid will be reset to the + * real values. + * @see setRunPrivileged() + */ + bool keepPrivs; + + /** + * This function is called from start() right before a fork() takes + * place. According to the @p comm parameter this function has to initialize + * the in, out and err data members of TDEProcess. + * + * This function should return 1 if setting the needed communication channels + * was successful. + * + * The default implementation is to create UNIX STREAM sockets for the + * communication, but you could reimplement this function to establish a + * TCP/IP communication for network communication, for example. + */ + virtual int setupCommunication(Communication comm); + + /** + * Called right after a (successful) fork() on the parent side. This function + * will usually do some communications cleanup, like closing in[0], + * out[1] and out[1]. + * + * Furthermore, it must also create the TQSocketNotifiers innot, + * outnot and errnot and connect their Qt signals to the respective + * TDEProcess slots. + * + * For a more detailed explanation, it is best to have a look at the default + * implementation in tdeprocess.cpp. + */ + virtual int commSetupDoneP(); + + /** + * Called right after a (successful) fork(), but before an exec() on the child + * process' side. It usually duplicates the in[0], out[1] and + * err[1] file handles to the respective standard I/O handles. + */ + virtual int commSetupDoneC(); + + + /** + * Immediately called after a successfully started process in NotifyOnExit + * mode has exited. This function normally calls commClose() + * and emits the processExited() signal. + * @param state the exit code of the process as returned by waitpid() + */ + virtual void processHasExited(int state); + + /** + * Cleans up the communication links to the child after it has exited. + * This function should act upon the values of pid() and runs. + * See the tdeprocess.cpp source for details. + * @li If pid() returns zero, the communication links should be closed + * only. + * @li if pid() returns non-zero and runs is false, all data + * immediately available from the communication links should be processed + * before closing them. + * @li if pid() returns non-zero and runs is true, the communication + * links should be monitored for data until the file handle returned by + * TDEProcessController::theTDEProcessController->notifierFd() becomes ready + * for reading - when it triggers, runs should be reset to false, and + * the function should be immediately left without closing anything. + * + * The previous semantics of this function are forward-compatible, but should + * be avoided, as they are prone to race conditions and can cause TDEProcess + * (and thus the whole program) to lock up under certain circumstances. At the + * end the function closes the communication links in any case. Additionally + * @li if runs is true, the communication links are monitored for data + * until all of them have returned EOF. Note that if any system function is + * interrupted (errno == EINTR) the polling loop should be aborted. + * @li if runs is false, all data immediately available from the + * communication links is processed. + */ + virtual void commClose(); + + /* KDE 4 - commClose will be changed to perform cleanup only in all cases * + * If @p notfd is -1, all data immediately available from the + * communication links should be processed. + * If @p notfd is not -1, the communication links should be monitored + * for data until the file handle @p notfd becomes ready for reading. + */ +// virtual void commDrain(int notfd); + + /** + * Specify the actual executable that should be started (first argument to execve) + * Normally the the first argument is the executable but you can + * override that with this function. + */ + void setBinaryExecutable(const char *filename); + + /** + * The socket descriptors for stdout. + */ + int out[2]; + /** + * The socket descriptors for stdin. + */ + int in[2]; + /** + * The socket descriptors for stderr. + */ + int err[2]; + + /** + * The socket notifier for in[1]. + */ + TQSocketNotifier *innot; + /** + * The socket notifier for out[0]. + */ + TQSocketNotifier *outnot; + /** + * The socket notifier for err[0]. + */ + TQSocketNotifier *errnot; + + /** + * Lists the communication links that are activated for the child + * process. Should not be modified from derived classes. + */ + Communication communication; + + /** + * Called by slotChildOutput() this function copies data arriving from + * the child process' stdout to the respective buffer and emits the signal + * receivedStdout(). + */ + int childOutput(int fdno); + + /** + * Called by slotChildError() this function copies data arriving from + * the child process' stderr to the respective buffer and emits the signal + * receivedStderr(). + */ + int childError(int fdno); + + /** + * The buffer holding the data that has to be sent to the child + */ + const char *input_data; + /** + * The number of bytes already transmitted + */ + int input_sent; + /** + * The total length of input_data + */ + int input_total; + + /** + * TDEProcessController is a friend of TDEProcess because it has to have + * access to various data members. + */ + friend class TDEProcessController; + +protected: + virtual void virtual_hook( int id, void* data ); +private: + TDEProcessPrivate *d; +}; + +class KShellProcessPrivate; + +/** +* @obsolete +* +* Use TDEProcess and TDEProcess::setUseShell(true) instead. +* +* @short A class derived from TDEProcess to start child +* processes through a shell. +* @author Christian Czezatke +*/ +class TDECORE_EXPORT KShellProcess: public TDEProcess +{ + TQ_OBJECT + +public: + + /** + * Constructor + * + * If no shellname is specified, the user's default shell is used. + */ + KShellProcess(const char *shellname=0); + + /** + * Destructor. + */ + ~KShellProcess(); + + virtual bool start(RunMode runmode = NotifyOnExit, + Communication comm = NoCommunication); + + static TQString quote(const TQString &arg); + +private: + TQCString shell; + +protected: + virtual void virtual_hook( int id, void* data ); +private: + KShellProcessPrivate *d; +}; + +#endif diff --git a/tdecore/tdesycoca.cpp b/tdecore/tdesycoca.cpp index b39ca239e..b1b150df7 100644 --- a/tdecore/tdesycoca.cpp +++ b/tdecore/tdesycoca.cpp @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include diff --git a/tdecore/tests/CMakeLists.txt b/tdecore/tests/CMakeLists.txt index f3e0aae45..fa14f6357 100644 --- a/tdecore/tests/CMakeLists.txt +++ b/tdecore/tests/CMakeLists.txt @@ -31,7 +31,7 @@ tde_add_library( tdeconfigtest SHARED AUTOMOC ) set( CHECKS - tdeconfigtestgui klocaletest kprocesstest ksimpleconfigtest kstddirstest + tdeconfigtestgui klocaletest tdeprocesstest ksimpleconfigtest kstddirstest tdeuniqueapptest ktempfiletest krandomsequencetest kdebugtest ksocktest kstringhandlertest kcmdlineargstest kapptest kmemtest dcopkonqtest kipctest cplusplustest kiconloadertest kresolvertest diff --git a/tdecore/tests/Makefile.am b/tdecore/tests/Makefile.am index 9398716eb..19787c4df 100644 --- a/tdecore/tests/Makefile.am +++ b/tdecore/tests/Makefile.am @@ -21,7 +21,7 @@ INCLUDES = -I$(top_srcdir)/tdecore $(all_includes) AM_LDFLAGS = $(QT_LDFLAGS) $(X_LDFLAGS) $(KDE_RPATH) -check_PROGRAMS = tdeconfigtestgui klocaletest kprocesstest ksimpleconfigtest \ +check_PROGRAMS = tdeconfigtestgui klocaletest tdeprocesstest ksimpleconfigtest \ kstddirstest kurltest tdeuniqueapptest ktempfiletest krandomsequencetest \ kdebugtest ksocktest kstringhandlertest kcmdlineargstest kapptest \ kmemtest kidlservertest kidlclienttest dcopkonqtest kipctest \ @@ -32,7 +32,7 @@ check_PROGRAMS = tdeconfigtestgui klocaletest kprocesstest ksimpleconfigtest \ TESTS = kurltest tdestdacceltest -noinst_HEADERS = klocaletest.h kprocesstest.h KIDLTest.h \ +noinst_HEADERS = klocaletest.h tdeprocesstest.h KIDLTest.h \ kipctest.h kprociotest.h METASOURCES = AUTO @@ -46,7 +46,7 @@ klocaletest_SOURCES = klocaletest.cpp ksimpleconfigtest_SOURCES = ksimpleconfigtest.cpp kurltest_SOURCES = kurltest.cpp kstddirstest_SOURCES = kstddirstest.cpp -kprocesstest_SOURCES = kprocesstest.cpp +tdeprocesstest_SOURCES = tdeprocesstest.cpp tdeuniqueapptest_SOURCES = tdeuniqueapptest.cpp kapptest_SOURCES = kapptest.cpp ksocktest_SOURCES = ksocktest.cpp diff --git a/tdecore/tests/kprocesstest.cpp b/tdecore/tests/kprocesstest.cpp deleted file mode 100644 index b7908ec6e..000000000 --- a/tdecore/tests/kprocesstest.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// -// MAIN -- a little demo of the capabilities of the "TDEProcess" class -// -// version 0.2, Aug 2nd 1997 -// $Id$ -// -// (C) Christian Czezatke -// e9025461@student.tuwien.ac.at -// - - -#include "kprocess.h" - -#include -#include -#include - -#include - -#include "kprocesstest.h" - -#define PROCNO 10 - - -// -// A nice input for "sort"... ;- ) -// -static const char txt[] = "hat\nder\nalte\nhexenmeister\nsich\ndoch\neinmal\nwegbegeben\n\ -und\nnun\nsollen\nseine\ngeister\nsich\nnach\nmeinem\nwillen\nregen\nseine\nwort\nund\n\ -werke\nmerkt\nich\nund\nden\nbrauch\nund\nmit\ngeistesstaerke\ntu\nich\nwunder\nauch\n"; - - -int main(int argc, char *argv[]) -{ - TDEProcess p1, p2, p3, p4; - Dummy dummy; - TDEApplication app(argc, argv, TQCString("kprocesstest")); - - - printf("Welcome to the TDEProcess Demo Application!\n"); - - // - // The kghostview demo -- Starts a kghostview instance blocking. -- After - // kghostview has exited, kghostview is restarted non-blocking. When the process exits, - // the signal "processExited" will be emitted. - // - - p1 << "kghostview"; - TQObject::connect(&p1, TQ_SIGNAL(processExited(TDEProcess *)), &dummy, TQ_SLOT(printMessage(TDEProcess *))); - - printf("starting kghostview blocking (close to continue)\n"); - p1.start(TDEProcess::Block); - printf("restarting kghostview non blocking\n"); - p1.start(); - - - // - // A konsole with tcsh to demonstrate how to pass command line options to a process - // with "TDEProcess" (is run blocking) - // - - printf("Starting konsole with /bin/tcsh as shell (close to continue)\n"); - p2 << "konsole" << "-e" << "/bin/tcsh"; - p2.setWorkingDirectory("/tmp"); - TQObject::connect(&p2, TQ_SIGNAL(processExited(TDEProcess *)), &dummy, TQ_SLOT(printMessage(TDEProcess *))); - p2.start(TDEProcess::Block); - - // - // Getting the output from a process. "ls" with parameter "-l" is called and it output is captured - // - - p3 << "ls" << "-l"; - TQObject::connect(&p3, TQ_SIGNAL(processExited(TDEProcess *)), - &dummy, TQ_SLOT(printMessage(TDEProcess *))); - - TQObject::connect(&p3, TQ_SIGNAL(receivedStdout(TDEProcess *, char *, int)), - &dummy, TQ_SLOT(gotOutput(TDEProcess *, char *, int))); - TQObject::connect(&p3, TQ_SIGNAL(receivedStderr(TDEProcess *, char *, int)), - &dummy, TQ_SLOT(gotOutput(TDEProcess *, char *, int))); - - p3.start(TDEProcess::NotifyOnExit, TDEProcess::AllOutput); - - - // - // An even more advanced example of communicating with a child proces. -- A "sort" command - // is started. After it has been started a list of words (as stored in "txt") is written - // to its stdin. When the sort command has absorbed all its input it will emit the signal - // "inputSent". -- This signal is connected to "outputDone" in the Dummy object. - // - // "OutputDone" will do a "sendEof" to p4. -- This will cause "sort" to perform its task. - // The output of sort is then captured once more by connecting to the signal "outputWaiting" - // - // - - p4 << "sort"; - TQObject::connect(&p4, TQ_SIGNAL(processExited(TDEProcess *)), - &dummy, TQ_SLOT(printMessage(TDEProcess *))); - - TQObject::connect(&p4, TQ_SIGNAL(receivedStdout(TDEProcess *, char *, int)), - &dummy, TQ_SLOT(gotOutput(TDEProcess *, char *, int))); - TQObject::connect(&p4, TQ_SIGNAL(receivedStderr(TDEProcess *, char *, int)), - &dummy, TQ_SLOT(gotOutput(TDEProcess *, char *, int))); - - TQObject::connect(&p4, TQ_SIGNAL(wroteStdin(TDEProcess *)), - &dummy, TQ_SLOT(outputDone(TDEProcess *))); - - p4.start(TDEProcess::NotifyOnExit, TDEProcess::All); - printf("after p4.start"); - p4.writeStdin(txt, strlen(txt)); - - printf("Entering man Qt event loop -- press to abort\n"); - app.exec(); - - return 0; -} -#include "kprocesstest.moc" diff --git a/tdecore/tests/kprocesstest.h b/tdecore/tests/kprocesstest.h deleted file mode 100644 index 4bd4ab71e..000000000 --- a/tdecore/tests/kprocesstest.h +++ /dev/null @@ -1,50 +0,0 @@ -// -// DUMMY -- A dummy class with a slot to demonstrate TDEProcess signals -// -// version 0.2, Aug 2nd 1997 -// -// (C) Christian Czezatke -// e9025461@student.tuwien.ac.at -// - - -#ifndef __DUMMY_H__ -#define __DUMMY_H__ - -#include -#include -#include "kprocess.h" - -class Dummy : public TQObject -{ - TQ_OBJECT - - public slots: - void printMessage(TDEProcess *proc) - { - printf("Process %d exited!\n", (int)proc->getPid()); - } - - void gotOutput(TDEProcess*, char *buffer, int len) - { - char result[1025]; // this is ugly since it relys on the internal buffer size of TDEProcess, - memcpy(result, buffer, len); // NEVER do that in your own application... ;-) - result[len] = '\0'; - printf("OUTPUT>>%s", result); - } - - void outputDone(TDEProcess *proc) - /* - Slot Procedure for the "sort" example. -- If it is indicated that the "sort" command has - absorbed all its input, we send an "EOF" to it to indicate that there is no more - data to be processed. - */ - { - proc->closeStdin(); - } - -}; - -#endif - - diff --git a/tdecore/tests/kprociotest.cpp b/tdecore/tests/kprociotest.cpp index e7165092c..81522f394 100644 --- a/tdecore/tests/kprociotest.cpp +++ b/tdecore/tests/kprociotest.cpp @@ -9,7 +9,7 @@ // -#include "kprocess.h" +#include "tdeprocess.h" #include #include diff --git a/tdecore/tests/tdeprocesstest.cpp b/tdecore/tests/tdeprocesstest.cpp new file mode 100644 index 000000000..0d27f69fa --- /dev/null +++ b/tdecore/tests/tdeprocesstest.cpp @@ -0,0 +1,116 @@ +// +// MAIN -- a little demo of the capabilities of the "TDEProcess" class +// +// version 0.2, Aug 2nd 1997 +// $Id$ +// +// (C) Christian Czezatke +// e9025461@student.tuwien.ac.at +// + + +#include "tdeprocess.h" + +#include +#include +#include + +#include + +#include "tdeprocesstest.h" + +#define PROCNO 10 + + +// +// A nice input for "sort"... ;- ) +// +static const char txt[] = "hat\nder\nalte\nhexenmeister\nsich\ndoch\neinmal\nwegbegeben\n\ +und\nnun\nsollen\nseine\ngeister\nsich\nnach\nmeinem\nwillen\nregen\nseine\nwort\nund\n\ +werke\nmerkt\nich\nund\nden\nbrauch\nund\nmit\ngeistesstaerke\ntu\nich\nwunder\nauch\n"; + + +int main(int argc, char *argv[]) +{ + TDEProcess p1, p2, p3, p4; + Dummy dummy; + TDEApplication app(argc, argv, TQCString("tdeprocesstest")); + + + printf("Welcome to the TDEProcess Demo Application!\n"); + + // + // The kghostview demo -- Starts a kghostview instance blocking. -- After + // kghostview has exited, kghostview is restarted non-blocking. When the process exits, + // the signal "processExited" will be emitted. + // + + p1 << "kghostview"; + TQObject::connect(&p1, TQ_SIGNAL(processExited(TDEProcess *)), &dummy, TQ_SLOT(printMessage(TDEProcess *))); + + printf("starting kghostview blocking (close to continue)\n"); + p1.start(TDEProcess::Block); + printf("restarting kghostview non blocking\n"); + p1.start(); + + + // + // A konsole with tcsh to demonstrate how to pass command line options to a process + // with "TDEProcess" (is run blocking) + // + + printf("Starting konsole with /bin/tcsh as shell (close to continue)\n"); + p2 << "konsole" << "-e" << "/bin/tcsh"; + p2.setWorkingDirectory("/tmp"); + TQObject::connect(&p2, TQ_SIGNAL(processExited(TDEProcess *)), &dummy, TQ_SLOT(printMessage(TDEProcess *))); + p2.start(TDEProcess::Block); + + // + // Getting the output from a process. "ls" with parameter "-l" is called and it output is captured + // + + p3 << "ls" << "-l"; + TQObject::connect(&p3, TQ_SIGNAL(processExited(TDEProcess *)), + &dummy, TQ_SLOT(printMessage(TDEProcess *))); + + TQObject::connect(&p3, TQ_SIGNAL(receivedStdout(TDEProcess *, char *, int)), + &dummy, TQ_SLOT(gotOutput(TDEProcess *, char *, int))); + TQObject::connect(&p3, TQ_SIGNAL(receivedStderr(TDEProcess *, char *, int)), + &dummy, TQ_SLOT(gotOutput(TDEProcess *, char *, int))); + + p3.start(TDEProcess::NotifyOnExit, TDEProcess::AllOutput); + + + // + // An even more advanced example of communicating with a child proces. -- A "sort" command + // is started. After it has been started a list of words (as stored in "txt") is written + // to its stdin. When the sort command has absorbed all its input it will emit the signal + // "inputSent". -- This signal is connected to "outputDone" in the Dummy object. + // + // "OutputDone" will do a "sendEof" to p4. -- This will cause "sort" to perform its task. + // The output of sort is then captured once more by connecting to the signal "outputWaiting" + // + // + + p4 << "sort"; + TQObject::connect(&p4, TQ_SIGNAL(processExited(TDEProcess *)), + &dummy, TQ_SLOT(printMessage(TDEProcess *))); + + TQObject::connect(&p4, TQ_SIGNAL(receivedStdout(TDEProcess *, char *, int)), + &dummy, TQ_SLOT(gotOutput(TDEProcess *, char *, int))); + TQObject::connect(&p4, TQ_SIGNAL(receivedStderr(TDEProcess *, char *, int)), + &dummy, TQ_SLOT(gotOutput(TDEProcess *, char *, int))); + + TQObject::connect(&p4, TQ_SIGNAL(wroteStdin(TDEProcess *)), + &dummy, TQ_SLOT(outputDone(TDEProcess *))); + + p4.start(TDEProcess::NotifyOnExit, TDEProcess::All); + printf("after p4.start"); + p4.writeStdin(txt, strlen(txt)); + + printf("Entering man Qt event loop -- press to abort\n"); + app.exec(); + + return 0; +} +#include "tdeprocesstest.moc" diff --git a/tdecore/tests/tdeprocesstest.h b/tdecore/tests/tdeprocesstest.h new file mode 100644 index 000000000..32a559157 --- /dev/null +++ b/tdecore/tests/tdeprocesstest.h @@ -0,0 +1,48 @@ +// +// DUMMY -- A dummy class with a slot to demonstrate TDEProcess signals +// +// version 0.2, Aug 2nd 1997 +// +// (C) Christian Czezatke +// e9025461@student.tuwien.ac.at +// + + +#ifndef __DUMMY_H__ +#define __DUMMY_H__ + +#include +#include +#include "tdeprocess.h" + +class Dummy : public TQObject +{ + TQ_OBJECT + + public slots: + void printMessage(TDEProcess *proc) + { + printf("Process %d exited!\n", (int)proc->getPid()); + } + + void gotOutput(TDEProcess*, char *buffer, int len) + { + char result[1025]; // this is ugly since it relys on the internal buffer size of TDEProcess, + memcpy(result, buffer, len); // NEVER do that in your own application... ;-) + result[len] = '\0'; + printf("OUTPUT>>%s", result); + } + + void outputDone(TDEProcess *proc) + /* + Slot Procedure for the "sort" example. -- If it is indicated that the "sort" command has + absorbed all its input, we send an "EOF" to it to indicate that there is no more + data to be processed. + */ + { + proc->closeStdin(); + } + +}; + +#endif -- cgit v1.2.1