Konsole
ProcessInfo.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "ProcessInfo.h"
00022
00023
00024 #include <sys/socket.h>
00025 #include <netinet/in.h>
00026 #include <arpa/inet.h>
00027
00028
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
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 )
00045
00046
00047
00048
00049
00050
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
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
00080
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
00096
00097
00098 return output;
00099 }
00100
00101 QString ProcessInfo::formatCommand(const QString& name,
00102 const QVector<QString>& arguments,
00103 CommandFormat format) const
00104 {
00105
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
00140 QSet<QString> dirNamesToShorten = commonDirNames();
00141
00142 QListIterator<QString> iter(parts);
00143 iter.toBack();
00144
00145
00146
00147
00148
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
00265
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
00275
00276
00277
00278
00279
00280
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
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
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 , bool )
00384 {
00385 return false;
00386 }
00387
00388 bool UnixProcessInfo::readArguments(int pid)
00389 {
00390
00391
00392
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
00441
00442
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
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
00495 const QVector<QString>& args = _process.arguments(&ok);
00496
00497
00498
00499
00500
00501 static const QString noOptionsArguments("1246AaCfgkMNnqsTtVvXxY");
00502
00503 static const QString singleOptionArguments("bcDeFiLlmOopRSw");
00504
00505 if ( ok )
00506 {
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519 for ( int i = 1 ; i < args.count() ; i++ )
00520 {
00521
00522
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
00537
00538 if ( _host.isEmpty() )
00539 {
00540
00541 kDebug() << "[username] and host: " << args[i];
00542
00543
00544
00545
00546
00547 int separatorPosition = args[i].indexOf('@');
00548 if ( separatorPosition != -1 )
00549 {
00550
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
00560 _host = args[i];
00561 kDebug() << "found only host: " << _host;
00562 }
00563 }
00564 else
00565 {
00566
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
00599
00600
00601
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
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