2 * shellprocess.cpp - execute a shell process
4 * Copyright © 2004,2005,2007,2008 by David Jarvie <djarvie@kde.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include <kapplication.h>
27 #include <kauthorized.h>
29 #include "shellprocess.h"
32 QByteArray
ShellProcess::mShellName
;
33 QByteArray
ShellProcess::mShellPath
;
34 bool ShellProcess::mInitialised
= false;
35 bool ShellProcess::mAuthorised
= false;
38 ShellProcess::ShellProcess(const QString
& command
)
46 /******************************************************************************
49 bool ShellProcess::start(OpenMode openMode
)
53 mStatus
= UNAUTHORISED
;
56 connect(this, SIGNAL(bytesWritten(qint64
)), SLOT(writtenStdin(qint64
)));
57 connect(this, SIGNAL(finished(int,QProcess::ExitStatus
)), SLOT(slotExited(int,QProcess::ExitStatus
)));
58 connect(this, SIGNAL(readyReadStandardOutput()), SLOT(stdoutReady()));
59 connect(this, SIGNAL(readyReadStandardError()), SLOT(stderrReady()));
61 args
<< QLatin1String("-c") << mCommand
;
62 QProcess::start(QLatin1String(shellName()), args
, openMode
);
63 if (!waitForStarted())
72 /******************************************************************************
73 * Called when a shell process execution completes.
74 * Interprets the exit status according to which shell was called, and emits
75 * a shellExited() signal.
77 void ShellProcess::slotExited(int exitCode
, QProcess::ExitStatus exitStatus
)
79 kDebug() << exitCode
<< "," << exitStatus
;
83 if (exitStatus
!= NormalExit
)
85 kWarning(5950) << mCommand
<< ":" << mShellName
<< ": crashed/killed";
90 // Some shells report if the command couldn't be found, or is not executable
91 if ((mShellName
== "bash" && (exitCode
== 126 || exitCode
== 127))
92 || (mShellName
== "ksh" && exitCode
== 127))
94 kWarning(5950) << mCommand
<< ":" << mShellName
<< ": not found or not executable";
98 emit
shellExited(this);
101 /******************************************************************************
102 * Write a string to STDIN.
104 void ShellProcess::writeStdin(const char* buffer
, int bufflen
)
106 QByteArray
scopy(buffer
, bufflen
); // construct a deep copy
107 bool doWrite
= mStdinQueue
.isEmpty();
108 mStdinQueue
.enqueue(scopy
);
111 mStdinBytes
= mStdinQueue
.head().length();
112 write(mStdinQueue
.head());
116 /******************************************************************************
117 * Called when output to STDIN completes.
118 * Send the next queued output, if any.
119 * Note that buffers written to STDIN must not be freed until the bytesWritten()
120 * signal has been processed.
122 void ShellProcess::writtenStdin(qint64 bytes
)
124 mStdinBytes
-= bytes
;
126 return; // buffer has only been partially written so far
127 if (!mStdinQueue
.isEmpty())
128 mStdinQueue
.dequeue(); // free the buffer which has now been written
129 if (!mStdinQueue
.isEmpty())
131 mStdinBytes
= mStdinQueue
.head().length();
132 write(mStdinQueue
.head());
138 /******************************************************************************
139 * Tell the process to exit once all STDIN strings have been written.
141 void ShellProcess::stdinExit()
143 if (mStdinQueue
.isEmpty())
149 /******************************************************************************
150 * Return the error message corresponding to the command exit status.
151 * Reply = null string if not yet exited, or if command successful.
153 QString
ShellProcess::errorMessage() const
158 return i18nc("@info", "Failed to execute command (shell access not authorized)");
161 return i18nc("@info", "Failed to execute command");
163 return i18nc("@info", "Command execution error");
166 return i18nc("@info", "Command exit code: %1", mExitCode
);
167 // Fall through to INACTIVE
175 /******************************************************************************
176 * Determine which shell to use.
177 * Don't use the KProcess default shell, since we need to know which shell is
178 * used in order to decide what its exit code means.
180 const QByteArray
& ShellProcess::shellPath()
182 if (mShellPath
.isEmpty())
184 // Get the path to the shell
185 mShellPath
= "/bin/sh";
186 QByteArray envshell
= qgetenv("SHELL").trimmed();
187 if (!envshell
.isEmpty())
189 KDE_struct_stat fileinfo
;
190 if (KDE_stat(envshell
.data(), &fileinfo
) != -1 // ensure file exists
191 && !S_ISDIR(fileinfo
.st_mode
) // and it's not a directory
192 && !S_ISCHR(fileinfo
.st_mode
) // and it's not a character device
193 && !S_ISBLK(fileinfo
.st_mode
) // and it's not a block device
195 && !S_ISSOCK(fileinfo
.st_mode
) // and it's not a socket
197 && !S_ISFIFO(fileinfo
.st_mode
) // and it's not a fifo
198 && !access(envshell
.data(), X_OK
)) // and it's executable
199 mShellPath
= envshell
;
202 // Get the shell filename with the path stripped off
203 int i
= mShellPath
.lastIndexOf('/');
205 mShellName
= mShellPath
.mid(i
+ 1);
207 mShellName
= mShellPath
;
212 /******************************************************************************
213 * Check whether shell commands are allowed at all.
215 bool ShellProcess::authorised()
219 mAuthorised
= KAuthorized::authorizeKAction(QLatin1String("shell_access"));
224 #include "moc_shellprocess.cpp"