kalarm/lib

shellprocess.cpp
1/*
2 * shellprocess.cpp - execute a shell process
3 * Program: kalarm
4 * Copyright (c) 2004, 2005 by David Jarvie <software@astrojar.org.uk>
5 *
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.
10 *
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.
15 *
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.
19 */
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <stdlib.h>
26#include <sys/stat.h>
27#include <tdeapplication.h>
28#include <tdelocale.h>
29#include <kdebug.h>
30
31#include "shellprocess.moc"
32
33
34TQCString ShellProcess::mShellName;
35TQCString ShellProcess::mShellPath;
36bool ShellProcess::mInitialised = false;
37bool ShellProcess::mAuthorised = false;
38
39
40ShellProcess::ShellProcess(const TQString& command)
41 : KShellProcess(shellName()),
42 mCommand(command),
43 mStatus(INACTIVE),
44 mStdinExit(false)
45{
46}
47
48/******************************************************************************
49* Execute a command.
50*/
51bool ShellProcess::start(Communication comm)
52{
53 if (!authorised())
54 {
55 mStatus = UNAUTHORISED;
56 return false;
57 }
58 KShellProcess::operator<<(mCommand);
59 connect(this, TQ_SIGNAL(wroteStdin(TDEProcess*)), TQ_SLOT(writtenStdin(TDEProcess*)));
60 connect(this, TQ_SIGNAL(processExited(TDEProcess*)), TQ_SLOT(slotExited(TDEProcess*)));
61 if (!KShellProcess::start(TDEProcess::NotifyOnExit, comm))
62 {
63 mStatus = START_FAIL;
64 return false;
65 }
66 mStatus = RUNNING;
67 return true;
68}
69
70/******************************************************************************
71* Called when a shell process execution completes.
72* Interprets the exit status according to which shell was called, and emits
73* a shellExited() signal.
74*/
75void ShellProcess::slotExited(TDEProcess* proc)
76{
77 kdDebug(5950) << "ShellProcess::slotExited()\n";
78 mStdinQueue.clear();
79 mStatus = SUCCESS;
80 if (!proc->normalExit())
81 {
82 kdWarning(5950) << "ShellProcess::slotExited(" << mCommand << ") " << mShellName << ": died/killed\n";
83 mStatus = DIED;
84 }
85 else
86 {
87 // Some shells report if the command couldn't be found, or is not executable
88 int status = proc->exitStatus();
89 if ((mShellName == "bash" && (status == 126 || status == 127))
90 || (mShellName == "ksh" && status == 127))
91 {
92 kdWarning(5950) << "ShellProcess::slotExited(" << mCommand << ") " << mShellName << ": not found or not executable\n";
93 mStatus = NOT_FOUND;
94 }
95 }
96 emit shellExited(this);
97}
98
99/******************************************************************************
100* Write a string to STDIN.
101*/
102void ShellProcess::writeStdin(const char* buffer, int bufflen)
103{
104 TQCString scopy(buffer, bufflen+1); // construct a deep copy
105 bool write = mStdinQueue.isEmpty();
106 mStdinQueue.append(scopy);
107 if (write)
108 TDEProcess::writeStdin(mStdinQueue.first(), mStdinQueue.first().length());
109}
110
111/******************************************************************************
112* Called when output to STDIN completes.
113* Send the next queued output, if any.
114* Note that buffers written to STDIN must not be freed until the writtenStdin()
115* signal has been processed.
116*/
117void ShellProcess::writtenStdin(TDEProcess* proc)
118{
119 mStdinQueue.pop_front(); // free the buffer which has now been written
120 if (!mStdinQueue.isEmpty())
121 proc->writeStdin(mStdinQueue.first(), mStdinQueue.first().length());
122 else if (mStdinExit)
123 kill();
124}
125
126/******************************************************************************
127* Tell the process to exit once all STDIN strings have been written.
128*/
130{
131 if (mStdinQueue.isEmpty())
132 kill();
133 else
134 mStdinExit = true;
135}
136
137/******************************************************************************
138* Return the error message corresponding to the command exit status.
139* Reply = null string if not yet exited, or if command successful.
140*/
142{
143 switch (mStatus)
144 {
145 case UNAUTHORISED:
146 return i18n("Failed to execute command (shell access not authorized):");
147 case START_FAIL:
148 case NOT_FOUND:
149 return i18n("Failed to execute command:");
150 case DIED:
151 return i18n("Command execution error:");
152 case INACTIVE:
153 case RUNNING:
154 case SUCCESS:
155 default:
156 return TQString();
157 }
158}
159
160/******************************************************************************
161* Determine which shell to use.
162* This is a duplication of what KShellProcess does, but we need to know
163* which shell is used in order to decide what its exit code means.
164*/
165const TQCString& ShellProcess::shellPath()
166{
167 if (mShellPath.isEmpty())
168 {
169 // Get the path to the shell
170 mShellPath = "/bin/sh";
171 TQCString envshell = TQCString(getenv("SHELL")).stripWhiteSpace();
172 if (!envshell.isEmpty())
173 {
174 struct stat fileinfo;
175 if (stat(envshell.data(), &fileinfo) != -1 // ensure file exists
176 && !S_ISDIR(fileinfo.st_mode) // and it's not a directory
177 && !S_ISCHR(fileinfo.st_mode) // and it's not a character device
178 && !S_ISBLK(fileinfo.st_mode) // and it's not a block device
179#ifdef S_ISSOCK
180 && !S_ISSOCK(fileinfo.st_mode) // and it's not a socket
181#endif
182 && !S_ISFIFO(fileinfo.st_mode) // and it's not a fifo
183 && !access(envshell.data(), X_OK)) // and it's executable
184 mShellPath = envshell;
185 }
186
187 // Get the shell filename with the path stripped off
188 int i = mShellPath.findRev('/');
189 if (i >= 0)
190 mShellName = mShellPath.mid(i + 1);
191 else
192 mShellName = mShellPath;
193 }
194 return mShellPath;
195}
196
197/******************************************************************************
198* Check whether shell commands are allowed at all.
199*/
201{
202 if (!mInitialised)
203 {
204 mAuthorised = tdeApp->authorize("shell_access");
205 mInitialised = true;
206 }
207 return mAuthorised;
208}
static bool authorised()
Returns whether the user is authorised to run shell commands.
void shellExited(ShellProcess *)
Signal emitted when the shell process execution completes.
TQString errorMessage() const
Returns the error message corresponding to the command exit status.
static const TQCString & shellPath()
Determines which shell to use.
ShellProcess(const TQString &command)
Constructor.
void writeStdin(const char *buffer, int bufflen)
Writes a string to the process's STDIN.
void stdinExit()
Tell the process to exit once any outstanding STDIN strings have been written.
bool start(Communication comm=NoCommunication)
Executes the configured command.
Status status() const
Returns the current status of the shell process.
Definition: shellprocess.h:83