You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdelibs/tdecore/kprocess.cpp

1138 lines
24 KiB

/*
$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 <config.h>
#ifdef __sgi
#define __svr4__
#endif
#ifdef __osf__
#define _OSF_SOURCE
#include <float.h>
#endif
#ifdef _AIX
#define _ALL_SOURCE
#endif
#ifdef Q_OS_UNIX
#include <sys/socket.h>
#include <sys/ioctl.h>
#endif
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/wait.h>
#ifdef HAVE_SYS_STROPTS_H
#include <sys/stropts.h> // Defines I_PUSH
#define _NEW_TTY_CTRL
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include <time.h>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <tqfile.h>
#include <tqsocketnotifier.h>
#include <tqapplication.h>
#include <kdebug.h>
#include <kstandarddirs.h>
#include <kuser.h>
//////////////////
// 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<TQString,TQString> 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<TQString,TQString>::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<char **>(malloc( 4 * sizeof(char *)));
arglist[0] = d->shell.data();
arglist[1] = (char *) "-c";
arglist[2] = shellCmd.data();
arglist[3] = 0;
}
else
{
arglist = static_cast<char **>(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, TQT_SIGNAL(activated(int)),
this, TQT_SLOT(slotSendData(int)));
}
if (communication & Stdout) {
outnot = new TQSocketNotifier(out[0], TQSocketNotifier::Read, this);
TQ_CHECK_PTR(outnot);
TQObject::connect(outnot, TQT_SIGNAL(activated(int)),
this, TQT_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, TQT_SIGNAL(activated(int)),
this, TQT_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"