kalarm/lib

shellprocess.cpp

00001 /*
00002  *  shellprocess.cpp  -  execute a shell process
00003  *  Program:  kalarm
00004  *  Copyright (C) 2004, 2005 by David Jarvie <software@astrojar.org.uk>
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License along
00017  *  with this program; if not, write to the Free Software Foundation, Inc.,
00018  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif
00024 
00025 #include <stdlib.h>
00026 #include <sys/stat.h>
00027 #include <kapplication.h>
00028 #include <klocale.h>
00029 #include <kdebug.h>
00030 
00031 #include "shellprocess.moc"
00032 
00033 
00034 QCString  ShellProcess::mShellName;
00035 QCString  ShellProcess::mShellPath;
00036 bool      ShellProcess::mInitialised = false;
00037 bool      ShellProcess::mAuthorised  = false;
00038 
00039 
00040 ShellProcess::ShellProcess(const QString& command)
00041     : KShellProcess(shellName()),
00042       mCommand(command),
00043       mStatus(INACTIVE),
00044       mStdinExit(false)
00045 {
00046 }
00047 
00048 /******************************************************************************
00049 * Execute a command.
00050 */
00051 bool ShellProcess::start(Communication comm)
00052 {
00053     if (!authorised())
00054     {
00055         mStatus = UNAUTHORISED;
00056         return false;
00057     }
00058     KShellProcess::operator<<(mCommand);
00059     connect(this, SIGNAL(wroteStdin(KProcess*)), SLOT(writtenStdin(KProcess*)));
00060     connect(this, SIGNAL(processExited(KProcess*)), SLOT(slotExited(KProcess*)));
00061     if (!KShellProcess::start(KProcess::NotifyOnExit, comm))
00062     {
00063         mStatus = START_FAIL;
00064         return false;
00065     }
00066     mStatus = RUNNING;
00067     return true;
00068 }
00069 
00070 /******************************************************************************
00071 * Called when a shell process execution completes.
00072 * Interprets the exit status according to which shell was called, and emits
00073 * a shellExited() signal.
00074 */
00075 void ShellProcess::slotExited(KProcess* proc)
00076 {
00077     kdDebug(5950) << "ShellProcess::slotExited()\n";
00078     mStdinQueue.clear();
00079     mStatus = SUCCESS;
00080     if (!proc->normalExit())
00081     {
00082         kdWarning(5950) << "ShellProcess::slotExited(" << mCommand << ") " << mShellName << ": died/killed\n";
00083         mStatus = DIED;
00084     }
00085     else
00086     {
00087         // Some shells report if the command couldn't be found, or is not executable
00088         int status = proc->exitStatus();
00089         if (mShellName == "bash"  &&  (status == 126 || status == 127)
00090         ||  mShellName == "ksh"  &&  status == 127)
00091         {
00092             kdWarning(5950) << "ShellProcess::slotExited(" << mCommand << ") " << mShellName << ": not found or not executable\n";
00093             mStatus = NOT_FOUND;
00094         }
00095     }
00096     emit shellExited(this);
00097 }
00098 
00099 /******************************************************************************
00100 * Write a string to STDIN.
00101 */
00102 void ShellProcess::writeStdin(const char* buffer, int bufflen)
00103 {
00104     QCString scopy(buffer, bufflen+1);    // construct a deep copy
00105     bool write = !mStdinQueue.count();
00106     mStdinQueue.append(scopy);
00107     if (write)
00108         KProcess::writeStdin(mStdinQueue.first(), mStdinQueue.first().length());
00109 }
00110 
00111 /******************************************************************************
00112 * Called when output to STDIN completes.
00113 * Send the next queued output, if any.
00114 * Note that buffers written to STDIN must not be freed until the writtenStdin()
00115 * signal has been processed.
00116 */
00117 void ShellProcess::writtenStdin(KProcess* proc)
00118 {
00119     mStdinQueue.pop_front();   // free the buffer which has now been written
00120     if (mStdinQueue.count())
00121         proc->writeStdin(mStdinQueue.first(), mStdinQueue.first().length());
00122     else if (mStdinExit)
00123         kill();
00124 }
00125 
00126 /******************************************************************************
00127 * Tell the process to exit once all STDIN strings have been written.
00128 */
00129 void ShellProcess::stdinExit()
00130 {
00131     if (mStdinQueue.isEmpty())
00132         kill();
00133     else
00134         mStdinExit = true;
00135 }
00136 
00137 /******************************************************************************
00138 * Return the error message corresponding to the command exit status.
00139 * Reply = null string if not yet exited, or if command successful.
00140 */
00141 QString ShellProcess::errorMessage() const
00142 {
00143     switch (mStatus)
00144     {
00145         case UNAUTHORISED:
00146             return i18n("Failed to execute command (shell access not authorized):");
00147         case START_FAIL:
00148         case NOT_FOUND:
00149             return i18n("Failed to execute command:");
00150         case DIED:
00151             return i18n("Command execution error:");
00152         case INACTIVE:
00153         case RUNNING:
00154         case SUCCESS:
00155         default:
00156             return QString::null;
00157     }
00158 }
00159 
00160 /******************************************************************************
00161 * Determine which shell to use.
00162 * This is a duplication of what KShellProcess does, but we need to know
00163 * which shell is used in order to decide what its exit code means.
00164 */
00165 const QCString& ShellProcess::shellPath()
00166 {
00167     if (mShellPath.isEmpty())
00168     {
00169         // Get the path to the shell
00170         mShellPath = "/bin/sh";
00171         QCString envshell = QCString(getenv("SHELL")).stripWhiteSpace();
00172         if (!envshell.isEmpty())
00173         {
00174             struct stat fileinfo;
00175             if (stat(envshell.data(), &fileinfo) != -1  // ensure file exists
00176             &&  !S_ISDIR(fileinfo.st_mode)              // and it's not a directory
00177             &&  !S_ISCHR(fileinfo.st_mode)              // and it's not a character device
00178             &&  !S_ISBLK(fileinfo.st_mode)              // and it's not a block device
00179 #ifdef S_ISSOCK
00180             &&  !S_ISSOCK(fileinfo.st_mode)             // and it's not a socket
00181 #endif
00182             &&  !S_ISFIFO(fileinfo.st_mode)             // and it's not a fifo
00183             &&  !access(envshell.data(), X_OK))         // and it's executable
00184                 mShellPath = envshell;
00185         }
00186 
00187         // Get the shell filename with the path stripped off
00188         int i = mShellPath.findRev('/');
00189         if (i >= 0)
00190             mShellName = mShellPath.mid(i + 1);
00191         else
00192             mShellName = mShellPath;
00193     }
00194     return mShellPath;
00195 }
00196 
00197 /******************************************************************************
00198 * Check whether shell commands are allowed at all.
00199 */
00200 bool ShellProcess::authorised()
00201 {
00202     if (!mInitialised)
00203     {
00204         mAuthorised = kapp->authorize("shell_access");
00205         mInitialised = true;
00206     }
00207     return mAuthorised;
00208 }
KDE Home | KDE Accessibility Home | Description of Access Keys