french -> French
[kdepim.git] / kalarm / lib / shellprocess.cpp
blobb4f0479d7dd2914ace2c172eb6c05a3b052e00c3
1 /*
2 * shellprocess.cpp - execute a shell process
3 * Program: kalarm
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.
21 #include <stdlib.h>
22 #include <qglobal.h>
23 #include <kde_file.h>
24 #include <kapplication.h>
25 #include <klocale.h>
26 #include <kdebug.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)
39 : mCommand(command),
40 mStdinBytes(0),
41 mStatus(INACTIVE),
42 mStdinExit(false)
46 /******************************************************************************
47 * Execute a command.
49 bool ShellProcess::start(OpenMode openMode)
51 if (!authorised())
53 mStatus = UNAUTHORISED;
54 return false;
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()));
60 QStringList args;
61 args << QLatin1String("-c") << mCommand;
62 QProcess::start(QLatin1String(shellName()), args, openMode);
63 if (!waitForStarted())
65 mStatus = START_FAIL;
66 return false;
68 mStatus = RUNNING;
69 return true;
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;
80 mStdinQueue.clear();
81 mStatus = SUCCESS;
82 mExitCode = exitCode;
83 if (exitStatus != NormalExit)
85 kWarning(5950) << mCommand << ":" << mShellName << ": crashed/killed";
86 mStatus = DIED;
88 else
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";
95 mStatus = NOT_FOUND;
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);
109 if (doWrite)
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;
125 if (mStdinBytes > 0)
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());
134 else if (mStdinExit)
135 kill();
138 /******************************************************************************
139 * Tell the process to exit once all STDIN strings have been written.
141 void ShellProcess::stdinExit()
143 if (mStdinQueue.isEmpty())
144 kill();
145 else
146 mStdinExit = true;
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
155 switch (mStatus)
157 case UNAUTHORISED:
158 return i18nc("@info", "Failed to execute command (shell access not authorized)");
159 case START_FAIL:
160 case NOT_FOUND:
161 return i18nc("@info", "Failed to execute command");
162 case DIED:
163 return i18nc("@info", "Command execution error");
164 case SUCCESS:
165 if (mExitCode)
166 return i18nc("@info", "Command exit code: %1", mExitCode);
167 // Fall through to INACTIVE
168 case INACTIVE:
169 case RUNNING:
170 default:
171 return QString();
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
194 #ifdef S_ISSOCK
195 && !S_ISSOCK(fileinfo.st_mode) // and it's not a socket
196 #endif
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('/');
204 if (i >= 0)
205 mShellName = mShellPath.mid(i + 1);
206 else
207 mShellName = mShellPath;
209 return mShellPath;
212 /******************************************************************************
213 * Check whether shell commands are allowed at all.
215 bool ShellProcess::authorised()
217 if (!mInitialised)
219 mAuthorised = KAuthorized::authorizeKAction(QLatin1String("shell_access"));
220 mInitialised = true;
222 return mAuthorised;
224 #include "moc_shellprocess.cpp"
225 // vim: et sw=4: