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::validCurrentDir() const
00068 {
00069 bool ok = false;
00070
00071
00072
00073 int currentPid = parentPid(&ok);
00074 QString dir = currentDir(&ok);
00075 while ( !ok && currentPid != 0 )
00076 {
00077 ProcessInfo* current = ProcessInfo::newInstance(currentPid);
00078 current->update();
00079 currentPid = current->parentPid(&ok);
00080 dir = current->currentDir(&ok);
00081 delete current;
00082 }
00083
00084 return dir;
00085 }
00086
00087 QString ProcessInfo::format(const QString& input) const
00088 {
00089 bool ok = false;
00090
00091 QString output(input);
00092
00093
00094 output.replace("%u","NOT IMPLEMENTED YET");
00095 output.replace("%n",name(&ok));
00096 output.replace("%c",formatCommand(name(&ok),arguments(&ok),ShortCommandFormat));
00097 output.replace("%C",formatCommand(name(&ok),arguments(&ok),LongCommandFormat));
00098
00099 QString dir = validCurrentDir();
00100 output.replace("%D",dir);
00101 output.replace("%d",formatShortDir(dir));
00102
00103
00104
00105
00106 return output;
00107 }
00108
00109 QString ProcessInfo::formatCommand(const QString& name,
00110 const QVector<QString>& arguments,
00111 CommandFormat format) const
00112 {
00113
00114 return QStringList(QList<QString>::fromVector(arguments)).join(" ");
00115 }
00116
00117 QSet<QString> ProcessInfo::_commonDirNames;
00118
00119 QSet<QString> ProcessInfo::commonDirNames()
00120 {
00121 if ( _commonDirNames.isEmpty() )
00122 {
00123 KSharedConfigPtr config = KGlobal::config();
00124 KConfigGroup configGroup = config->group("ProcessInfo");
00125
00126 QStringList defaults = QStringList()
00127 << "src" << "build" << "debug" << "release"
00128 << "bin" << "lib" << "libs" << "tmp"
00129 << "doc" << "docs" << "data" << "share"
00130 << "examples" << "icons" << "pics" << "plugins"
00131 << "tests" << "media" << "l10n" << "include"
00132 << "includes" << "locale" << "ui";
00133
00134 _commonDirNames = QSet<QString>::fromList(configGroup.readEntry("CommonDirNames",defaults));
00135
00136 }
00137
00138 return _commonDirNames;
00139 }
00140
00141 QString ProcessInfo::formatShortDir(const QString& input) const
00142 {
00143 QString result;
00144
00145 QStringList parts = input.split( QDir::separator() );
00146
00147
00148 QSet<QString> dirNamesToShorten = commonDirNames();
00149
00150 QListIterator<QString> iter(parts);
00151 iter.toBack();
00152
00153
00154
00155
00156
00157 while ( iter.hasPrevious() )
00158 {
00159 QString part = iter.previous();
00160
00161 if ( dirNamesToShorten.contains(part) )
00162 {
00163 result.prepend(QDir::separator() + part[0]);
00164 }
00165 else
00166 {
00167 result.prepend(part);
00168 break;
00169 }
00170 }
00171
00172 return result;
00173 }
00174
00175 QVector<QString> ProcessInfo::arguments(bool* ok) const
00176 {
00177 *ok = _fields & ARGUMENTS;
00178
00179 return _arguments;
00180 }
00181
00182 QMap<QString,QString> ProcessInfo::environment(bool* ok) const
00183 {
00184 *ok = _fields & ENVIRONMENT;
00185
00186 return _environment;
00187 }
00188
00189 bool ProcessInfo::isValid() const
00190 {
00191 return _fields & PROCESS_ID;
00192 }
00193
00194 int ProcessInfo::pid(bool* ok) const
00195 {
00196 *ok = _fields & PROCESS_ID;
00197
00198 return _pid;
00199 }
00200
00201 int ProcessInfo::parentPid(bool* ok) const
00202 {
00203 *ok = _fields & PARENT_PID;
00204
00205 return _parentPid;
00206 }
00207
00208 int ProcessInfo::foregroundPid(bool* ok) const
00209 {
00210 *ok = _fields & FOREGROUND_PID;
00211
00212 return _foregroundPid;
00213 }
00214
00215 QString ProcessInfo::name(bool* ok) const
00216 {
00217 *ok = _fields & NAME;
00218
00219 return _name;
00220 }
00221
00222 void ProcessInfo::setPid(int pid)
00223 {
00224 _pid = pid;
00225 _fields |= PROCESS_ID;
00226 }
00227
00228 void ProcessInfo::setParentPid(int pid)
00229 {
00230 _parentPid = pid;
00231 _fields |= PARENT_PID;
00232 }
00233 void ProcessInfo::setForegroundPid(int pid)
00234 {
00235 _foregroundPid = pid;
00236 _fields |= FOREGROUND_PID;
00237 }
00238
00239 QString ProcessInfo::currentDir(bool* ok) const
00240 {
00241 if (ok)
00242 *ok = _fields & CURRENT_DIR;
00243
00244 return _currentDir;
00245 }
00246 void ProcessInfo::setCurrentDir(const QString& dir)
00247 {
00248 _fields |= CURRENT_DIR;
00249 _currentDir = dir;
00250 }
00251
00252 void ProcessInfo::setName(const QString& name)
00253 {
00254 _name = name;
00255 _fields |= NAME;
00256 }
00257 void ProcessInfo::addArgument(const QString& argument)
00258 {
00259 _arguments << argument;
00260 }
00261
00262 void ProcessInfo::addEnvironmentBinding(const QString& name , const QString& value)
00263 {
00264 _environment.insert(name,value);
00265 }
00266
00267 void ProcessInfo::setFileError( QFile::FileError error )
00268 {
00269 switch ( error )
00270 {
00271 case PermissionsError:
00272 setError( PermissionsError );
00273 break;
00274 case NoError:
00275 setError( NoError );
00276 break;
00277 default:
00278 setError( UnknownError );
00279 }
00280 }
00281
00282
00283
00284
00285
00286
00287 NullProcessInfo::NullProcessInfo(int pid,bool enableEnvironmentRead)
00288 : ProcessInfo(pid,enableEnvironmentRead)
00289 {
00290 }
00291
00292 bool NullProcessInfo::readProcessInfo(int , bool )
00293 {
00294 return false;
00295 }
00296
00297 UnixProcessInfo::UnixProcessInfo(int pid,bool enableEnvironmentRead)
00298 : ProcessInfo(pid,enableEnvironmentRead)
00299 {
00300 }
00301
00302 bool UnixProcessInfo::readProcessInfo(int pid , bool enableEnvironmentRead)
00303 {
00304 bool ok = readProcInfo(pid);
00305 if (ok)
00306 {
00307 ok |= readArguments(pid);
00308 ok |= readCurrentDir(pid);
00309 if ( enableEnvironmentRead )
00310 {
00311 ok |= readEnvironment(pid);
00312 }
00313 }
00314 return ok;
00315 }
00316
00317
00318 class LinuxProcessInfo : public UnixProcessInfo
00319 {
00320 public:
00321 LinuxProcessInfo(int pid, bool env) :
00322 UnixProcessInfo(pid,env)
00323 {
00324 }
00325
00326 private:
00327 virtual bool readProcInfo(int pid)
00328 {
00329
00330
00331 const int PARENT_PID_FIELD = 3;
00332 const int PROCESS_NAME_FIELD = 1;
00333 const int GROUP_PROCESS_FIELD = 7;
00334
00335 QString parentPidString;
00336 QString processNameString;
00337 QString foregroundPidString;
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347 QFile processInfo( QString("/proc/%1/stat").arg(pid) );
00348 if ( processInfo.open(QIODevice::ReadOnly) )
00349 {
00350 QTextStream stream(&processInfo);
00351 QString data = stream.readAll();
00352
00353 int stack = 0;
00354 int field = 0;
00355 int pos = 0;
00356
00357 while (pos < data.count())
00358 {
00359 QChar c = data[pos];
00360
00361 if ( c == '(' )
00362 stack++;
00363 else if ( c == ')' )
00364 stack--;
00365 else if ( stack == 0 && c == ' ' )
00366 field++;
00367 else
00368 {
00369 switch ( field )
00370 {
00371 case PARENT_PID_FIELD:
00372 parentPidString.append(c);
00373 break;
00374 case PROCESS_NAME_FIELD:
00375 processNameString.append(c);
00376 break;
00377 case GROUP_PROCESS_FIELD:
00378 foregroundPidString.append(c);
00379 break;
00380 }
00381 }
00382
00383 pos++;
00384 }
00385 }
00386 else
00387 {
00388 setFileError( processInfo.error() );
00389 return false;
00390 }
00391
00392
00393 bool ok = false;
00394 int foregroundPid = foregroundPidString.toInt(&ok);
00395 if (ok)
00396 setForegroundPid(foregroundPid);
00397
00398 int parentPid = parentPidString.toInt(&ok);
00399 if (ok)
00400 setParentPid(parentPid);
00401
00402 if (!processNameString.isEmpty())
00403 setName(processNameString);
00404
00405
00406 setPid(pid);
00407
00408 return ok;
00409 }
00410
00411 virtual bool readArguments(int pid)
00412 {
00413
00414
00415
00416
00417 QFile argumentsFile( QString("/proc/%1/cmdline").arg(pid) );
00418 if ( argumentsFile.open(QIODevice::ReadOnly) )
00419 {
00420 QTextStream stream(&argumentsFile);
00421 QString data = stream.readAll();
00422
00423 QStringList argList = data.split( QChar('\0') );
00424
00425 foreach ( const QString &entry , argList )
00426 {
00427 if (!entry.isEmpty())
00428 addArgument(entry);
00429 }
00430 }
00431 else
00432 {
00433 setFileError( argumentsFile.error() );
00434 }
00435
00436 return true;
00437 }
00438
00439 virtual bool readCurrentDir(int pid)
00440 {
00441 QFileInfo info( QString("/proc/%1/cwd").arg(pid) );
00442
00443 const bool readable = info.isReadable();
00444
00445 if ( readable && info.isSymLink() )
00446 {
00447 setCurrentDir( info.symLinkTarget() );
00448 return true;
00449 }
00450 else
00451 {
00452 if ( !readable )
00453 setError( PermissionsError );
00454 else
00455 setError( UnknownError );
00456
00457 return false;
00458 }
00459 }
00460
00461 virtual bool readEnvironment(int pid)
00462 {
00463
00464
00465
00466
00467 QFile environmentFile( QString("/proc/%1/environ").arg(pid) );
00468 if ( environmentFile.open(QIODevice::ReadOnly) )
00469 {
00470 QTextStream stream(&environmentFile);
00471 QString data = stream.readAll();
00472
00473 QStringList bindingList = data.split( QChar('\0') );
00474
00475 foreach( const QString &entry , bindingList )
00476 {
00477 QString name;
00478 QString value;
00479
00480 int splitPos = entry.indexOf('=');
00481
00482 if ( splitPos != -1 )
00483 {
00484 name = entry.mid(0,splitPos);
00485 value = entry.mid(splitPos+1,-1);
00486
00487 addEnvironmentBinding(name,value);
00488 }
00489 }
00490 }
00491 else
00492 {
00493 setFileError( environmentFile.error() );
00494 }
00495
00496 return true;
00497 }
00498 } ;
00499
00500 #ifdef Q_OS_SOLARIS
00501
00502
00503
00504
00505
00506
00507 #if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS==64)
00508 #undef _FILE_OFFSET_BITS
00509 #endif
00510 #include <procfs.h>
00511 #else
00512
00513
00514
00515
00516
00517
00518 struct psinfo {
00519 int pr_ppid;
00520 int pr_pgid;
00521 char* pr_fname;
00522 char* pr_psargs;
00523 } ;
00524 static const int PRARGSZ=1;
00525 #endif
00526
00527 class SolarisProcessInfo : public UnixProcessInfo
00528 {
00529 public:
00530 SolarisProcessInfo(int pid, bool readEnvironment)
00531 : UnixProcessInfo(pid,readEnvironment)
00532 {
00533 }
00534 private:
00535 virtual bool readProcInfo(int pid)
00536 {
00537 QFile psinfo( QString("/proc/%1/psinfo").arg(pid) );
00538 if ( psinfo.open( QIODevice::ReadOnly ) )
00539 {
00540 struct psinfo info;
00541 if (psinfo.read((char *)&info,sizeof(info)) != sizeof(info))
00542 {
00543 return false;
00544 }
00545
00546 setParentPid(info.pr_ppid);
00547 setForegroundPid(info.pr_pgid);
00548 setName(info.pr_fname);
00549 setPid(pid);
00550
00551
00552 info.pr_psargs[PRARGSZ-1]=0;
00553 addArgument(info.pr_psargs);
00554 }
00555 return true;
00556 }
00557
00558 virtual bool readArguments(int )
00559 {
00560
00561 return true;
00562 }
00563
00564 virtual bool readEnvironment(int )
00565 {
00566
00567 return true;
00568 }
00569
00570 virtual bool readCurrentDir(int pid)
00571 {
00572 QFileInfo info( QString("/proc/%1/path/cwd").arg(pid) );
00573 const bool readable = info.isReadable();
00574
00575 if ( readable && info.isSymLink() )
00576 {
00577 setCurrentDir( info.symLinkTarget() );
00578 return true;
00579 }
00580 else
00581 {
00582 if ( !readable )
00583 setError( PermissionsError );
00584 else
00585 setError( UnknownError );
00586
00587 return false;
00588 }
00589 }
00590 } ;
00591
00592 SSHProcessInfo::SSHProcessInfo(const ProcessInfo& process)
00593 : _process(process)
00594 {
00595 bool ok = false;
00596
00597
00598 const QString& name = _process.name(&ok);
00599
00600 if ( !ok || name != "ssh" )
00601 {
00602 if ( !ok )
00603 kDebug() << "Could not read process info";
00604 else
00605 kDebug() << "Process is not a SSH process";
00606
00607 return;
00608 }
00609
00610
00611 const QVector<QString>& args = _process.arguments(&ok);
00612
00613
00614
00615
00616
00617 static const QString noOptionsArguments("1246AaCfgkMNnqsTtVvXxY");
00618
00619 static const QString singleOptionArguments("bcDeFiLlmOopRSw");
00620
00621 if ( ok )
00622 {
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635 for ( int i = 1 ; i < args.count() ; i++ )
00636 {
00637
00638
00639 if ( args[i].startsWith('-') )
00640 {
00641 QChar argChar = ( args[i].length() > 1 ) ? args[i][1] : '\0';
00642
00643 if ( noOptionsArguments.contains(argChar) )
00644 continue;
00645 else if ( singleOptionArguments.contains(argChar) )
00646 {
00647 i++;
00648 continue;
00649 }
00650 }
00651
00652
00653
00654 if ( _host.isEmpty() )
00655 {
00656
00657
00658
00659
00660 int separatorPosition = args[i].indexOf('@');
00661 if ( separatorPosition != -1 )
00662 {
00663
00664 _user = args[i].left(separatorPosition);
00665 _host = args[i].mid(separatorPosition+1);
00666 }
00667 else
00668 {
00669
00670 _host = args[i];
00671 }
00672 }
00673 else
00674 {
00675
00676 _command = args[i];
00677 }
00678
00679 }
00680 }
00681 else
00682 {
00683 kDebug() << "Could not read arguments";
00684
00685 return;
00686 }
00687 }
00688
00689 QString SSHProcessInfo::userName() const
00690 {
00691 return _user;
00692 }
00693 QString SSHProcessInfo::host() const
00694 {
00695 return _host;
00696 }
00697 QString SSHProcessInfo::command() const
00698 {
00699 return _command;
00700 }
00701 QString SSHProcessInfo::format(const QString& input) const
00702 {
00703 QString output(input);
00704
00705
00706
00707
00708
00709 bool isIpAddress = false;
00710
00711 struct in_addr address;
00712 if ( inet_aton(_host.toLocal8Bit().constData(),&address) != 0 )
00713 isIpAddress = true;
00714 else
00715 isIpAddress = false;
00716
00717
00718 output.replace("%u",_user);
00719
00720 if ( isIpAddress )
00721 output.replace("%h",_host);
00722 else
00723 output.replace("%h",_host.left(_host.indexOf('.')));
00724
00725 output.replace("%H",_host);
00726 output.replace("%c",_command);
00727
00728 return output;
00729 }
00730
00731 ProcessInfo* ProcessInfo::newInstance(int pid,bool enableEnvironmentRead)
00732 {
00733 #ifdef Q_OS_LINUX
00734 return new LinuxProcessInfo(pid,enableEnvironmentRead);
00735 #elif defined(Q_OS_SOLARIS)
00736 return new SolarisProcessInfo(pid,enableEnvironmentRead);
00737 #else
00738 return new NullProcessInfo(pid,enableEnvironmentRead);
00739 #endif
00740 }
00741
00742
00743
00744
00745
00746
00747
00748
00749