• Skip to content
  • Skip to link menu
KDE 4.0 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

Konsole

ProcessInfo.cpp

Go to the documentation of this file.
00001 /*
00002     Copyright (C) 2007 by Robert Knight <robertknight@gmail.countm>
00003 
00004     This program is free software; you can redistribute it and/or modify
00005     it under the terms of the GNU General Public License as published by
00006     the Free Software Foundation; either version 2 of the License, or
00007     (at your option) any later version.
00008 
00009     This program is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012     GNU General Public License for more details.
00013 
00014     You should have received a copy of the GNU General Public License
00015     along with this program; if not, write to the Free Software
00016     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017     02110-1301  USA.
00018 */
00019 
00020 // Own
00021 #include "ProcessInfo.h"
00022 
00023 // Unix
00024 #include <sys/socket.h>
00025 #include <netinet/in.h>
00026 #include <arpa/inet.h>
00027 
00028 // Qt
00029 #include <KDebug>
00030 #include <QtCore/QDir>
00031 #include <QtCore/QFileInfo>
00032 #include <QtCore/QRegExp>
00033 #include <QtCore/QTextStream>
00034 #include <QtCore/QStringList>
00035 
00036 // KDE
00037 #include <KConfigGroup>
00038 #include <KGlobal>
00039 #include <KSharedConfig>
00040 
00041 using namespace Konsole;
00042 
00043 ProcessInfo::ProcessInfo(int pid , bool enableEnvironmentRead)
00044     : _fields( ARGUMENTS | ENVIRONMENT ) // arguments and environments
00045                                          // are currently always valid,
00046                                          // they just return an empty
00047                                          // vector / map respectively
00048                                          // if no arguments
00049                                          // or environment bindings
00050                                          // have been explicitly set
00051     , _enableEnvironmentRead(enableEnvironmentRead)
00052     , _pid(pid)
00053     , _parentPid(0)
00054     , _foregroundPid(0)
00055     , _lastError(NoError)
00056 {
00057 }
00058 
00059 ProcessInfo::Error ProcessInfo::error() const { return _lastError; }
00060 void ProcessInfo::setError(Error error) { _lastError = error; }
00061 
00062 void ProcessInfo::update() 
00063 {
00064     readProcessInfo(_pid,_enableEnvironmentRead);
00065 }
00066 
00067 QString ProcessInfo::format(const QString& input) const
00068 {
00069    bool ok = false;
00070 
00071    QString output(input);
00072 
00073    // search for and replace known marker
00074    output.replace("%u","NOT IMPLEMENTED YET");
00075    output.replace("%n",name(&ok));
00076    output.replace("%c",formatCommand(name(&ok),arguments(&ok),ShortCommandFormat));
00077    output.replace("%C",formatCommand(name(&ok),arguments(&ok),LongCommandFormat));
00078    
00079    // read current dir, if an error occurs try the parent as the next
00080    // best option
00081    int currentPid = parentPid(&ok);
00082    QString dir = currentDir(&ok);
00083    while ( !ok && currentPid != 0 )
00084    {
00085        ProcessInfo* current = ProcessInfo::newInstance(currentPid);
00086        current->update();
00087        currentPid = current->parentPid(&ok); 
00088        dir = current->currentDir(&ok);
00089        delete current;
00090    }
00091         
00092    output.replace("%D",dir);
00093    output.replace("%d",formatShortDir(dir));
00094    
00095    // remove any remaining %[LETTER] sequences
00096    // output.replace(QRegExp("%\\w"), QString());
00097 
00098    return output;
00099 }
00100 
00101 QString ProcessInfo::formatCommand(const QString& name, 
00102                                    const QVector<QString>& arguments,
00103                                    CommandFormat format) const
00104 {
00105     // TODO Implement me
00106     return QStringList(QList<QString>::fromVector(arguments)).join(" ");
00107 }
00108 
00109 QSet<QString> ProcessInfo::_commonDirNames;
00110 
00111 QSet<QString> ProcessInfo::commonDirNames() 
00112 {
00113     if ( _commonDirNames.isEmpty() )
00114     {
00115         KSharedConfigPtr config = KGlobal::config();
00116         KConfigGroup configGroup = config->group("ProcessInfo");
00117 
00118         QStringList defaults = QStringList() 
00119                              << "src" << "build" << "debug" << "release" 
00120                              << "bin" << "lib"   << "libs"  << "tmp" 
00121                              << "doc" << "docs"  << "data"  << "share"
00122                              << "examples" << "icons" << "pics" << "plugins" 
00123                              << "tests" << "media" << "l10n" << "include" 
00124                              << "includes" << "locale" << "ui";
00125 
00126         _commonDirNames = QSet<QString>::fromList(configGroup.readEntry("CommonDirNames",defaults));
00127 
00128     }
00129 
00130     return _commonDirNames;
00131 }
00132 
00133 QString ProcessInfo::formatShortDir(const QString& input) const
00134 {
00135     QString result;
00136 
00137     QStringList parts = input.split( QDir::separator() );
00138 
00139     // temporarily hard-coded
00140     QSet<QString> dirNamesToShorten = commonDirNames();
00141 
00142     QListIterator<QString> iter(parts);
00143     iter.toBack();
00144 
00145     // go backwards through the list of the path's parts
00146     // adding abbreviations of common directory names
00147     // and stopping when we reach a dir name which is not
00148     // in the commonDirNames set
00149     while ( iter.hasPrevious() )
00150     {
00151         QString part = iter.previous();
00152 
00153         if ( dirNamesToShorten.contains(part) )
00154         {
00155             result.prepend(QDir::separator() + part[0]);
00156         }
00157         else
00158         {
00159             result.prepend(part);
00160             break;
00161         }
00162     }
00163 
00164     return result;
00165 }
00166 
00167 QVector<QString> ProcessInfo::arguments(bool* ok) const
00168 {
00169     *ok = _fields & ARGUMENTS;
00170 
00171     return _arguments;
00172 }
00173 
00174 QMap<QString,QString> ProcessInfo::environment(bool* ok) const
00175 {
00176     *ok = _fields & ENVIRONMENT;
00177 
00178     return _environment;
00179 }
00180 
00181 bool ProcessInfo::isValid() const
00182 {
00183     return _fields & PROCESS_ID;
00184 }
00185 
00186 int ProcessInfo::pid(bool* ok) const
00187 {
00188     *ok = _fields & PROCESS_ID;
00189 
00190     return _pid;
00191 }
00192 
00193 int ProcessInfo::parentPid(bool* ok) const
00194 {
00195     *ok = _fields & PARENT_PID;
00196 
00197     return _parentPid;
00198 }
00199 
00200 int ProcessInfo::foregroundPid(bool* ok) const
00201 {
00202     *ok = _fields & FOREGROUND_PID;
00203 
00204     return _foregroundPid;
00205 }
00206 
00207 QString ProcessInfo::name(bool* ok) const
00208 {
00209     *ok = _fields & NAME;
00210 
00211     return _name;
00212 }
00213 
00214 void ProcessInfo::setPid(int pid)
00215 {
00216     _pid = pid;
00217     _fields |= PROCESS_ID;
00218 }
00219 
00220 void ProcessInfo::setParentPid(int pid)
00221 {
00222     _parentPid = pid;
00223     _fields |= PARENT_PID;
00224 }
00225 void ProcessInfo::setForegroundPid(int pid)
00226 {
00227     _foregroundPid = pid;
00228     _fields |= FOREGROUND_PID;
00229 }
00230 QString ProcessInfo::currentDir(bool* ok) const
00231 {
00232     *ok = _fields & CURRENT_DIR;
00233 
00234     return _currentDir;
00235 }
00236 void ProcessInfo::setCurrentDir(const QString& dir)
00237 {
00238     _fields |= CURRENT_DIR;
00239     _currentDir = dir;
00240 }
00241 
00242 void ProcessInfo::setName(const QString& name)
00243 {
00244     _name = name;
00245     _fields |= NAME;
00246 }
00247 void ProcessInfo::addArgument(const QString& argument)
00248 {
00249     _arguments << argument;    
00250 }
00251 
00252 void ProcessInfo::addEnvironmentBinding(const QString& name , const QString& value)
00253 {
00254     _environment.insert(name,value);
00255 }
00256 
00257 UnixProcessInfo::UnixProcessInfo(int pid,bool enableEnvironmentRead)
00258     : ProcessInfo(pid,enableEnvironmentRead)
00259 {
00260 }
00261 
00262 bool UnixProcessInfo::readProcessInfo(int pid , bool enableEnvironmentRead)
00263 {
00264     // indicies of various fields within the process status file which
00265     // contain various information about the process
00266     const int PARENT_PID_FIELD = 3;
00267     const int PROCESS_NAME_FIELD = 1;
00268     const int GROUP_PROCESS_FIELD = 7;
00269     
00270     QString parentPidString;
00271     QString processNameString;
00272     QString foregroundPidString;
00273 
00274     // read process status file ( /proc/<pid/stat )
00275     //
00276     // the expected file format is a list of fields separated by spaces, using
00277     // parenthesies to escape fields such as the process name which may itself contain
00278     // spaces:
00279     //
00280     // FIELD FIELD (FIELD WITH SPACES) FIELD FIELD
00281     //
00282     QFile processInfo( QString("/proc/%1/stat").arg(pid) );
00283     if ( processInfo.open(QIODevice::ReadOnly) )
00284     {
00285         QTextStream stream(&processInfo);
00286         QString data = stream.readAll();
00287 
00288         int stack = 0;
00289         int field = 0;
00290         int pos = 0;
00291 
00292         while (pos < data.count())
00293         {
00294             QChar c = data[pos];
00295 
00296             if ( c == '(' )
00297                 stack++;
00298             else if ( c == ')' )
00299                 stack--;
00300             else if ( stack == 0 && c == ' ' )
00301                 field++;
00302             else
00303             {
00304                 switch ( field )
00305                 {
00306                     case PARENT_PID_FIELD:
00307                         parentPidString.append(c);
00308                         break;
00309                     case PROCESS_NAME_FIELD:
00310                         processNameString.append(c);
00311                         break;
00312                     case GROUP_PROCESS_FIELD:
00313                         foregroundPidString.append(c);
00314                         break;
00315                 }
00316             }
00317 
00318             pos++; 
00319         }
00320     }
00321     else
00322     {
00323         setFileError( processInfo.error() ); 
00324         return false;
00325     }
00326     
00327     // check that data was read successfully
00328     bool ok = false;
00329     int foregroundPid = foregroundPidString.toInt(&ok);    
00330     if (ok)
00331         setForegroundPid(foregroundPid);
00332 
00333     int parentPid = parentPidString.toInt(&ok);
00334     if (ok)
00335         setParentPid(parentPid);
00336 
00337     if (!processNameString.isEmpty()) 
00338         setName(processNameString);
00339 
00340     readArguments(pid);
00341     readCurrentDir(pid); 
00342 
00343     if ( enableEnvironmentRead )
00344     {
00345         readEnvironment(pid);
00346     }
00347 
00348     // update object state
00349     setPid(pid);
00350 
00351     return true;
00352 }
00353 
00354 void ProcessInfo::setFileError( QFile::FileError error )
00355 {
00356     switch ( error )
00357     {
00358         case PermissionsError:
00359             setError( PermissionsError );
00360             break;
00361         case NoError:
00362             setError( NoError );
00363             break;
00364         default:
00365             setError( UnknownError );
00366     }
00367 }
00368 
00369 ProcessInfo* ProcessInfo::newInstance(int pid,bool enableEnvironmentRead)
00370 {
00371 #ifdef Q_OS_UNIX
00372     return new UnixProcessInfo(pid,enableEnvironmentRead);
00373 #else
00374     return new NullProcessInfo(pid,enableEnvironmentRead);
00375 #endif
00376 }
00377 
00378 NullProcessInfo::NullProcessInfo(int pid,bool enableEnvironmentRead)
00379     : ProcessInfo(pid,enableEnvironmentRead)
00380 {
00381 }
00382 
00383 bool NullProcessInfo::readProcessInfo(int /*pid*/ , bool /*enableEnvironmentRead*/)
00384 {
00385     return false;
00386 }
00387 
00388 bool UnixProcessInfo::readArguments(int pid)
00389 {
00390     // read command-line arguments file found at /proc/<pid>/cmdline
00391     // the expected format is a list of strings delimited by null characters,
00392     // and ending in a double null character pair.
00393 
00394     QFile argumentsFile( QString("/proc/%1/cmdline").arg(pid) );
00395     if ( argumentsFile.open(QIODevice::ReadOnly) )
00396     {
00397         QTextStream stream(&argumentsFile);
00398         QString data = stream.readAll();
00399 
00400         QStringList argList = data.split( QChar('\0') );
00401         
00402         foreach ( QString entry , argList )
00403         {
00404             if (!entry.isEmpty())
00405                 addArgument(entry);
00406         }    
00407     }
00408     else
00409     {
00410         setFileError( argumentsFile.error() );
00411     }
00412 
00413     return true;
00414 }
00415 
00416 bool UnixProcessInfo::readCurrentDir(int pid)
00417 {
00418     QFileInfo info( QString("/proc/%1/cwd").arg(pid) );
00419 
00420     const bool readable = info.isReadable();
00421 
00422     if ( readable && info.isSymLink() )
00423     {
00424         setCurrentDir( info.symLinkTarget() );
00425         return true;
00426     }
00427     else
00428     {
00429         if ( !readable )
00430             setError( PermissionsError );
00431         else
00432             setError( UnknownError );
00433         
00434         return false;
00435     }
00436 }
00437 
00438 bool UnixProcessInfo::readEnvironment(int pid)
00439 {
00440     // read environment bindings file found at /proc/<pid>/environ
00441     // the expected format is a list of KEY=VALUE strings delimited by null
00442     // characters and ending in a double null character pair.
00443 
00444     QFile environmentFile( QString("/proc/%1/environ").arg(pid) );
00445     if ( environmentFile.open(QIODevice::ReadOnly) )
00446     {
00447         QTextStream stream(&environmentFile);
00448         QString data = stream.readAll();
00449 
00450         QStringList bindingList = data.split( QChar('\0') );
00451    
00452         foreach( QString entry , bindingList )
00453         {
00454             QString name;
00455             QString value;
00456 
00457             int splitPos = entry.indexOf('=');
00458 
00459             if ( splitPos != -1 )
00460             {
00461                 name = entry.mid(0,splitPos);
00462                 value = entry.mid(splitPos+1,-1);
00463 
00464                 addEnvironmentBinding(name,value);  
00465             }  
00466         }
00467     }
00468     else
00469     {
00470         setFileError( environmentFile.error() );
00471     }
00472 
00473     return true;
00474 }
00475 
00476 SSHProcessInfo::SSHProcessInfo(const ProcessInfo& process)
00477  : _process(process)
00478 {
00479     bool ok = false;
00480 
00481     // check that this is a SSH process
00482     const QString& name = _process.name(&ok);
00483 
00484     if ( !ok || name != "ssh" )
00485     {
00486         if ( !ok )
00487             kDebug() << "Could not read process info";
00488         else
00489             kDebug() << "Process is not a SSH process";
00490 
00491         return;
00492     }
00493 
00494     // read arguments
00495     const QVector<QString>& args = _process.arguments(&ok); 
00496 
00497     // SSH options
00498     // these are taken from the SSH manual ( accessed via 'man ssh' )
00499     
00500     // options which take no arguments
00501     static const QString noOptionsArguments("1246AaCfgkMNnqsTtVvXxY"); 
00502     // options which take one argument
00503     static const QString singleOptionArguments("bcDeFiLlmOopRSw");
00504 
00505     if ( ok )
00506     {
00507          // find the username, host and command arguments
00508          //
00509          // the username/host is assumed to be the first argument 
00510          // which is not an option
00511          // ( ie. does not start with a dash '-' character )
00512          // or an argument to a previous option.
00513          //
00514          // the command, if specified, is assumed to be the argument following
00515          // the username and host
00516          //
00517          // note that we skip the argument at index 0 because that is the
00518          // program name ( expected to be 'ssh' in this case )
00519          for ( int i = 1 ; i < args.count() ; i++ )
00520          {
00521             // if this argument is an option then skip it, plus any 
00522             // following arguments which refer to this option
00523             if ( args[i].startsWith('-') )
00524             {
00525                 QChar argChar = ( args[i].length() > 1 ) ? args[i][1] : '\0';
00526 
00527                 if ( noOptionsArguments.contains(argChar) )
00528                     continue;
00529                 else if ( singleOptionArguments.contains(argChar) )
00530                 {
00531                     i++;
00532                     continue;
00533                 }
00534             }
00535 
00536             // check whether the host has been found yet
00537             // if not, this must be the username/host argument 
00538             if ( _host.isEmpty() )
00539             {
00540                 // found username and host argument
00541                 kDebug() << "[username] and host: " << args[i];
00542 
00543                 // check to see if only a hostname is specified, or whether
00544                 // both a username and host are specified ( in which case they
00545                 // are separated by an '@' character:  username@host )
00546             
00547                 int separatorPosition = args[i].indexOf('@');
00548                 if ( separatorPosition != -1 )
00549                 {
00550                     // username and host specified
00551                     _user = args[i].left(separatorPosition);
00552                     _host = args[i].mid(separatorPosition+1);
00553 
00554                     kDebug() << "found user: " << _user;
00555                     kDebug() << "found host: " << _host;
00556                 }
00557                 else
00558                 {
00559                     // just the host specified
00560                     _host = args[i];
00561                     kDebug() << "found only host: " << _host;
00562                 }
00563             }
00564             else
00565             {
00566                 // host has already been found, this must be the command argument
00567                 _command = args[i];
00568 
00569                 kDebug() << "found command: " << _command;
00570             }
00571 
00572          }
00573     }
00574     else
00575     {
00576         kDebug() << "Could not read arguments";
00577         
00578         return;
00579     }
00580 }
00581 
00582 QString SSHProcessInfo::userName() const
00583 {
00584     return _user;
00585 }
00586 QString SSHProcessInfo::host() const
00587 {
00588     return _host;
00589 }
00590 QString SSHProcessInfo::command() const
00591 {
00592     return _command;
00593 }
00594 QString SSHProcessInfo::format(const QString& input) const
00595 {
00596     QString output(input);
00597    
00598     // test whether host is an ip address
00599     // in which case 'short host' and 'full host'
00600     // markers in the input string are replaced with
00601     // the full address
00602     bool isIpAddress = false;
00603    
00604     struct in_addr address;
00605     if ( inet_aton(_host.toLocal8Bit().constData(),&address) != 0 )
00606         isIpAddress = true;
00607     else
00608         isIpAddress = false;
00609 
00610     // search for and replace known markers
00611     output.replace("%u",_user);
00612 
00613     if ( isIpAddress )
00614         output.replace("%h",_host);
00615     else
00616         output.replace("%h",_host.left(_host.indexOf('.')));
00617     
00618     output.replace("%H",_host);
00619     output.replace("%c",_command);
00620 
00621     return output;
00622 }
00623 
00624 
00625 
00626 

Konsole

Skip menu "Konsole"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • Konsole
  • Libraries
  •   libkonq
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal