File indexing completed on 2024-05-19 05:28:17

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