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
00231 QString ProcessInfo::currentDir(bool* ok) const
00232 {
00233 if (ok)
00234 *ok = _fields & CURRENT_DIR;
00235
00236 return _currentDir;
00237 }
00238 void ProcessInfo::setCurrentDir(const QString& dir)
00239 {
00240 _fields |= CURRENT_DIR;
00241 _currentDir = dir;
00242 }
00243
00244 void ProcessInfo::setName(const QString& name)
00245 {
00246 _name = name;
00247 _fields |= NAME;
00248 }
00249 void ProcessInfo::addArgument(const QString& argument)
00250 {
00251 _arguments << argument;
00252 }
00253
00254 void ProcessInfo::addEnvironmentBinding(const QString& name , const QString& value)
00255 {
00256 _environment.insert(name,value);
00257 }
00258
00259 void ProcessInfo::setFileError( QFile::FileError error )
00260 {
00261 switch ( error )
00262 {
00263 case PermissionsError:
00264 setError( PermissionsError );
00265 break;
00266 case NoError:
00267 setError( NoError );
00268 break;
00269 default:
00270 setError( UnknownError );
00271 }
00272 }
00273
00274
00275
00276
00277
00278
00279 NullProcessInfo::NullProcessInfo(int pid,bool enableEnvironmentRead)
00280 : ProcessInfo(pid,enableEnvironmentRead)
00281 {
00282 }
00283
00284 bool NullProcessInfo::readProcessInfo(int , bool )
00285 {
00286 return false;
00287 }
00288
00289 UnixProcessInfo::UnixProcessInfo(int pid,bool enableEnvironmentRead)
00290 : ProcessInfo(pid,enableEnvironmentRead)
00291 {
00292 }
00293
00294 bool UnixProcessInfo::readProcessInfo(int pid , bool enableEnvironmentRead)
00295 {
00296 bool ok = readProcInfo(pid);
00297 if (ok)
00298 {
00299 ok |= readArguments(pid);
00300 ok |= readCurrentDir(pid);
00301 if ( enableEnvironmentRead )
00302 {
00303 ok |= readEnvironment(pid);
00304 }
00305 }
00306 return ok;
00307 }
00308
00309
00310 class LinuxProcessInfo : public UnixProcessInfo
00311 {
00312 public:
00313 LinuxProcessInfo(int pid, bool env) :
00314 UnixProcessInfo(pid,env)
00315 {
00316 }
00317
00318 private:
00319 virtual bool readProcInfo(int pid)
00320 {
00321
00322
00323 const int PARENT_PID_FIELD = 3;
00324 const int PROCESS_NAME_FIELD = 1;
00325 const int GROUP_PROCESS_FIELD = 7;
00326
00327 QString parentPidString;
00328 QString processNameString;
00329 QString foregroundPidString;
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339 QFile processInfo( QString("/proc/%1/stat").arg(pid) );
00340 if ( processInfo.open(QIODevice::ReadOnly) )
00341 {
00342 QTextStream stream(&processInfo);
00343 QString data = stream.readAll();
00344
00345 int stack = 0;
00346 int field = 0;
00347 int pos = 0;
00348
00349 while (pos < data.count())
00350 {
00351 QChar c = data[pos];
00352
00353 if ( c == '(' )
00354 stack++;
00355 else if ( c == ')' )
00356 stack--;
00357 else if ( stack == 0 && c == ' ' )
00358 field++;
00359 else
00360 {
00361 switch ( field )
00362 {
00363 case PARENT_PID_FIELD:
00364 parentPidString.append(c);
00365 break;
00366 case PROCESS_NAME_FIELD:
00367 processNameString.append(c);
00368 break;
00369 case GROUP_PROCESS_FIELD:
00370 foregroundPidString.append(c);
00371 break;
00372 }
00373 }
00374
00375 pos++;
00376 }
00377 }
00378 else
00379 {
00380 setFileError( processInfo.error() );
00381 return false;
00382 }
00383
00384
00385 bool ok = false;
00386 int foregroundPid = foregroundPidString.toInt(&ok);
00387 if (ok)
00388 setForegroundPid(foregroundPid);
00389
00390 int parentPid = parentPidString.toInt(&ok);
00391 if (ok)
00392 setParentPid(parentPid);
00393
00394 if (!processNameString.isEmpty())
00395 setName(processNameString);
00396
00397
00398 setPid(pid);
00399
00400 return ok;
00401 }
00402
00403 virtual bool readArguments(int pid)
00404 {
00405
00406
00407
00408
00409 QFile argumentsFile( QString("/proc/%1/cmdline").arg(pid) );
00410 if ( argumentsFile.open(QIODevice::ReadOnly) )
00411 {
00412 QTextStream stream(&argumentsFile);
00413 QString data = stream.readAll();
00414
00415 QStringList argList = data.split( QChar('\0') );
00416
00417 foreach ( const QString &entry , argList )
00418 {
00419 if (!entry.isEmpty())
00420 addArgument(entry);
00421 }
00422 }
00423 else
00424 {
00425 setFileError( argumentsFile.error() );
00426 }
00427
00428 return true;
00429 }
00430
00431 virtual bool readCurrentDir(int pid)
00432 {
00433 QFileInfo info( QString("/proc/%1/cwd").arg(pid) );
00434
00435 const bool readable = info.isReadable();
00436
00437 if ( readable && info.isSymLink() )
00438 {
00439 setCurrentDir( info.symLinkTarget() );
00440 return true;
00441 }
00442 else
00443 {
00444 if ( !readable )
00445 setError( PermissionsError );
00446 else
00447 setError( UnknownError );
00448
00449 return false;
00450 }
00451 }
00452
00453 virtual bool readEnvironment(int pid)
00454 {
00455
00456
00457
00458
00459 QFile environmentFile( QString("/proc/%1/environ").arg(pid) );
00460 if ( environmentFile.open(QIODevice::ReadOnly) )
00461 {
00462 QTextStream stream(&environmentFile);
00463 QString data = stream.readAll();
00464
00465 QStringList bindingList = data.split( QChar('\0') );
00466
00467 foreach( const QString &entry , bindingList )
00468 {
00469 QString name;
00470 QString value;
00471
00472 int splitPos = entry.indexOf('=');
00473
00474 if ( splitPos != -1 )
00475 {
00476 name = entry.mid(0,splitPos);
00477 value = entry.mid(splitPos+1,-1);
00478
00479 addEnvironmentBinding(name,value);
00480 }
00481 }
00482 }
00483 else
00484 {
00485 setFileError( environmentFile.error() );
00486 }
00487
00488 return true;
00489 }
00490 } ;
00491
00492 #ifdef Q_OS_SOLARIS
00493
00494
00495
00496
00497
00498
00499 #if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS==64)
00500 #undef _FILE_OFFSET_BITS
00501 #endif
00502 #include <procfs.h>
00503 #else
00504
00505
00506
00507
00508
00509
00510 struct psinfo {
00511 int pr_ppid;
00512 int pr_pgid;
00513 char* pr_fname;
00514 char* pr_psargs;
00515 } ;
00516 static const int PRARGSZ=1;
00517 #endif
00518
00519 class SolarisProcessInfo : public UnixProcessInfo
00520 {
00521 public:
00522 SolarisProcessInfo(int pid, bool readEnvironment)
00523 : UnixProcessInfo(pid,readEnvironment)
00524 {
00525 }
00526 private:
00527 virtual bool readProcInfo(int pid)
00528 {
00529 QFile psinfo( QString("/proc/%1/psinfo").arg(pid) );
00530 if ( psinfo.open( QIODevice::ReadOnly ) )
00531 {
00532 struct psinfo info;
00533 if (psinfo.read((char *)&info,sizeof(info)) != sizeof(info))
00534 {
00535 return false;
00536 }
00537
00538 setParentPid(info.pr_ppid);
00539 setForegroundPid(info.pr_pgid);
00540 setName(info.pr_fname);
00541 setPid(pid);
00542
00543
00544 info.pr_psargs[PRARGSZ-1]=0;
00545 addArgument(info.pr_psargs);
00546 }
00547 return true;
00548 }
00549
00550 virtual bool readArguments(int )
00551 {
00552
00553 return true;
00554 }
00555
00556 virtual bool readEnvironment(int )
00557 {
00558
00559 return true;
00560 }
00561
00562 virtual bool readCurrentDir(int pid)
00563 {
00564 QFileInfo info( QString("/proc/%1/path/cwd").arg(pid) );
00565 const bool readable = info.isReadable();
00566
00567 if ( readable && info.isSymLink() )
00568 {
00569 setCurrentDir( info.symLinkTarget() );
00570 return true;
00571 }
00572 else
00573 {
00574 if ( !readable )
00575 setError( PermissionsError );
00576 else
00577 setError( UnknownError );
00578
00579 return false;
00580 }
00581 }
00582 } ;
00583
00584 SSHProcessInfo::SSHProcessInfo(const ProcessInfo& process)
00585 : _process(process)
00586 {
00587 bool ok = false;
00588
00589
00590 const QString& name = _process.name(&ok);
00591
00592 if ( !ok || name != "ssh" )
00593 {
00594 if ( !ok )
00595 kDebug() << "Could not read process info";
00596 else
00597 kDebug() << "Process is not a SSH process";
00598
00599 return;
00600 }
00601
00602
00603 const QVector<QString>& args = _process.arguments(&ok);
00604
00605
00606
00607
00608
00609 static const QString noOptionsArguments("1246AaCfgkMNnqsTtVvXxY");
00610
00611 static const QString singleOptionArguments("bcDeFiLlmOopRSw");
00612
00613 if ( ok )
00614 {
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627 for ( int i = 1 ; i < args.count() ; i++ )
00628 {
00629
00630
00631 if ( args[i].startsWith('-') )
00632 {
00633 QChar argChar = ( args[i].length() > 1 ) ? args[i][1] : '\0';
00634
00635 if ( noOptionsArguments.contains(argChar) )
00636 continue;
00637 else if ( singleOptionArguments.contains(argChar) )
00638 {
00639 i++;
00640 continue;
00641 }
00642 }
00643
00644
00645
00646 if ( _host.isEmpty() )
00647 {
00648
00649
00650
00651
00652 int separatorPosition = args[i].indexOf('@');
00653 if ( separatorPosition != -1 )
00654 {
00655
00656 _user = args[i].left(separatorPosition);
00657 _host = args[i].mid(separatorPosition+1);
00658 }
00659 else
00660 {
00661
00662 _host = args[i];
00663 }
00664 }
00665 else
00666 {
00667
00668 _command = args[i];
00669 }
00670
00671 }
00672 }
00673 else
00674 {
00675 kDebug() << "Could not read arguments";
00676
00677 return;
00678 }
00679 }
00680
00681 QString SSHProcessInfo::userName() const
00682 {
00683 return _user;
00684 }
00685 QString SSHProcessInfo::host() const
00686 {
00687 return _host;
00688 }
00689 QString SSHProcessInfo::command() const
00690 {
00691 return _command;
00692 }
00693 QString SSHProcessInfo::format(const QString& input) const
00694 {
00695 QString output(input);
00696
00697
00698
00699
00700
00701 bool isIpAddress = false;
00702
00703 struct in_addr address;
00704 if ( inet_aton(_host.toLocal8Bit().constData(),&address) != 0 )
00705 isIpAddress = true;
00706 else
00707 isIpAddress = false;
00708
00709
00710 output.replace("%u",_user);
00711
00712 if ( isIpAddress )
00713 output.replace("%h",_host);
00714 else
00715 output.replace("%h",_host.left(_host.indexOf('.')));
00716
00717 output.replace("%H",_host);
00718 output.replace("%c",_command);
00719
00720 return output;
00721 }
00722
00723 ProcessInfo* ProcessInfo::newInstance(int pid,bool enableEnvironmentRead)
00724 {
00725 #ifdef Q_OS_LINUX
00726 return new LinuxProcessInfo(pid,enableEnvironmentRead);
00727 #elif defined(Q_OS_SOLARIS)
00728 return new SolarisProcessInfo(pid,enableEnvironmentRead);
00729 #else
00730 return new NullProcessInfo(pid,enableEnvironmentRead);
00731 #endif
00732 }
00733