File indexing completed on 2025-03-16 08:11:26
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 }