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 }