You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1066 lines
28 KiB
1066 lines
28 KiB
/* |
|
Copyright 2007-2008 by Robert Knight <robertknight@gmail.countm> |
|
|
|
This program is free software; you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation; either version 2 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program; if not, write to the Free Software |
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
02110-1301 USA. |
|
*/ |
|
|
|
// Own |
|
#include "ProcessInfo.h" |
|
#include <config-konsole.h> |
|
|
|
// Unix |
|
#include <sys/socket.h> |
|
#include <netinet/in.h> |
|
#include <arpa/inet.h> |
|
#include <unistd.h> |
|
#include <pwd.h> |
|
|
|
// Qt |
|
#include <KDebug> |
|
#include <QtCore/QDir> |
|
#include <QtCore/QFileInfo> |
|
#include <QtCore/QRegExp> |
|
#include <QtCore/QTextStream> |
|
#include <QtCore/QStringList> |
|
|
|
// KDE |
|
#include <KConfigGroup> |
|
#include <KGlobal> |
|
#include <KSharedConfig> |
|
|
|
#if defined(Q_OS_MAC) |
|
#include <sys/sysctl.h> |
|
#ifdef HAVE_SYS_PROC_INFO_H |
|
#include <sys/proc_info.h> |
|
#endif |
|
#ifdef HAVE_SYS_PROC_H |
|
#include <sys/proc.h> |
|
#endif |
|
#include <kde_file.h> |
|
#endif |
|
|
|
#if defined(Q_OS_FREEBSD) |
|
#include <sys/sysctl.h> //krazy:exclude=includes |
|
#include <sys/types.h> |
|
#include <sys/user.h> |
|
#include <libutil.h> |
|
#endif |
|
|
|
using namespace Konsole; |
|
|
|
ProcessInfo::ProcessInfo(int pid , bool enableEnvironmentRead) |
|
: _fields( ARGUMENTS | ENVIRONMENT ) // arguments and environments |
|
// are currently always valid, |
|
// they just return an empty |
|
// vector / map respectively |
|
// if no arguments |
|
// or environment bindings |
|
// have been explicitly set |
|
, _enableEnvironmentRead(enableEnvironmentRead) |
|
, _pid(pid) |
|
, _parentPid(0) |
|
, _foregroundPid(0) |
|
, _userId(0) |
|
, _lastError(NoError) |
|
, _userName(QString()) |
|
{ |
|
} |
|
|
|
ProcessInfo::Error ProcessInfo::error() const { return _lastError; } |
|
void ProcessInfo::setError(Error error) { _lastError = error; } |
|
|
|
void ProcessInfo::update() |
|
{ |
|
readProcessInfo(_pid,_enableEnvironmentRead); |
|
} |
|
|
|
QString ProcessInfo::validCurrentDir() const |
|
{ |
|
bool ok = false; |
|
|
|
// read current dir, if an error occurs try the parent as the next |
|
// best option |
|
int currentPid = parentPid(&ok); |
|
QString dir = currentDir(&ok); |
|
while ( !ok && currentPid != 0 ) |
|
{ |
|
ProcessInfo* current = ProcessInfo::newInstance(currentPid); |
|
current->update(); |
|
currentPid = current->parentPid(&ok); |
|
dir = current->currentDir(&ok); |
|
delete current; |
|
} |
|
|
|
return dir; |
|
} |
|
|
|
QString ProcessInfo::format(const QString& input) const |
|
{ |
|
bool ok = false; |
|
|
|
QString output(input); |
|
|
|
// search for and replace known marker |
|
output.replace("%u",userName()); |
|
output.replace("%n",name(&ok)); |
|
output.replace("%c",formatCommand(name(&ok),arguments(&ok),ShortCommandFormat)); |
|
output.replace("%C",formatCommand(name(&ok),arguments(&ok),LongCommandFormat)); |
|
|
|
QString dir = validCurrentDir(); |
|
output.replace("%D",dir); |
|
output.replace("%d",formatShortDir(dir)); |
|
|
|
// remove any remaining %[LETTER] sequences |
|
// output.replace(QRegExp("%\\w"), QString()); |
|
|
|
return output; |
|
} |
|
|
|
QString ProcessInfo::formatCommand(const QString& name, |
|
const QVector<QString>& arguments, |
|
CommandFormat format) const |
|
{ |
|
Q_UNUSED(name); |
|
Q_UNUSED(format); |
|
|
|
// TODO Implement me |
|
return QStringList(QList<QString>::fromVector(arguments)).join(" "); |
|
} |
|
|
|
QSet<QString> ProcessInfo::_commonDirNames; |
|
|
|
QSet<QString> ProcessInfo::commonDirNames() |
|
{ |
|
if ( _commonDirNames.isEmpty() ) |
|
{ |
|
KSharedConfigPtr config = KGlobal::config(); |
|
KConfigGroup configGroup = config->group("ProcessInfo"); |
|
|
|
QStringList defaults = QStringList() |
|
<< "src" << "build" << "debug" << "release" |
|
<< "bin" << "lib" << "libs" << "tmp" |
|
<< "doc" << "docs" << "data" << "share" |
|
<< "examples" << "icons" << "pics" << "plugins" |
|
<< "tests" << "media" << "l10n" << "include" |
|
<< "includes" << "locale" << "ui"; |
|
|
|
_commonDirNames = QSet<QString>::fromList(configGroup.readEntry("CommonDirNames",defaults)); |
|
|
|
} |
|
|
|
return _commonDirNames; |
|
} |
|
|
|
QString ProcessInfo::formatShortDir(const QString& input) const |
|
{ |
|
QString result; |
|
|
|
QStringList parts = input.split( QDir::separator() ); |
|
|
|
// temporarily hard-coded |
|
QSet<QString> dirNamesToShorten = commonDirNames(); |
|
|
|
QListIterator<QString> iter(parts); |
|
iter.toBack(); |
|
|
|
// go backwards through the list of the path's parts |
|
// adding abbreviations of common directory names |
|
// and stopping when we reach a dir name which is not |
|
// in the commonDirNames set |
|
while ( iter.hasPrevious() ) |
|
{ |
|
QString part = iter.previous(); |
|
|
|
if ( dirNamesToShorten.contains(part) ) |
|
{ |
|
result.prepend(QDir::separator() + part[0]); |
|
} |
|
else |
|
{ |
|
result.prepend(part); |
|
break; |
|
} |
|
} |
|
|
|
return result; |
|
} |
|
|
|
QVector<QString> ProcessInfo::arguments(bool* ok) const |
|
{ |
|
*ok = _fields & ARGUMENTS; |
|
|
|
return _arguments; |
|
} |
|
|
|
QMap<QString,QString> ProcessInfo::environment(bool* ok) const |
|
{ |
|
*ok = _fields & ENVIRONMENT; |
|
|
|
return _environment; |
|
} |
|
|
|
bool ProcessInfo::isValid() const |
|
{ |
|
return _fields & PROCESS_ID; |
|
} |
|
|
|
int ProcessInfo::pid(bool* ok) const |
|
{ |
|
*ok = _fields & PROCESS_ID; |
|
|
|
return _pid; |
|
} |
|
|
|
int ProcessInfo::parentPid(bool* ok) const |
|
{ |
|
*ok = _fields & PARENT_PID; |
|
|
|
return _parentPid; |
|
} |
|
|
|
int ProcessInfo::foregroundPid(bool* ok) const |
|
{ |
|
*ok = _fields & FOREGROUND_PID; |
|
|
|
return _foregroundPid; |
|
} |
|
|
|
QString ProcessInfo::name(bool* ok) const |
|
{ |
|
*ok = _fields & NAME; |
|
|
|
return _name; |
|
} |
|
|
|
int ProcessInfo::userId(bool* ok) const |
|
{ |
|
*ok = _fields & UID; |
|
|
|
return _userId; |
|
} |
|
|
|
QString ProcessInfo::userName() const |
|
{ |
|
return _userName; |
|
} |
|
|
|
void ProcessInfo::setPid(int pid) |
|
{ |
|
_pid = pid; |
|
_fields |= PROCESS_ID; |
|
} |
|
|
|
void ProcessInfo::setUserId(int uid) |
|
{ |
|
_userId = uid; |
|
_fields |= UID; |
|
} |
|
|
|
void ProcessInfo::setUserName(const QString& name) |
|
{ |
|
_userName = name; |
|
} |
|
|
|
|
|
void ProcessInfo::setParentPid(int pid) |
|
{ |
|
_parentPid = pid; |
|
_fields |= PARENT_PID; |
|
} |
|
void ProcessInfo::setForegroundPid(int pid) |
|
{ |
|
_foregroundPid = pid; |
|
_fields |= FOREGROUND_PID; |
|
} |
|
|
|
QString ProcessInfo::currentDir(bool* ok) const |
|
{ |
|
if (ok) |
|
*ok = _fields & CURRENT_DIR; |
|
|
|
return _currentDir; |
|
} |
|
void ProcessInfo::setCurrentDir(const QString& dir) |
|
{ |
|
_fields |= CURRENT_DIR; |
|
_currentDir = dir; |
|
} |
|
|
|
void ProcessInfo::setName(const QString& name) |
|
{ |
|
_name = name; |
|
_fields |= NAME; |
|
} |
|
void ProcessInfo::addArgument(const QString& argument) |
|
{ |
|
_arguments << argument; |
|
} |
|
|
|
void ProcessInfo::addEnvironmentBinding(const QString& name , const QString& value) |
|
{ |
|
_environment.insert(name,value); |
|
} |
|
|
|
void ProcessInfo::setFileError( QFile::FileError error ) |
|
{ |
|
switch ( error ) |
|
{ |
|
case PermissionsError: |
|
setError( PermissionsError ); |
|
break; |
|
case NoError: |
|
setError( NoError ); |
|
break; |
|
default: |
|
setError( UnknownError ); |
|
} |
|
} |
|
|
|
// |
|
// ProcessInfo::newInstance() is way at the bottom so it can see all of the |
|
// implementations of the UnixProcessInfo abstract class. |
|
// |
|
|
|
NullProcessInfo::NullProcessInfo(int pid,bool enableEnvironmentRead) |
|
: ProcessInfo(pid,enableEnvironmentRead) |
|
{ |
|
} |
|
|
|
bool NullProcessInfo::readProcessInfo(int /*pid*/ , bool /*enableEnvironmentRead*/) |
|
{ |
|
return false; |
|
} |
|
|
|
void NullProcessInfo::readUserName() |
|
{ |
|
} |
|
|
|
UnixProcessInfo::UnixProcessInfo(int pid,bool enableEnvironmentRead) |
|
: ProcessInfo(pid,enableEnvironmentRead) |
|
{ |
|
} |
|
|
|
bool UnixProcessInfo::readProcessInfo(int pid , bool enableEnvironmentRead) |
|
{ |
|
bool ok = readProcInfo(pid); |
|
if (ok) |
|
{ |
|
ok |= readArguments(pid); |
|
ok |= readCurrentDir(pid); |
|
if ( enableEnvironmentRead ) |
|
{ |
|
ok |= readEnvironment(pid); |
|
} |
|
} |
|
return ok; |
|
} |
|
|
|
void UnixProcessInfo::readUserName() |
|
{ |
|
bool ok = false; |
|
int uid = userId(&ok); |
|
if (!ok) return; |
|
|
|
struct passwd passwdStruct; |
|
struct passwd *getpwResult; |
|
char *getpwBuffer; |
|
long getpwBufferSize; |
|
int getpwStatus; |
|
|
|
getpwBufferSize = sysconf(_SC_GETPW_R_SIZE_MAX); |
|
if (getpwBufferSize == -1) |
|
getpwBufferSize = 16384; |
|
|
|
getpwBuffer = new char[getpwBufferSize]; |
|
if (getpwBuffer == NULL) |
|
return; |
|
getpwStatus = getpwuid_r(uid, &passwdStruct, getpwBuffer, getpwBufferSize, &getpwResult); |
|
if (getpwResult != NULL) |
|
setUserName(QString(passwdStruct.pw_name)); |
|
else |
|
setUserName(QString()); |
|
delete [] getpwBuffer; |
|
} |
|
|
|
|
|
class LinuxProcessInfo : public UnixProcessInfo |
|
{ |
|
public: |
|
LinuxProcessInfo(int pid, bool env) : |
|
UnixProcessInfo(pid,env) |
|
{ |
|
} |
|
|
|
private: |
|
virtual bool readProcInfo(int pid) |
|
{ |
|
// indicies of various fields within the process status file which |
|
// contain various information about the process |
|
const int PARENT_PID_FIELD = 3; |
|
const int PROCESS_NAME_FIELD = 1; |
|
const int GROUP_PROCESS_FIELD = 7; |
|
|
|
QString parentPidString; |
|
QString processNameString; |
|
QString foregroundPidString; |
|
QString uidLine; |
|
QString uidString; |
|
QStringList uidStrings; |
|
|
|
// For user id read process status file ( /proc/<pid>/status ) |
|
// Can not use getuid() due to it does not work for 'su' |
|
QFile statusInfo( QString("/proc/%1/status").arg(pid) ); |
|
if ( statusInfo.open(QIODevice::ReadOnly) ) |
|
{ |
|
QTextStream stream(&statusInfo); |
|
QString statusLine; |
|
do { |
|
statusLine = stream.readLine(0); |
|
if (statusLine.startsWith(QLatin1String("Uid:"))) |
|
uidLine = statusLine; |
|
} while (!statusLine.isNull() && uidLine.isNull()); |
|
|
|
uidStrings << uidLine.split('\t', QString::SkipEmptyParts); |
|
// Must be 5 entries: 'Uid: %d %d %d %d' and |
|
// uid string must be less than 5 chars (uint) |
|
if (uidStrings.size() == 5) |
|
uidString = uidStrings[1]; |
|
if (uidString.size() > 5) |
|
uidString.clear(); |
|
|
|
bool ok = false; |
|
int uid = uidString.toInt(&ok); |
|
if (ok) |
|
setUserId(uid); |
|
readUserName(); |
|
} |
|
else |
|
{ |
|
setFileError( statusInfo.error() ); |
|
return false; |
|
} |
|
|
|
|
|
// read process status file ( /proc/<pid/stat ) |
|
// |
|
// the expected file format is a list of fields separated by spaces, using |
|
// parenthesies to escape fields such as the process name which may itself contain |
|
// spaces: |
|
// |
|
// FIELD FIELD (FIELD WITH SPACES) FIELD FIELD |
|
// |
|
QFile processInfo( QString("/proc/%1/stat").arg(pid) ); |
|
if ( processInfo.open(QIODevice::ReadOnly) ) |
|
{ |
|
QTextStream stream(&processInfo); |
|
QString data = stream.readAll(); |
|
|
|
int stack = 0; |
|
int field = 0; |
|
int pos = 0; |
|
|
|
while (pos < data.count()) |
|
{ |
|
QChar c = data[pos]; |
|
|
|
if ( c == '(' ) |
|
stack++; |
|
else if ( c == ')' ) |
|
stack--; |
|
else if ( stack == 0 && c == ' ' ) |
|
field++; |
|
else |
|
{ |
|
switch ( field ) |
|
{ |
|
case PARENT_PID_FIELD: |
|
parentPidString.append(c); |
|
break; |
|
case PROCESS_NAME_FIELD: |
|
processNameString.append(c); |
|
break; |
|
case GROUP_PROCESS_FIELD: |
|
foregroundPidString.append(c); |
|
break; |
|
} |
|
} |
|
|
|
pos++; |
|
} |
|
} |
|
else |
|
{ |
|
setFileError( processInfo.error() ); |
|
return false; |
|
} |
|
|
|
// check that data was read successfully |
|
bool ok = false; |
|
int foregroundPid = foregroundPidString.toInt(&ok); |
|
if (ok) |
|
setForegroundPid(foregroundPid); |
|
|
|
int parentPid = parentPidString.toInt(&ok); |
|
if (ok) |
|
setParentPid(parentPid); |
|
|
|
if (!processNameString.isEmpty()) |
|
setName(processNameString); |
|
|
|
// update object state |
|
setPid(pid); |
|
|
|
return ok; |
|
} |
|
|
|
virtual bool readArguments(int pid) |
|
{ |
|
// read command-line arguments file found at /proc/<pid>/cmdline |
|
// the expected format is a list of strings delimited by null characters, |
|
// and ending in a double null character pair. |
|
|
|
QFile argumentsFile( QString("/proc/%1/cmdline").arg(pid) ); |
|
if ( argumentsFile.open(QIODevice::ReadOnly) ) |
|
{ |
|
QTextStream stream(&argumentsFile); |
|
QString data = stream.readAll(); |
|
|
|
QStringList argList = data.split( QChar('\0') ); |
|
|
|
foreach ( const QString &entry , argList ) |
|
{ |
|
if (!entry.isEmpty()) |
|
addArgument(entry); |
|
} |
|
} |
|
else |
|
{ |
|
setFileError( argumentsFile.error() ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
virtual bool readCurrentDir(int pid) |
|
{ |
|
QFileInfo info( QString("/proc/%1/cwd").arg(pid) ); |
|
|
|
const bool readable = info.isReadable(); |
|
|
|
if ( readable && info.isSymLink() ) |
|
{ |
|
setCurrentDir( info.symLinkTarget() ); |
|
return true; |
|
} |
|
else |
|
{ |
|
if ( !readable ) |
|
setError( PermissionsError ); |
|
else |
|
setError( UnknownError ); |
|
|
|
return false; |
|
} |
|
} |
|
|
|
virtual bool readEnvironment(int pid) |
|
{ |
|
// read environment bindings file found at /proc/<pid>/environ |
|
// the expected format is a list of KEY=VALUE strings delimited by null |
|
// characters and ending in a double null character pair. |
|
|
|
QFile environmentFile( QString("/proc/%1/environ").arg(pid) ); |
|
if ( environmentFile.open(QIODevice::ReadOnly) ) |
|
{ |
|
QTextStream stream(&environmentFile); |
|
QString data = stream.readAll(); |
|
|
|
QStringList bindingList = data.split( QChar('\0') ); |
|
|
|
foreach( const QString &entry , bindingList ) |
|
{ |
|
QString name; |
|
QString value; |
|
|
|
int splitPos = entry.indexOf('='); |
|
|
|
if ( splitPos != -1 ) |
|
{ |
|
name = entry.mid(0,splitPos); |
|
value = entry.mid(splitPos+1,-1); |
|
|
|
addEnvironmentBinding(name,value); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
setFileError( environmentFile.error() ); |
|
} |
|
|
|
return true; |
|
} |
|
} ; |
|
|
|
#if defined(Q_OS_FREEBSD) |
|
class FreeBSDProcessInfo : public UnixProcessInfo |
|
{ |
|
public: |
|
FreeBSDProcessInfo(int pid, bool readEnvironment) : |
|
UnixProcessInfo(pid, readEnvironment) |
|
{ |
|
} |
|
|
|
private: |
|
virtual bool readProcInfo(int pid) |
|
{ |
|
int managementInfoBase[4]; |
|
size_t mibLength; |
|
struct kinfo_proc* kInfoProc; |
|
|
|
managementInfoBase[0] = CTL_KERN; |
|
managementInfoBase[1] = KERN_PROC; |
|
managementInfoBase[2] = KERN_PROC_PID; |
|
managementInfoBase[3] = pid; |
|
|
|
if (sysctl(managementInfoBase, 4, NULL, &mibLength, NULL, 0) == -1) |
|
return false; |
|
|
|
kInfoProc = new struct kinfo_proc [mibLength]; |
|
|
|
if (sysctl(managementInfoBase, 4, kInfoProc, &mibLength, NULL, 0) == -1) |
|
{ |
|
delete [] kInfoProc; |
|
return false; |
|
} |
|
|
|
#if defined(__DragonFly__) |
|
setName(kInfoProc->kp_comm); |
|
setPid(kInfoProc->kp_pid); |
|
setParentPid(kInfoProc->kp_ppid); |
|
setForegroundPid(kInfoProc->kp_pgid); |
|
setUserId(kInfoProc->kp_uid); |
|
#else |
|
setName(kInfoProc->ki_comm); |
|
setPid(kInfoProc->ki_pid); |
|
setParentPid(kInfoProc->ki_ppid); |
|
setForegroundPid(kInfoProc->ki_pgid); |
|
setUserId(kInfoProc->ki_uid); |
|
#endif |
|
|
|
readUserName(); |
|
|
|
delete [] kInfoProc; |
|
return true; |
|
} |
|
|
|
virtual bool readArguments(int pid) |
|
{ |
|
char args[ARG_MAX]; |
|
int managementInfoBase[4]; |
|
size_t len; |
|
|
|
managementInfoBase[0] = CTL_KERN; |
|
managementInfoBase[1] = KERN_PROC; |
|
managementInfoBase[2] = KERN_PROC_PID; |
|
managementInfoBase[3] = pid; |
|
|
|
len = sizeof(args); |
|
if (sysctl(managementInfoBase, 4, args, &len, NULL, 0) == -1) |
|
return false; |
|
|
|
const QStringList argumentList = QString(args).split(QChar('\0')); |
|
|
|
for (QStringList::const_iterator it = argumentList.begin(); it != argumentList.end(); ++it) |
|
{ |
|
addArgument(*it); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
virtual bool readEnvironment(int pid) |
|
{ |
|
// Not supported in FreeBSD? |
|
return false; |
|
} |
|
|
|
virtual bool readCurrentDir(int pid) |
|
{ |
|
#if defined(__DragonFly__) |
|
// Not supported in DragonFly |
|
return false; |
|
#else |
|
int numrecords; |
|
struct kinfo_file* info = 0; |
|
|
|
info = kinfo_getfile(pid, &numrecords); |
|
|
|
if (!info) |
|
return false; |
|
|
|
for (int i = 0; i < numrecords; ++i) |
|
{ |
|
if (info[i].kf_fd == KF_FD_TYPE_CWD) |
|
{ |
|
setCurrentDir(info[i].kf_path); |
|
|
|
free(info); |
|
return true; |
|
} |
|
} |
|
|
|
free(info); |
|
return false; |
|
#endif |
|
} |
|
} ; |
|
#endif |
|
|
|
#if defined(Q_OS_MAC) |
|
class MacProcessInfo : public UnixProcessInfo |
|
{ |
|
public: |
|
MacProcessInfo(int pid, bool env) : |
|
UnixProcessInfo(pid, env) |
|
{ |
|
} |
|
|
|
private: |
|
virtual bool readProcInfo(int pid) |
|
{ |
|
int managementInfoBase[4]; |
|
size_t mibLength; |
|
struct kinfo_proc* kInfoProc; |
|
KDE_struct_stat statInfo; |
|
|
|
// Find the tty device of 'pid' (Example: /dev/ttys001) |
|
managementInfoBase[0] = CTL_KERN; |
|
managementInfoBase[1] = KERN_PROC; |
|
managementInfoBase[2] = KERN_PROC_PID; |
|
managementInfoBase[3] = pid; |
|
|
|
if (sysctl(managementInfoBase, 4, NULL, &mibLength, NULL, 0) == -1) |
|
{ |
|
return false; |
|
} |
|
else |
|
{ |
|
kInfoProc = new struct kinfo_proc [mibLength]; |
|
if (sysctl(managementInfoBase, 4, kInfoProc, &mibLength, NULL, 0) == -1) |
|
{ |
|
delete [] kInfoProc; |
|
return false; |
|
} |
|
else |
|
{ |
|
QString deviceNumber = QString(devname(((&kInfoProc->kp_eproc)->e_tdev), S_IFCHR)); |
|
QString fullDeviceName = QString("/dev/") + deviceNumber.rightJustified(3, '0'); |
|
delete [] kInfoProc; |
|
|
|
QByteArray deviceName = fullDeviceName.toLatin1(); |
|
const char* ttyName = deviceName.data(); |
|
|
|
if (KDE_stat(ttyName, &statInfo) != 0) |
|
return false; |
|
|
|
// Find all processes attached to ttyName |
|
managementInfoBase[0] = CTL_KERN; |
|
managementInfoBase[1] = KERN_PROC; |
|
managementInfoBase[2] = KERN_PROC_TTY; |
|
managementInfoBase[3] = statInfo.st_rdev; |
|
|
|
mibLength = 0; |
|
if (sysctl(managementInfoBase, sizeof(managementInfoBase)/sizeof(int), NULL, &mibLength, NULL, 0) == -1) |
|
return false; |
|
|
|
kInfoProc = new struct kinfo_proc [mibLength]; |
|
if (sysctl(managementInfoBase, sizeof(managementInfoBase)/sizeof(int), kInfoProc, &mibLength, NULL, 0) == -1) |
|
return false; |
|
|
|
// The foreground program is the first one |
|
setName(QString(kInfoProc->kp_proc.p_comm)); |
|
|
|
delete [] kInfoProc; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
virtual bool readArguments(int pid) |
|
{ |
|
Q_UNUSED(pid); |
|
return false; |
|
} |
|
virtual bool readCurrentDir(int pid) |
|
{ |
|
Q_UNUSED(pid); |
|
return false; |
|
} |
|
virtual bool readEnvironment(int pid) |
|
{ |
|
Q_UNUSED(pid); |
|
return false; |
|
} |
|
} ; |
|
#endif |
|
|
|
#ifdef Q_OS_SOLARIS |
|
// The procfs structure definition requires off_t to be |
|
// 32 bits, which only applies if FILE_OFFSET_BITS=32. |
|
// Futz around here to get it to compile regardless, |
|
// although some of the structure sizes might be wrong. |
|
// Fortunately, the structures we actually use don't use |
|
// off_t, and we're safe. |
|
#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS==64) |
|
#undef _FILE_OFFSET_BITS |
|
#endif |
|
#include <procfs.h> |
|
#else |
|
// On non-Solaris platforms, define a fake psinfo structure |
|
// so that the SolarisProcessInfo class can be compiled |
|
// |
|
// That avoids the trap where you change the API and |
|
// don't notice it in #ifdeffed platform-specific parts |
|
// of the code. |
|
struct psinfo { |
|
int pr_ppid; |
|
int pr_pgid; |
|
char* pr_fname; |
|
char* pr_psargs; |
|
} ; |
|
static const int PRARGSZ=1; |
|
#endif |
|
|
|
class SolarisProcessInfo : public UnixProcessInfo |
|
{ |
|
public: |
|
SolarisProcessInfo(int pid, bool readEnvironment) |
|
: UnixProcessInfo(pid,readEnvironment) |
|
{ |
|
} |
|
private: |
|
virtual bool readProcInfo(int pid) |
|
{ |
|
QFile psinfo( QString("/proc/%1/psinfo").arg(pid) ); |
|
if ( psinfo.open( QIODevice::ReadOnly ) ) |
|
{ |
|
struct psinfo info; |
|
if (psinfo.read((char *)&info,sizeof(info)) != sizeof(info)) |
|
{ |
|
return false; |
|
} |
|
|
|
setParentPid(info.pr_ppid); |
|
setForegroundPid(info.pr_pgid); |
|
setName(info.pr_fname); |
|
setPid(pid); |
|
|
|
// Bogus, because we're treating the arguments as one single string |
|
info.pr_psargs[PRARGSZ-1]=0; |
|
addArgument(info.pr_psargs); |
|
} |
|
return true; |
|
} |
|
|
|
virtual bool readArguments(int /*pid*/) |
|
{ |
|
// Handled in readProcInfo() |
|
return false; |
|
} |
|
|
|
virtual bool readEnvironment(int /*pid*/) |
|
{ |
|
// Not supported in Solaris |
|
return false; |
|
} |
|
|
|
virtual bool readCurrentDir(int pid) |
|
{ |
|
QFileInfo info( QString("/proc/%1/path/cwd").arg(pid) ); |
|
const bool readable = info.isReadable(); |
|
|
|
if ( readable && info.isSymLink() ) |
|
{ |
|
setCurrentDir( info.symLinkTarget() ); |
|
return true; |
|
} |
|
else |
|
{ |
|
if ( !readable ) |
|
setError( PermissionsError ); |
|
else |
|
setError( UnknownError ); |
|
|
|
return false; |
|
} |
|
} |
|
} ; |
|
|
|
SSHProcessInfo::SSHProcessInfo(const ProcessInfo& process) |
|
: _process(process) |
|
{ |
|
bool ok = false; |
|
|
|
// check that this is a SSH process |
|
const QString& name = _process.name(&ok); |
|
|
|
if ( !ok || name != "ssh" ) |
|
{ |
|
if ( !ok ) |
|
kWarning() << "Could not read process info"; |
|
else |
|
kWarning() << "Process is not a SSH process"; |
|
|
|
return; |
|
} |
|
|
|
// read arguments |
|
const QVector<QString>& args = _process.arguments(&ok); |
|
|
|
// SSH options |
|
// these are taken from the SSH manual ( accessed via 'man ssh' ) |
|
|
|
// options which take no arguments |
|
static const QString noOptionsArguments("1246AaCfgkMNnqsTtVvXxY"); |
|
// options which take one argument |
|
static const QString singleOptionArguments("bcDeFiLlmOopRSw"); |
|
|
|
if ( ok ) |
|
{ |
|
// find the username, host and command arguments |
|
// |
|
// the username/host is assumed to be the first argument |
|
// which is not an option |
|
// ( ie. does not start with a dash '-' character ) |
|
// or an argument to a previous option. |
|
// |
|
// the command, if specified, is assumed to be the argument following |
|
// the username and host |
|
// |
|
// note that we skip the argument at index 0 because that is the |
|
// program name ( expected to be 'ssh' in this case ) |
|
for ( int i = 1 ; i < args.count() ; i++ ) |
|
{ |
|
// if this argument is an option then skip it, plus any |
|
// following arguments which refer to this option |
|
if ( args[i].startsWith('-') ) |
|
{ |
|
QChar argChar = ( args[i].length() > 1 ) ? args[i][1] : '\0'; |
|
|
|
if ( noOptionsArguments.contains(argChar) ) |
|
continue; |
|
else if ( singleOptionArguments.contains(argChar) ) |
|
{ |
|
i++; |
|
continue; |
|
} |
|
} |
|
|
|
// check whether the host has been found yet |
|
// if not, this must be the username/host argument |
|
if ( _host.isEmpty() ) |
|
{ |
|
// check to see if only a hostname is specified, or whether |
|
// both a username and host are specified ( in which case they |
|
// are separated by an '@' character: username@host ) |
|
|
|
int separatorPosition = args[i].indexOf('@'); |
|
if ( separatorPosition != -1 ) |
|
{ |
|
// username and host specified |
|
_user = args[i].left(separatorPosition); |
|
_host = args[i].mid(separatorPosition+1); |
|
} |
|
else |
|
{ |
|
// just the host specified |
|
_host = args[i]; |
|
} |
|
} |
|
else |
|
{ |
|
// host has already been found, this must be the command argument |
|
_command = args[i]; |
|
} |
|
|
|
} |
|
} |
|
else |
|
{ |
|
kWarning() << "Could not read arguments"; |
|
|
|
return; |
|
} |
|
} |
|
|
|
QString SSHProcessInfo::userName() const |
|
{ |
|
return _user; |
|
} |
|
QString SSHProcessInfo::host() const |
|
{ |
|
return _host; |
|
} |
|
QString SSHProcessInfo::command() const |
|
{ |
|
return _command; |
|
} |
|
QString SSHProcessInfo::format(const QString& input) const |
|
{ |
|
QString output(input); |
|
|
|
// test whether host is an ip address |
|
// in which case 'short host' and 'full host' |
|
// markers in the input string are replaced with |
|
// the full address |
|
bool isIpAddress = false; |
|
|
|
struct in_addr address; |
|
if ( inet_aton(_host.toLocal8Bit().constData(),&address) != 0 ) |
|
isIpAddress = true; |
|
else |
|
isIpAddress = false; |
|
|
|
// search for and replace known markers |
|
output.replace("%u",_user); |
|
|
|
if ( isIpAddress ) |
|
output.replace("%h",_host); |
|
else |
|
output.replace("%h",_host.left(_host.indexOf('.'))); |
|
|
|
output.replace("%H",_host); |
|
output.replace("%c",_command); |
|
|
|
return output; |
|
} |
|
|
|
ProcessInfo* ProcessInfo::newInstance(int pid,bool enableEnvironmentRead) |
|
{ |
|
#ifdef Q_OS_LINUX |
|
return new LinuxProcessInfo(pid,enableEnvironmentRead); |
|
#elif defined(Q_OS_SOLARIS) |
|
return new SolarisProcessInfo(pid,enableEnvironmentRead); |
|
#elif defined(Q_OS_MAC) |
|
return new MacProcessInfo(pid,enableEnvironmentRead); |
|
#elif defined(Q_OS_FREEBSD) |
|
return new FreeBSDProcessInfo(pid,enableEnvironmentRead); |
|
#else |
|
return new NullProcessInfo(pid,enableEnvironmentRead); |
|
#endif |
|
} |
|
|
|
|