26 #include "kprocctrl.h"
45 #include <sys/socket.h>
46 #include <sys/ioctl.h>
49 #include <sys/types.h>
51 #include <sys/resource.h>
55 #ifdef HAVE_SYS_STROPTS_H
56 #include <sys/stropts.h>
59 #ifdef HAVE_SYS_SELECT_H
60 #include <sys/select.h>
76 #include <tqsocketnotifier.h>
77 #include <tqapplication.h>
80 #include <kstandarddirs.h>
88 class TDEProcessPrivate {
92 addUtmp(false), useShell(false),
110 TQMap<TQString,TQString> env;
113 TQCString executable;
121 : TQObject( parent, name ),
122 run_mode(NotifyOnExit),
130 communication(NoCommunication),
138 d =
new TDEProcessPrivate;
147 run_mode(NotifyOnExit),
155 communication(NoCommunication),
163 d =
new TDEProcessPrivate;
173 d->env.insert(name, value);
185 TQMap<TQString,TQString>::Iterator it;
186 for(it = d->env.begin(); it != d->env.end(); ++it)
188 setenv(TQFile::encodeName(it.key()).data(),
189 TQFile::encodeName(it.data()).data(), 1);
191 if (!d->wd.isEmpty())
193 chdir(TQFile::encodeName(d->wd).data());
214 if (setpriority(PRIO_PROCESS,
pid_, prio))
217 if (prio > 19 || prio < (geteuid() ? getpriority(PRIO_PROCESS, 0) : -20))
252 d->executable = filename;
257 if (
runs)
return false;
259 if (proc.isEmpty())
return false;
263 arguments.prepend(TQFile::encodeName(proc));
270 TQStringList::ConstIterator it =
args.begin();
271 for ( ; it !=
args.end() ; ++it )
272 arguments.append(TQFile::encodeName(*it));
289 arguments.append(TQFile::encodeName(arg));
301 kdDebug(175) <<
"Attempted to start an already running process" <<
endl;
307 kdDebug(175) <<
"Attempted to start a process without arguments" <<
endl;
315 if (d->shell.isEmpty()) {
316 kdDebug(175) <<
"Invalid shell specified" <<
endl;
320 for (uint i = 0; i < n; i++) {
325 arglist =
static_cast<char **
>(malloc( 4 *
sizeof(
char *)));
326 arglist[0] = d->shell.data();
327 arglist[1] = (
char *)
"-c";
328 arglist[2] = shellCmd.data();
333 arglist =
static_cast<char **
>(malloc( (n + 1) *
sizeof(
char *)));
334 for (uint i = 0; i < n; i++)
343 kdDebug(175) <<
"Could not setup Communication!" <<
endl;
350 #ifdef HAVE_INITGROUPS
351 struct passwd *pw = geteuid() ? 0 : getpwuid(getuid());
367 fcntl(fd[1], F_SETFD, FD_CLOEXEC);
370 kdDebug(175) <<
"Could not finish comm setup in child!" <<
endl;
373 struct sigaction act;
374 sigemptyset(&act.sa_mask);
375 act.sa_handler = SIG_DFL;
377 for (
int sig = 1; sig < NSIG; sig++)
378 sigaction(sig, &act, 0L);
381 setpriority(PRIO_PROCESS, 0, d->priority);
386 #ifdef HAVE_INITGROUPS
388 initgroups(pw->pw_name, pw->pw_gid);
390 if (geteuid() != getuid())
392 if (geteuid() != getuid())
401 const char *executable = arglist[0];
402 if (!d->executable.isEmpty())
403 executable = d->executable.data();
404 execvp(executable, arglist);
407 write(fd[1], &resultByte, 1);
409 }
else if (
pid_ == -1) {
421 kdDebug(175) <<
"Could not finish comm setup in parent!" <<
endl;
428 int n = ::read(fd[0], &resultByte, 1);
517 # define timersub(a, b, result) \
519 (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
520 (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
521 if ((result)->tv_usec < 0) { \
522 --(result)->tv_sec; \
523 (result)->tv_usec += 1000000; \
536 struct timeval tv, *tvp;
542 gettimeofday(&etv, 0);
543 etv.tv_sec += timeout;
562 gettimeofday(&tv, 0);
563 timersub(&etv, &tv, &tv);
565 tv.tv_sec = tv.tv_usec = 0;
569 switch( select( fd+1, &fds, 0, 0, tvp ) )
618 return WEXITSTATUS(
status);
640 innot->setEnabled(
true);
651 outnot->setEnabled(
false);
666 if (!(d->usePty & Stdin))
680 if (!(d->usePty & Stdout))
694 if (!(d->usePty & Stderr))
705 if (d->pty && d->pty->masterFd() >= 0) {
748 innot->setEnabled(
false);
757 else if ((errno != EAGAIN) && (errno != EINTR))
759 kdDebug(175) <<
"Error writing to stdin of child process" <<
endl;
767 d->useShell = useShell;
772 #if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__GNU__) && !defined(__DragonFly__)
774 if (!access(
"/usr/xpg4/bin/sh", X_OK ))
775 d->shell =
"/usr/xpg4/bin/sh";
778 if (!access(
"/bin/ksh", X_OK ))
779 d->shell =
"/bin/ksh";
782 if (!access(
"/usr/ucb/sh", X_OK ))
783 d->shell =
"/usr/ucb/sh";
786 d->shell =
"/bin/sh";
793 d->addUtmp = addUtmp;
812 return TQString(arg).replace(q,
"'\\''").prepend(q).append(q);
849 len = ::read(fdno, buffer, 1024);
864 len = ::read(fdno, buffer, 1024);
881 if (!(~(comm & d->usePty) & (Stdout | Stderr))) {
882 kdWarning(175) <<
"Invalid usePty/communication combination (" << d->usePty <<
"/" << comm <<
")" <<
endl;
888 int rcomm = comm & d->usePty;
889 int mfd = d->pty->masterFd();
902 if (socketpair(AF_UNIX, SOCK_STREAM, 0,
in))
904 fcntl(
in[0], F_SETFD, FD_CLOEXEC);
905 fcntl(
in[1], F_SETFD, FD_CLOEXEC);
908 if (socketpair(AF_UNIX, SOCK_STREAM, 0,
out))
910 fcntl(
out[0], F_SETFD, FD_CLOEXEC);
911 fcntl(
out[1], F_SETFD, FD_CLOEXEC);
914 if (socketpair(AF_UNIX, SOCK_STREAM, 0,
err))
916 fcntl(
err[0], F_SETFD, FD_CLOEXEC);
917 fcntl(
err[1], F_SETFD, FD_CLOEXEC);
958 fcntl(
in[1], F_SETFL, O_NONBLOCK | fcntl(
in[1], F_GETFL));
959 innot =
new TQSocketNotifier(
in[1], TQSocketNotifier::Write,
this);
961 innot->setEnabled(
false);
962 TQObject::connect(
innot, TQ_SIGNAL(activated(
int)),
967 outnot =
new TQSocketNotifier(
out[0], TQSocketNotifier::Read,
this);
969 TQObject::connect(
outnot, TQ_SIGNAL(activated(
int)),
976 errnot =
new TQSocketNotifier(
err[0], TQSocketNotifier::Read,
this );
978 TQObject::connect(
errnot, TQ_SIGNAL(activated(
int)),
992 if (d->usePty & Stdin) {
993 if (dup2(d->pty->slaveFd(), STDIN_FILENO) < 0) ok = 0;
995 if (dup2(
in[0], STDIN_FILENO) < 0) ok = 0;
997 int null_fd = open(
"/dev/null", O_RDONLY );
998 if (dup2( null_fd, STDIN_FILENO ) < 0) ok = 0;
1002 memset(&so, 0,
sizeof(so));
1003 if (d->usePty & Stdout) {
1004 if (dup2(d->pty->slaveFd(), STDOUT_FILENO) < 0) ok = 0;
1006 if (dup2(
out[1], STDOUT_FILENO) < 0 ||
1007 setsockopt(
out[1], SOL_SOCKET, SO_LINGER, (
char *)&so,
sizeof(so)))
1010 if (dup2(
out[1], STDERR_FILENO) < 0)
1014 if (d->usePty & Stderr) {
1015 if (dup2(d->pty->slaveFd(), STDERR_FILENO) < 0) ok = 0;
1017 if (dup2(
err[1], STDERR_FILENO) < 0 ||
1018 setsockopt(
err[1], SOL_SOCKET, SO_LINGER, (
char *)&so,
sizeof(so)))
1052 struct timeval timeout, *p_timeout;
1056 FD_SET(
out[0], &rfds);
1060 FD_SET(
err[0], &rfds);
1061 if (
err[0] > max_fd)
1065 FD_SET(notfd, &rfds);
1074 timeout.tv_sec = timeout.tv_usec = 0;
1075 p_timeout = &timeout;
1078 int fds_ready = select(max_fd+1, &rfds, 0, 0, p_timeout);
1079 if (fds_ready < 0) {
1083 }
else if (!fds_ready)
1092 if (
runs && FD_ISSET(notfd, &rfds)) {
1107 void TDEProcess::virtual_hook(
int,
void* )
1118 setUseShell(
true, shellname ? shellname : getenv(
"SHELL") );
1124 TQString KShellProcess::quote(
const TQString &arg)
1134 void KShellProcess::virtual_hook(
int id,
void* data )
1135 { TDEProcess::virtual_hook(
id, data ); }
1137 #include "kprocess.moc"
Provides a high level representation of a pseudo tty pair, including utmp support.
KShellProcess(const char *shellname=0)
Constructor.
virtual bool start(RunMode runmode=NotifyOnExit, Communication comm=NoCommunication)
Starts the process.
~KShellProcess()
Destructor.
Represents a user on your system.
@ UseRealUserID
Use the real user id.
static void deref()
Destroy the instance if one exists and it is not referenced any more.
static void ref()
Create an instance if none exists yet.
void rescheduleCheck()
This function must be called at some point after calling unscheduleCheck().
static TDEProcessController * theTDEProcessController
Only a single instance of this class is allowed at a time, and this static variable is used to track ...
void unscheduleCheck()
Call this function to defer processing of the data that became available on notifierFd().
Child process invocation, monitoring and control.
int status
The process' exit status as returned by waitpid().
TDEProcess & operator<<(const TQString &arg)
Sets the executable and the command line argument list for this process.
pid_t pid_
The PID of the currently running process.
TQSocketNotifier * outnot
The socket notifier for out[0].
virtual void processHasExited(int state)
Immediately called after a successfully started process in NotifyOnExit mode has exited.
bool isRunning() const
Checks whether the process is running.
bool closePty()
Deletes the optional utmp entry and closes the pty.
int out[2]
The socket descriptors for stdout.
virtual int commSetupDoneC()
Called right after a (successful) fork(), but before an exec() on the child process' side.
pid_t pid() const
Returns the process id of the process.
virtual int commSetupDoneP()
Called right after a (successful) fork() on the parent side.
Communication communication
Lists the communication links that are activated for the child process.
bool runs
true if the process is currently running.
int in[2]
The socket descriptors for stdin.
virtual bool start(RunMode runmode=NotifyOnExit, Communication comm=NoCommunication)
Starts the process.
int exitStatus() const
Returns the exit status of the process.
int childError(int fdno)
Called by slotChildError() this function copies data arriving from the child process' stderr to the r...
void suspend()
Suspend processing of data from stdout of the child process.
int input_sent
The number of bytes already transmitted.
void processExited(TDEProcess *proc)
Emitted after the process has terminated when the process was run in the NotifyOnExit (==default opti...
bool keepPrivs
If false, the child process' effective uid & gid will be reset to the real values.
void clearArguments()
Clear a command line argument list that has been set by using operator<<.
virtual bool kill(int signo=SIGTERM)
Stop the process (by sending it a signal).
bool wait(int timeout=-1)
Suspend execution of the current thread until the child process dies or the timeout hits.
static TQString quote(const TQString &arg)
This function can be used to quote an argument string such that the shell processes it properly.
void setEnvironment(const TQString &name, const TQString &value)
Adds the variable name to the process' environment.
RunMode
Run-modes for a child process.
@ OwnGroup
Same as NotifyOnExit, but the process is run in an own session, just like with DontCare.
@ DontCare
The application does not receive notifications from the subprocess when it is finished or aborted.
@ NotifyOnExit
The application is notified when the subprocess dies.
@ Block
The application is suspended until the started process is finished.
void setupEnvironment()
Sets up the environment according to the data passed via setEnvironment()
Communication
Modes in which the communication channel can be opened.
void slotSendData(int dummy)
Called when another bulk of data can be sent to the child's stdin.
void detach()
Detaches TDEProcess from child process.
TQValueList< TQCString > arguments
The list of the process' command line arguments.
void setUsePty(Communication comm, bool addUtmp)
Specify whether to create a pty (pseudo-terminal) for running the command.
bool setExecutable(const TQString &proc) KDE_DEPRECATED
RunMode run_mode
How to run the process (Block, NotifyOnExit, DontCare).
int childOutput(int fdno)
Called by slotChildOutput() this function copies data arriving from the child process' stdout to the ...
bool writeStdin(const char *buffer, int buflen)
virtual void commClose()
Cleans up the communication links to the child after it has exited.
int input_total
The total length of input_data.
int exitSignal() const
Returns the signal the process was killed by.
void resume()
Resume processing of data from stdout of the child process.
void setWorkingDirectory(const TQString &dir)
Changes the current working directory (CWD) of the process to be started.
virtual int setupCommunication(Communication comm)
This function is called from start() right before a fork() takes place.
void slotChildError(int fdno)
This slot gets activated when data from the child's stderr arrives.
TQSocketNotifier * errnot
The socket notifier for err[0].
bool closeStderr()
Shuts down the Stderr communication link.
TQSocketNotifier * innot
The socket notifier for in[1].
void slotChildOutput(int fdno)
This slot gets activated when data from the child's stdout arrives.
const char * input_data
The buffer holding the data that has to be sent to the child.
bool setPriority(int prio)
Sets the scheduling priority of the process.
void receivedStderr(TDEProcess *proc, char *buffer, int buflen)
Emitted, when output from the child process has been received on stderr.
bool signalled() const
Checks whether the process was killed by a signal.
void wroteStdin(TDEProcess *proc)
Emitted after all the data that has been specified by a prior call to writeStdin() has actually been ...
virtual ~TDEProcess()
Destructor:
void closeAll()
Close stdin, stdout, stderr and the pty.
int err[2]
The socket descriptors for stderr.
bool closeStdout()
Shuts down the Stdout communication link.
bool closeStdin()
Shuts down the Stdin communication link.
bool coreDumped() const
Checks whether a killed process dumped core.
void setUseShell(bool useShell, const char *shell=0)
Specify whether to start the command via a shell or directly.
void setRunPrivileged(bool keepPrivileges)
Controls whether the started process should drop any setuid/setgid privileges or whether it should ke...
bool normalExit() const
Checks whether the process exited cleanly.
bool runPrivileged() const
Returns whether the started process will drop any setuid/setgid privileges or whether it will keep th...
void setBinaryExecutable(const char *filename)
Specify the actual executable that should be started (first argument to execve) Normally the the firs...
void receivedStdout(TDEProcess *proc, char *buffer, int buflen)
Emitted, when output from the child process has been received on stdout.
KPty * pty() const
Obtains the pty object used by this process.
const TQValueList< TQCString > & args()
Lets you see what your arguments are for debugging.
kndbgstream & endl(kndbgstream &s)
Does nothing.