File indexing completed on 2025-01-19 04:23:28

0001 /*
0002     Copyright 2007-2008 by Robert Knight <robertknight@gmail.countm>
0003 
0004     This program is free software; you can redistribute it and/or modify
0005     it under the terms of the GNU General Public License as published by
0006     the Free Software Foundation; either version 2 of the License, or
0007     (at your option) any later version.
0008 
0009     This program is distributed in the hope that it will be useful,
0010     but WITHOUT ANY WARRANTY; without even the implied warranty of
0011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012     GNU General Public License for more details.
0013 
0014     You should have received a copy of the GNU General Public License
0015     along with this program; if not, write to the Free Software
0016     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0017     02110-1301  USA.
0018 */
0019 
0020 // Own
0021 #include "ProcessInfo.h"
0022 
0023 // Unix
0024 #include <sys/socket.h>
0025 #include <netinet/in.h>
0026 #include <arpa/inet.h>
0027 #include <unistd.h>
0028 #include <pwd.h>
0029 #include <sys/param.h>
0030 
0031 // Qt
0032 #include <QtCore/QDir>
0033 #include <QtCore/QFileInfo>
0034 #include <QtCore/QFlags>
0035 #include <QtCore/QTextStream>
0036 #include <QtCore/QStringList>
0037 #include <QtNetwork/QHostInfo>
0038 
0039 // KDE
0040 // #include <KConfigGroup>
0041 // #include <KSharedConfig>
0042 #include <QDebug>
0043 
0044 #if defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) || defined(Q_OS_MAC)
0045 #include <sys/sysctl.h>
0046 #endif
0047 
0048 #if defined(Q_OS_MAC)
0049 #include <libproc.h>
0050 #endif
0051 
0052 #if defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
0053 #include <sys/types.h>
0054 #include <sys/user.h>
0055 #include <sys/syslimits.h>
0056 #   if defined(Q_OS_FREEBSD)
0057 #   include <libutil.h>
0058 #   endif
0059 #endif
0060 
0061 using namespace Konsole;
0062 
0063 ProcessInfo::ProcessInfo(int aPid , bool enableEnvironmentRead)
0064     : _fields(ARGUMENTS | ENVIRONMENT)   // arguments and environments
0065     // are currently always valid,
0066     // they just return an empty
0067     // vector / map respectively
0068     // if no arguments
0069     // or environment bindings
0070     // have been explicitly set
0071     , _enableEnvironmentRead(enableEnvironmentRead)
0072     , _pid(aPid)
0073     , _parentPid(0)
0074     , _foregroundPid(0)
0075     , _userId(0)
0076     , _lastError(NoError)
0077     , _userName(QString())
0078     , _userHomeDir(QString())
0079 {
0080 }
0081 
0082 ProcessInfo::Error ProcessInfo::error() const
0083 {
0084     return _lastError;
0085 }
0086 void ProcessInfo::setError(Error error)
0087 {
0088     _lastError = error;
0089 }
0090 
0091 void ProcessInfo::update()
0092 {
0093     readProcessInfo(_pid, _enableEnvironmentRead);
0094 }
0095 
0096 QString ProcessInfo::validCurrentDir() const
0097 {
0098     bool ok = false;
0099 
0100     // read current dir, if an error occurs try the parent as the next
0101     // best option
0102     int currentPid = parentPid(&ok);
0103     QString dir = currentDir(&ok);
0104     while (!ok && currentPid != 0) {
0105         ProcessInfo* current = ProcessInfo::newInstance(currentPid);
0106         current->update();
0107         currentPid = current->parentPid(&ok);
0108         dir = current->currentDir(&ok);
0109         delete current;
0110     }
0111 
0112     return dir;
0113 }
0114 
0115 QString ProcessInfo::format(const QString& input) const
0116 {
0117     bool ok = false;
0118 
0119     QString output(input);
0120 
0121     // search for and replace known marker
0122     output.replace("%u", userName());
0123     output.replace("%h", localHost());
0124     output.replace("%n", name(&ok));
0125 
0126     QString dir = validCurrentDir();
0127     if (output.contains("%D")) {
0128         QString homeDir = userHomeDir();
0129         QString tempDir = dir;
0130         // Change User's Home Dir w/ ~ only at the beginning
0131         if (tempDir.startsWith(homeDir)) {
0132             tempDir.remove(0, homeDir.length());
0133             tempDir.prepend('~');
0134         }
0135         output.replace("%D", tempDir);
0136     }
0137     output.replace("%d", formatShortDir(dir));
0138 
0139     return output;
0140 }
0141 
0142 QSet<QString> ProcessInfo::_commonDirNames;
0143 
0144 QSet<QString> ProcessInfo::commonDirNames()
0145 {
0146     // static bool forTheFirstTime = true;
0147     //
0148     // if (forTheFirstTime) {
0149     //     const KSharedConfigPtr& config = KSharedConfig::openConfig();
0150     //     const KConfigGroup& configGroup = config->group("ProcessInfo");
0151     //     _commonDirNames = QSet<QString>::fromList(configGroup.readEntry("CommonDirNames", QStringList()));
0152     //
0153     //     forTheFirstTime = false;
0154     // }
0155 
0156     return _commonDirNames;
0157 }
0158 
0159 QString ProcessInfo::formatShortDir(const QString& input) const
0160 {
0161     QString result;
0162 
0163     const QStringList& parts = input.split(QDir::separator());
0164 
0165     QSet<QString> dirNamesToShorten = commonDirNames();
0166 
0167     QListIterator<QString> iter(parts);
0168     iter.toBack();
0169 
0170     // go backwards through the list of the path's parts
0171     // adding abbreviations of common directory names
0172     // and stopping when we reach a dir name which is not
0173     // in the commonDirNames set
0174     while (iter.hasPrevious()) {
0175         const QString& part = iter.previous();
0176 
0177         if (dirNamesToShorten.contains(part)) {
0178             result.prepend(QString(QDir::separator()) + part[0]);
0179         } else {
0180             result.prepend(part);
0181             break;
0182         }
0183     }
0184 
0185     return result;
0186 }
0187 
0188 QVector<QString> ProcessInfo::arguments(bool* ok) const
0189 {
0190     *ok = _fields.testFlag(ARGUMENTS);
0191 
0192     return _arguments;
0193 }
0194 
0195 QMap<QString, QString> ProcessInfo::environment(bool* ok) const
0196 {
0197     *ok = _fields.testFlag(ENVIRONMENT);
0198 
0199     return _environment;
0200 }
0201 
0202 bool ProcessInfo::isValid() const
0203 {
0204     return _fields.testFlag(PROCESS_ID);
0205 }
0206 
0207 int ProcessInfo::pid(bool* ok) const
0208 {
0209     *ok = _fields.testFlag(PROCESS_ID);
0210 
0211     return _pid;
0212 }
0213 
0214 int ProcessInfo::parentPid(bool* ok) const
0215 {
0216     *ok = _fields.testFlag(PARENT_PID);
0217 
0218     return _parentPid;
0219 }
0220 
0221 int ProcessInfo::foregroundPid(bool* ok) const
0222 {
0223     *ok = _fields.testFlag(FOREGROUND_PID);
0224 
0225     return _foregroundPid;
0226 }
0227 
0228 QString ProcessInfo::name(bool* ok) const
0229 {
0230     *ok = _fields.testFlag(NAME);
0231 
0232     return _name;
0233 }
0234 
0235 int ProcessInfo::userId(bool* ok) const
0236 {
0237     *ok = _fields.testFlag(UID);
0238 
0239     return _userId;
0240 }
0241 
0242 QString ProcessInfo::userName() const
0243 {
0244     return _userName;
0245 }
0246 
0247 QString ProcessInfo::userHomeDir() const
0248 {
0249     return _userHomeDir;
0250 }
0251 
0252 QString ProcessInfo::localHost()
0253 {
0254     return QHostInfo::localHostName();
0255 }
0256 
0257 void ProcessInfo::setPid(int aPid)
0258 {
0259     _pid = aPid;
0260     _fields |= PROCESS_ID;
0261 }
0262 
0263 void ProcessInfo::setUserId(int uid)
0264 {
0265     _userId = uid;
0266     _fields |= UID;
0267 }
0268 
0269 void ProcessInfo::setUserName(const QString& name)
0270 {
0271     _userName = name;
0272     setUserHomeDir();
0273 }
0274 
0275 void ProcessInfo::setUserHomeDir()
0276 {
0277     _userHomeDir = QDir::homePath();
0278 }
0279 
0280 void ProcessInfo::setParentPid(int aPid)
0281 {
0282     _parentPid = aPid;
0283     _fields |= PARENT_PID;
0284 }
0285 void ProcessInfo::setForegroundPid(int aPid)
0286 {
0287     _foregroundPid = aPid;
0288     _fields |= FOREGROUND_PID;
0289 }
0290 
0291 QString ProcessInfo::currentDir(bool* ok) const
0292 {
0293     if (ok)
0294         *ok = _fields & CURRENT_DIR;
0295 
0296     return _currentDir;
0297 }
0298 void ProcessInfo::setCurrentDir(const QString& dir)
0299 {
0300     _fields |= CURRENT_DIR;
0301     _currentDir = dir;
0302 }
0303 
0304 void ProcessInfo::setName(const QString& name)
0305 {
0306     _name = name;
0307     _fields |= NAME;
0308 }
0309 void ProcessInfo::addArgument(const QString& argument)
0310 {
0311     _arguments << argument;
0312 }
0313 
0314 void ProcessInfo::clearArguments()
0315 {
0316     _arguments.clear();
0317 }
0318 
0319 void ProcessInfo::addEnvironmentBinding(const QString& name , const QString& value)
0320 {
0321     _environment.insert(name, value);
0322 }
0323 
0324 void ProcessInfo::setFileError(QFile::FileError error)
0325 {
0326     switch (error) {
0327     case QFile::PermissionsError:
0328         setError(ProcessInfo::PermissionsError);
0329         break;
0330     case QFile::NoError:
0331         setError(ProcessInfo::NoError);
0332         break;
0333     default:
0334         setError(ProcessInfo::UnknownError);
0335     }
0336 }
0337 
0338 //
0339 // ProcessInfo::newInstance() is way at the bottom so it can see all of the
0340 // implementations of the UnixProcessInfo abstract class.
0341 //
0342 
0343 NullProcessInfo::NullProcessInfo(int aPid, bool enableEnvironmentRead)
0344     : ProcessInfo(aPid, enableEnvironmentRead)
0345 {
0346 }
0347 
0348 bool NullProcessInfo::readProcessInfo(int /*pid*/ , bool /*enableEnvironmentRead*/)
0349 {
0350     return false;
0351 }
0352 
0353 void NullProcessInfo::readUserName()
0354 {
0355 }
0356 
0357 #if !defined(Q_OS_WIN)
0358 UnixProcessInfo::UnixProcessInfo(int aPid, bool enableEnvironmentRead)
0359     : ProcessInfo(aPid, enableEnvironmentRead)
0360 {
0361 }
0362 
0363 bool UnixProcessInfo::readProcessInfo(int aPid , bool enableEnvironmentRead)
0364 {
0365     // prevent _arguments from growing longer and longer each time this
0366     // method is called.
0367     clearArguments();
0368 
0369     bool ok = readProcInfo(aPid);
0370     if (ok) {
0371         ok |= readArguments(aPid);
0372         ok |= readCurrentDir(aPid);
0373         if (enableEnvironmentRead) {
0374             ok |= readEnvironment(aPid);
0375         }
0376     }
0377     return ok;
0378 }
0379 
0380 void UnixProcessInfo::readUserName()
0381 {
0382     bool ok = false;
0383     const int uid = userId(&ok);
0384     if (!ok) return;
0385 
0386     struct passwd passwdStruct;
0387     struct passwd* getpwResult;
0388     char* getpwBuffer;
0389     long getpwBufferSize;
0390     int getpwStatus;
0391 
0392     getpwBufferSize = sysconf(_SC_GETPW_R_SIZE_MAX);
0393     if (getpwBufferSize == -1)
0394         getpwBufferSize = 16384;
0395 
0396     getpwBuffer = new char[getpwBufferSize];
0397     if (getpwBuffer == NULL)
0398         return;
0399     getpwStatus = getpwuid_r(uid, &passwdStruct, getpwBuffer, getpwBufferSize, &getpwResult);
0400     if ((getpwStatus == 0) && (getpwResult != NULL)) {
0401         setUserName(QString(passwdStruct.pw_name));
0402     } else {
0403         setUserName(QString());
0404         qWarning() << "getpwuid_r returned error : " << getpwStatus;
0405     }
0406     delete [] getpwBuffer;
0407 }
0408 #endif
0409 
0410 #if defined(Q_OS_LINUX)
0411 class LinuxProcessInfo : public UnixProcessInfo
0412 {
0413 public:
0414     LinuxProcessInfo(int aPid, bool env) :
0415         UnixProcessInfo(aPid, env) {
0416     }
0417 
0418 private:
0419     virtual bool readProcInfo(int aPid) {
0420         // indicies of various fields within the process status file which
0421         // contain various information about the process
0422         const int PARENT_PID_FIELD = 3;
0423         const int PROCESS_NAME_FIELD = 1;
0424         const int GROUP_PROCESS_FIELD = 7;
0425 
0426         QString parentPidString;
0427         QString processNameString;
0428         QString foregroundPidString;
0429         QString uidLine;
0430         QString uidString;
0431         QStringList uidStrings;
0432 
0433         // For user id read process status file ( /proc/<pid>/status )
0434         //  Can not use getuid() due to it does not work for 'su'
0435         QFile statusInfo(QString("/proc/%1/status").arg(aPid));
0436         if (statusInfo.open(QIODevice::ReadOnly)) {
0437             QTextStream stream(&statusInfo);
0438             QString statusLine;
0439             do {
0440                 statusLine = stream.readLine();
0441                 if (statusLine.startsWith(QLatin1String("Uid:")))
0442                     uidLine = statusLine;
0443             } while (!statusLine.isNull() && uidLine.isNull());
0444 
0445 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
0446       uidStrings << uidLine.split('\t', QString::SkipEmptyParts);
0447 #else
0448       uidStrings << uidLine.split('\t', Qt::SkipEmptyParts);
0449 #endif
0450             // Must be 5 entries: 'Uid: %d %d %d %d' and
0451             // uid string must be less than 5 chars (uint)
0452             if (uidStrings.size() == 5)
0453                 uidString = uidStrings[1];
0454             if (uidString.size() > 5)
0455                 uidString.clear();
0456 
0457             bool ok = false;
0458             const int uid = uidString.toInt(&ok);
0459             if (ok)
0460                 setUserId(uid);
0461             readUserName();
0462         } else {
0463             setFileError(statusInfo.error());
0464             return false;
0465         }
0466 
0467         // read process status file ( /proc/<pid/stat )
0468         //
0469         // the expected file format is a list of fields separated by spaces, using
0470         // parenthesies to escape fields such as the process name which may itself contain
0471         // spaces:
0472         //
0473         // FIELD FIELD (FIELD WITH SPACES) FIELD FIELD
0474         //
0475         QFile processInfo(QString("/proc/%1/stat").arg(aPid));
0476         if (processInfo.open(QIODevice::ReadOnly)) {
0477             QTextStream stream(&processInfo);
0478             const QString& data = stream.readAll();
0479 
0480             int stack = 0;
0481             int field = 0;
0482             int pos = 0;
0483 
0484             while (pos < data.count()) {
0485                 QChar c = data[pos];
0486 
0487                 if (c == '(') {
0488                     stack++;
0489                 } else if (c == ')') {
0490                     stack--;
0491                 } else if (stack == 0 && c == ' ') {
0492                     field++;
0493                 } else {
0494                     switch (field) {
0495                     case PARENT_PID_FIELD:
0496                         parentPidString.append(c);
0497                         break;
0498                     case PROCESS_NAME_FIELD:
0499                         processNameString.append(c);
0500                         break;
0501                     case GROUP_PROCESS_FIELD:
0502                         foregroundPidString.append(c);
0503                         break;
0504                     }
0505                 }
0506 
0507                 pos++;
0508             }
0509         } else {
0510             setFileError(processInfo.error());
0511             return false;
0512         }
0513 
0514         // check that data was read successfully
0515         bool ok = false;
0516         const int foregroundPid = foregroundPidString.toInt(&ok);
0517         if (ok)
0518             setForegroundPid(foregroundPid);
0519 
0520         const int parentPid = parentPidString.toInt(&ok);
0521         if (ok)
0522             setParentPid(parentPid);
0523 
0524         if (!processNameString.isEmpty())
0525             setName(processNameString);
0526 
0527         // update object state
0528         setPid(aPid);
0529 
0530         return ok;
0531     }
0532 
0533     virtual bool readArguments(int aPid) {
0534         // read command-line arguments file found at /proc/<pid>/cmdline
0535         // the expected format is a list of strings delimited by null characters,
0536         // and ending in a double null character pair.
0537 
0538         QFile argumentsFile(QString("/proc/%1/cmdline").arg(aPid));
0539         if (argumentsFile.open(QIODevice::ReadOnly)) {
0540             QTextStream stream(&argumentsFile);
0541             const QString& data = stream.readAll();
0542 
0543             const QStringList& argList = data.split(QChar('\0'));
0544 
0545             for(const QString & entry : argList) {
0546                 if (!entry.isEmpty())
0547                     addArgument(entry);
0548             }
0549         } else {
0550             setFileError(argumentsFile.error());
0551         }
0552 
0553         return true;
0554     }
0555 
0556     virtual bool readCurrentDir(int aPid) {
0557         char path_buffer[MAXPATHLEN + 1];
0558         path_buffer[MAXPATHLEN] = 0;
0559         QByteArray procCwd = QFile::encodeName(QString("/proc/%1/cwd").arg(aPid));
0560         const int length = readlink(procCwd.constData(), path_buffer, MAXPATHLEN);
0561         if (length == -1) {
0562             setError(UnknownError);
0563             return false;
0564         }
0565 
0566         path_buffer[length] = '\0';
0567         QString path = QFile::decodeName(path_buffer);
0568 
0569         setCurrentDir(path);
0570         return true;
0571     }
0572 
0573     virtual bool readEnvironment(int aPid) {
0574         // read environment bindings file found at /proc/<pid>/environ
0575         // the expected format is a list of KEY=VALUE strings delimited by null
0576         // characters and ending in a double null character pair.
0577 
0578         QFile environmentFile(QString("/proc/%1/environ").arg(aPid));
0579         if (environmentFile.open(QIODevice::ReadOnly)) {
0580             QTextStream stream(&environmentFile);
0581             const QString& data = stream.readAll();
0582 
0583             const QStringList& bindingList = data.split(QChar('\0'));
0584 
0585             for(const QString & entry : bindingList) {
0586                 QString name;
0587                 QString value;
0588 
0589                 const int splitPos = entry.indexOf('=');
0590 
0591                 if (splitPos != -1) {
0592                     name = entry.mid(0, splitPos);
0593                     value = entry.mid(splitPos + 1, -1);
0594 
0595                     addEnvironmentBinding(name, value);
0596                 }
0597             }
0598         } else {
0599             setFileError(environmentFile.error());
0600         }
0601 
0602         return true;
0603     }
0604 };
0605 
0606 #elif defined(Q_OS_FREEBSD)
0607 class FreeBSDProcessInfo : public UnixProcessInfo
0608 {
0609 public:
0610     FreeBSDProcessInfo(int aPid, bool readEnvironment) :
0611         UnixProcessInfo(aPid, readEnvironment) {
0612     }
0613 
0614 private:
0615     virtual bool readProcInfo(int aPid) {
0616         int managementInfoBase[4];
0617         size_t mibLength;
0618         struct kinfo_proc* kInfoProc;
0619 
0620         managementInfoBase[0] = CTL_KERN;
0621         managementInfoBase[1] = KERN_PROC;
0622         managementInfoBase[2] = KERN_PROC_PID;
0623         managementInfoBase[3] = aPid;
0624 
0625         if (sysctl(managementInfoBase, 4, NULL, &mibLength, NULL, 0) == -1)
0626             return false;
0627 
0628         kInfoProc = new struct kinfo_proc [mibLength];
0629 
0630         if (sysctl(managementInfoBase, 4, kInfoProc, &mibLength, NULL, 0) == -1) {
0631             delete [] kInfoProc;
0632             return false;
0633         }
0634 
0635 #if defined(HAVE_OS_DRAGONFLYBSD)
0636         setName(kInfoProc->kp_comm);
0637         setPid(kInfoProc->kp_pid);
0638         setParentPid(kInfoProc->kp_ppid);
0639         setForegroundPid(kInfoProc->kp_pgid);
0640         setUserId(kInfoProc->kp_uid);
0641 #else
0642         setName(kInfoProc->ki_comm);
0643         setPid(kInfoProc->ki_pid);
0644         setParentPid(kInfoProc->ki_ppid);
0645         setForegroundPid(kInfoProc->ki_pgid);
0646         setUserId(kInfoProc->ki_uid);
0647 #endif
0648 
0649         readUserName();
0650 
0651         delete [] kInfoProc;
0652         return true;
0653     }
0654 
0655     virtual bool readArguments(int aPid) {
0656         char args[ARG_MAX];
0657         int managementInfoBase[4];
0658         size_t len;
0659 
0660         managementInfoBase[0] = CTL_KERN;
0661         managementInfoBase[1] = KERN_PROC;
0662         managementInfoBase[2] = KERN_PROC_PID;
0663         managementInfoBase[3] = aPid;
0664 
0665         len = sizeof(args);
0666         if (sysctl(managementInfoBase, 4, args, &len, NULL, 0) == -1)
0667             return false;
0668 
0669         const QStringList& argumentList = QString(args).split(QChar('\0'));
0670 
0671         for (QStringList::const_iterator it = argumentList.begin(); it != argumentList.end(); ++it) {
0672             addArgument(*it);
0673         }
0674 
0675         return true;
0676     }
0677 
0678     virtual bool readEnvironment(int aPid) {
0679         Q_UNUSED(aPid);
0680         // Not supported in FreeBSD?
0681         return false;
0682     }
0683 
0684     virtual bool readCurrentDir(int aPid) {
0685 #if defined(HAVE_OS_DRAGONFLYBSD)
0686         char buf[PATH_MAX];
0687         int managementInfoBase[4];
0688         size_t len;
0689 
0690         managementInfoBase[0] = CTL_KERN;
0691         managementInfoBase[1] = KERN_PROC;
0692         managementInfoBase[2] = KERN_PROC_CWD;
0693         managementInfoBase[3] = aPid;
0694 
0695         len = sizeof(buf);
0696         if (sysctl(managementInfoBase, 4, buf, &len, NULL, 0) == -1)
0697             return false;
0698 
0699         setCurrentDir(buf);
0700 
0701         return true;
0702 #else
0703         int numrecords;
0704         struct kinfo_file* info = 0;
0705 
0706         info = kinfo_getfile(aPid, &numrecords);
0707 
0708         if (!info)
0709             return false;
0710 
0711         for (int i = 0; i < numrecords; ++i) {
0712             if (info[i].kf_fd == KF_FD_TYPE_CWD) {
0713                 setCurrentDir(info[i].kf_path);
0714 
0715                 free(info);
0716                 return true;
0717             }
0718         }
0719 
0720         free(info);
0721         return false;
0722 #endif
0723     }
0724 };
0725 
0726 #elif defined(Q_OS_OPENBSD)
0727 class OpenBSDProcessInfo : public UnixProcessInfo
0728 {
0729 public:
0730     OpenBSDProcessInfo(int aPid, bool readEnvironment) :
0731         UnixProcessInfo(aPid, readEnvironment) {
0732     }
0733 
0734 private:
0735     virtual bool readProcInfo(int aPid) {
0736         int      managementInfoBase[6];
0737         size_t   mibLength;
0738         struct kinfo_proc*  kInfoProc;
0739 
0740         managementInfoBase[0] = CTL_KERN;
0741         managementInfoBase[1] = KERN_PROC;
0742         managementInfoBase[2] = KERN_PROC_PID;
0743         managementInfoBase[3] = aPid;
0744         managementInfoBase[4] = sizeof(struct kinfo_proc);
0745         managementInfoBase[5] = 1;
0746 
0747         if (::sysctl(managementInfoBase, 6, NULL, &mibLength, NULL, 0) == -1) {
0748             qWarning() << "first sysctl() call failed with code" << errno;
0749             return false;
0750         }
0751 
0752         kInfoProc = (struct kinfo_proc*)malloc(mibLength);
0753 
0754         if (::sysctl(managementInfoBase, 6, kInfoProc, &mibLength, NULL, 0) == -1) {
0755             qWarning() << "second sysctl() call failed with code" << errno;
0756             free(kInfoProc);
0757             return false;
0758         }
0759 
0760         setName(kInfoProc->p_comm);
0761         setPid(kInfoProc->p_pid);
0762         setParentPid(kInfoProc->p_ppid);
0763         setForegroundPid(kInfoProc->p_tpgid);
0764         setUserId(kInfoProc->p_uid);
0765         setUserName(kInfoProc->p_login);
0766 
0767         free(kInfoProc);
0768         return true;
0769     }
0770 
0771     char** readProcArgs(int aPid, int whatMib) {
0772         void*   buf = NULL;
0773         void*   nbuf;
0774         size_t  len = 4096;
0775         int     rc = -1;
0776         int     managementInfoBase[4];
0777 
0778         managementInfoBase[0] = CTL_KERN;
0779         managementInfoBase[1] = KERN_PROC_ARGS;
0780         managementInfoBase[2] = aPid;
0781         managementInfoBase[3] = whatMib;
0782 
0783         do {
0784             len *= 2;
0785             nbuf = realloc(buf, len);
0786             if (nbuf == NULL) {
0787                 break;
0788             }
0789 
0790             buf = nbuf;
0791             rc = ::sysctl(managementInfoBase, 4, buf, &len, NULL, 0);
0792             qWarning() << "sysctl() call failed with code" << errno;
0793         } while (rc == -1 && errno == ENOMEM);
0794 
0795         if (nbuf == NULL || rc == -1) {
0796             free(buf);
0797             return NULL;
0798         }
0799 
0800         return (char**)buf;
0801     }
0802 
0803     virtual bool readArguments(int aPid) {
0804         char**  argv;
0805 
0806         argv = readProcArgs(aPid, KERN_PROC_ARGV);
0807         if (argv == NULL) {
0808             return false;
0809         }
0810 
0811         for (char** p = argv; *p != NULL; p++) {
0812             addArgument(QString(*p));
0813         }
0814         free(argv);
0815         return true;
0816     }
0817 
0818     virtual bool readEnvironment(int aPid) {
0819         char**  envp;
0820         char*   eqsign;
0821 
0822         envp = readProcArgs(aPid, KERN_PROC_ENV);
0823         if (envp == NULL) {
0824             return false;
0825         }
0826 
0827         for (char **p = envp; *p != NULL; p++) {
0828             eqsign = strchr(*p, '=');
0829             if (eqsign == NULL || eqsign[1] == '\0') {
0830                 continue;
0831             }
0832             *eqsign = '\0';
0833             addEnvironmentBinding(QString((const char *)p),
0834                 QString((const char *)eqsign + 1));
0835         }
0836         free(envp);
0837         return true;
0838     }
0839 
0840     virtual bool readCurrentDir(int aPid) {
0841         char    buf[PATH_MAX];
0842         int     managementInfoBase[3];
0843         size_t  len;
0844 
0845         managementInfoBase[0] = CTL_KERN;
0846         managementInfoBase[1] = KERN_PROC_CWD;
0847         managementInfoBase[2] = aPid;
0848 
0849         len = sizeof(buf);
0850         if (::sysctl(managementInfoBase, 3, buf, &len, NULL, 0) == -1) {
0851             qWarning() << "sysctl() call failed with code" << errno;
0852             return false;
0853         }
0854 
0855         setCurrentDir(buf);
0856         return true;
0857     }
0858 };
0859 
0860 #elif defined(Q_OS_MAC)
0861 class MacProcessInfo : public UnixProcessInfo
0862 {
0863 public:
0864     MacProcessInfo(int aPid, bool env) :
0865         UnixProcessInfo(aPid, env) {
0866     }
0867 
0868 private:
0869     virtual bool readProcInfo(int aPid) {
0870         int managementInfoBase[4];
0871         size_t mibLength;
0872         struct kinfo_proc* kInfoProc;
0873 /*
0874         KDE_struct_stat statInfo;
0875 
0876         // Find the tty device of 'pid' (Example: /dev/ttys001)
0877         managementInfoBase[0] = CTL_KERN;
0878         managementInfoBase[1] = KERN_PROC;
0879         managementInfoBase[2] = KERN_PROC_PID;
0880         managementInfoBase[3] = aPid;
0881 
0882         if (sysctl(managementInfoBase, 4, NULL, &mibLength, NULL, 0) == -1) {
0883             return false;
0884         } else {
0885             kInfoProc = new struct kinfo_proc [mibLength];
0886             if (sysctl(managementInfoBase, 4, kInfoProc, &mibLength, NULL, 0) == -1) {
0887                 delete [] kInfoProc;
0888                 return false;
0889             } else {
0890                 const QString deviceNumber = QString(devname(((&kInfoProc->kp_eproc)->e_tdev), S_IFCHR));
0891                 const QString fullDeviceName =  QString("/dev/") + deviceNumber.rightJustified(3, '0');
0892                 delete [] kInfoProc;
0893 
0894                 const QByteArray deviceName = fullDeviceName.toLatin1();
0895                 const char* ttyName = deviceName.data();
0896 
0897                 if (KDE::stat(ttyName, &statInfo) != 0)
0898                     return false;
0899 
0900                 // Find all processes attached to ttyName
0901                 managementInfoBase[0] = CTL_KERN;
0902                 managementInfoBase[1] = KERN_PROC;
0903                 managementInfoBase[2] = KERN_PROC_TTY;
0904                 managementInfoBase[3] = statInfo.st_rdev;
0905 
0906                 mibLength = 0;
0907                 if (sysctl(managementInfoBase, sizeof(managementInfoBase) / sizeof(int), NULL, &mibLength, NULL, 0) == -1)
0908                     return false;
0909 
0910                 kInfoProc = new struct kinfo_proc [mibLength];
0911                 if (sysctl(managementInfoBase, sizeof(managementInfoBase) / sizeof(int), kInfoProc, &mibLength, NULL, 0) == -1)
0912                     return false;
0913 
0914                 // The foreground program is the first one
0915                 setName(QString(kInfoProc->kp_proc.p_comm));
0916 
0917                 delete [] kInfoProc;
0918             }
0919             setPid(aPid);
0920         }
0921         return true;
0922 */
0923         return false;
0924     }
0925 
0926     virtual bool readArguments(int aPid) {
0927         Q_UNUSED(aPid);
0928         return false;
0929     }
0930     virtual bool readCurrentDir(int aPid) {
0931         struct proc_vnodepathinfo vpi;
0932         const int nb = proc_pidinfo(aPid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi));
0933         if (nb == sizeof(vpi)) {
0934             setCurrentDir(QString(vpi.pvi_cdir.vip_path));
0935             return true;
0936         }
0937         return false;
0938     }
0939     virtual bool readEnvironment(int aPid) {
0940         Q_UNUSED(aPid);
0941         return false;
0942     }
0943 };
0944 
0945 #elif defined(Q_OS_SOLARIS)
0946 // The procfs structure definition requires off_t to be
0947 // 32 bits, which only applies if FILE_OFFSET_BITS=32.
0948 // Futz around here to get it to compile regardless,
0949 // although some of the structure sizes might be wrong.
0950 // Fortunately, the structures we actually use don't use
0951 // off_t, and we're safe.
0952 #if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS==64)
0953 #undef _FILE_OFFSET_BITS
0954 #endif
0955 #include <procfs.h>
0956 
0957 class SolarisProcessInfo : public UnixProcessInfo
0958 {
0959 public:
0960     SolarisProcessInfo(int aPid, bool readEnvironment)
0961         : UnixProcessInfo(aPid, readEnvironment) {
0962     }
0963 private:
0964     virtual bool readProcInfo(int aPid) {
0965         QFile psinfo(QString("/proc/%1/psinfo").arg(aPid));
0966         if (psinfo.open(QIODevice::ReadOnly)) {
0967             struct psinfo info;
0968             if (psinfo.read((char *)&info, sizeof(info)) != sizeof(info)) {
0969                 return false;
0970             }
0971 
0972             setParentPid(info.pr_ppid);
0973             setForegroundPid(info.pr_pgid);
0974             setName(info.pr_fname);
0975             setPid(aPid);
0976 
0977             // Bogus, because we're treating the arguments as one single string
0978             info.pr_psargs[PRARGSZ - 1] = 0;
0979             addArgument(info.pr_psargs);
0980         }
0981         return true;
0982     }
0983 
0984     virtual bool readArguments(int /*pid*/) {
0985         // Handled in readProcInfo()
0986         return false;
0987     }
0988 
0989     virtual bool readEnvironment(int /*pid*/) {
0990         // Not supported in Solaris
0991         return false;
0992     }
0993 
0994     // FIXME: This will have the same issues as BKO 251351; the Linux
0995     // version uses readlink.
0996     virtual bool readCurrentDir(int aPid) {
0997         QFileInfo info(QString("/proc/%1/path/cwd").arg(aPid));
0998         const bool readable = info.isReadable();
0999 
1000         if (readable && info.isSymLink()) {
1001             setCurrentDir(info.symLinkTarget());
1002             return true;
1003         } else {
1004             if (!readable)
1005                 setError(PermissionsError);
1006             else
1007                 setError(UnknownError);
1008 
1009             return false;
1010         }
1011     }
1012 };
1013 #endif
1014 
1015 SSHProcessInfo::SSHProcessInfo(const ProcessInfo& process)
1016     : _process(process)
1017 {
1018     bool ok = false;
1019 
1020     // check that this is a SSH process
1021     const QString& name = _process.name(&ok);
1022 
1023     if (!ok || name != "ssh") {
1024         if (!ok)
1025             qWarning() << "Could not read process info";
1026         else
1027             qWarning() << "Process is not a SSH process";
1028 
1029         return;
1030     }
1031 
1032     // read arguments
1033     const QVector<QString>& args = _process.arguments(&ok);
1034 
1035     // SSH options
1036     // these are taken from the SSH manual ( accessed via 'man ssh' )
1037 
1038     // options which take no arguments
1039     static const QString noArgumentOptions("1246AaCfgKkMNnqsTtVvXxYy");
1040     // options which take one argument
1041     static const QString singleArgumentOptions("bcDeFIiLlmOopRSWw");
1042 
1043     if (ok) {
1044         // find the username, host and command arguments
1045         //
1046         // the username/host is assumed to be the first argument
1047         // which is not an option
1048         // ( ie. does not start with a dash '-' character )
1049         // or an argument to a previous option.
1050         //
1051         // the command, if specified, is assumed to be the argument following
1052         // the username and host
1053         //
1054         // note that we skip the argument at index 0 because that is the
1055         // program name ( expected to be 'ssh' in this case )
1056         for (int i = 1 ; i < args.count() ; i++) {
1057             // If this one is an option ...
1058             // Most options together with its argument will be skipped.
1059             if (args[i].startsWith('-')) {
1060                 const QChar optionChar = (args[i].length() > 1) ? args[i][1] : '\0';
1061                 // for example: -p2222 or -p 2222 ?
1062                 const bool optionArgumentCombined =  args[i].length() > 2;
1063 
1064                 if (noArgumentOptions.contains(optionChar)) {
1065                     continue;
1066                 } else if (singleArgumentOptions.contains(optionChar)) {
1067                     QString argument;
1068                     if (optionArgumentCombined) {
1069                         argument = args[i].mid(2);
1070                     } else {
1071                         // Verify correct # arguments are given
1072                         if ((i + 1) < args.count()) {
1073                             argument = args[i + 1];
1074                         }
1075                         i++;
1076                     }
1077 
1078                     // support using `-l user` to specify username.
1079                     if (optionChar == 'l')
1080                         _user = argument;
1081                     // support using `-p port` to specify port.
1082                     else if (optionChar == 'p')
1083                         _port = argument;
1084 
1085                     continue;
1086                 }
1087             }
1088 
1089             // check whether the host has been found yet
1090             // if not, this must be the username/host argument
1091             if (_host.isEmpty()) {
1092                 // check to see if only a hostname is specified, or whether
1093                 // both a username and host are specified ( in which case they
1094                 // are separated by an '@' character:  username@host )
1095 
1096                 const int separatorPosition = args[i].indexOf('@');
1097                 if (separatorPosition != -1) {
1098                     // username and host specified
1099                     _user = args[i].left(separatorPosition);
1100                     _host = args[i].mid(separatorPosition + 1);
1101                 } else {
1102                     // just the host specified
1103                     _host = args[i];
1104                 }
1105             } else {
1106                 // host has already been found, this must be the command argument
1107                 _command = args[i];
1108             }
1109         }
1110     } else {
1111         qWarning() << "Could not read arguments";
1112 
1113         return;
1114     }
1115 }
1116 
1117 QString SSHProcessInfo::userName() const
1118 {
1119     return _user;
1120 }
1121 QString SSHProcessInfo::host() const
1122 {
1123     return _host;
1124 }
1125 QString SSHProcessInfo::port() const
1126 {
1127     return _port;
1128 }
1129 QString SSHProcessInfo::command() const
1130 {
1131     return _command;
1132 }
1133 QString SSHProcessInfo::format(const QString& input) const
1134 {
1135     QString output(input);
1136 
1137     // test whether host is an ip address
1138     // in which case 'short host' and 'full host'
1139     // markers in the input string are replaced with
1140     // the full address
1141     bool isIpAddress = false;
1142 
1143     struct in_addr address;
1144     if (inet_aton(_host.toLocal8Bit().constData(), &address) != 0)
1145         isIpAddress = true;
1146     else
1147         isIpAddress = false;
1148 
1149     // search for and replace known markers
1150     output.replace("%u", _user);
1151 
1152     if (isIpAddress)
1153         output.replace("%h", _host);
1154     else
1155         output.replace("%h", _host.left(_host.indexOf('.')));
1156 
1157     output.replace("%H", _host);
1158     output.replace("%c", _command);
1159 
1160     return output;
1161 }
1162 
1163 ProcessInfo* ProcessInfo::newInstance(int aPid, bool enableEnvironmentRead)
1164 {
1165 #if defined(Q_OS_LINUX)
1166     return new LinuxProcessInfo(aPid, enableEnvironmentRead);
1167 #elif defined(Q_OS_SOLARIS)
1168     return new SolarisProcessInfo(aPid, enableEnvironmentRead);
1169 #elif defined(Q_OS_MAC)
1170     return new MacProcessInfo(aPid, enableEnvironmentRead);
1171 #elif defined(Q_OS_FREEBSD)
1172     return new FreeBSDProcessInfo(aPid, enableEnvironmentRead);
1173 #elif defined(Q_OS_OPENBSD)
1174     return new OpenBSDProcessInfo(aPid, enableEnvironmentRead);
1175 #else
1176     return new NullProcessInfo(aPid, enableEnvironmentRead);
1177 #endif
1178 }