diff --git a/src/lib/util/kprocesslist.cpp b/src/lib/util/kprocesslist.cpp index 3246d88..934d871 100644 --- a/src/lib/util/kprocesslist.cpp +++ b/src/lib/util/kprocesslist.cpp @@ -1,116 +1,101 @@ /************************************************************************** ** ** This file is part of the KDE Frameworks ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** Copyright (c) 2019 David Hallas ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at info@qt.nokia.com. ** **************************************************************************/ #include "kprocesslist.h" #include "kprocesslist_p.h" -#include using namespace KProcessList; KProcessInfoPrivate::KProcessInfoPrivate() : valid(false), pid(-1) { } KProcessInfo::KProcessInfo() : d_ptr(new KProcessInfoPrivate) { } KProcessInfo::KProcessInfo(qint64 pid, const QString& command, const QString& user) : KProcessInfo(pid, command, command, user) {} KProcessInfo::KProcessInfo(qint64 pid, const QString& command, const QString &name, const QString& user) : d_ptr(new KProcessInfoPrivate) { d_ptr->valid = true; d_ptr->pid = pid; d_ptr->name = name; d_ptr->command = command; d_ptr->user = user; } KProcessInfo::KProcessInfo(const KProcessInfo &other) : d_ptr(new KProcessInfoPrivate) { *this = other; } KProcessInfo::~KProcessInfo() { } KProcessInfo &KProcessInfo::operator=(const KProcessInfo &other) { d_ptr = other.d_ptr; return *this; } bool KProcessInfo::isValid() const { return d_ptr->valid; } qint64 KProcessInfo::pid() const { return d_ptr->pid; } QString KProcessInfo::name() const { return d_ptr->name; } QString KProcessInfo::command() const { return d_ptr->command; } QString KProcessInfo::user() const { return d_ptr->user; } - -KProcessInfo KProcessList::processInfo(qint64 pid) -{ - KProcessInfoList processInfoList = KProcessList::processInfoList(); - auto testProcessIterator = std::find_if(processInfoList.begin(), processInfoList.end(), - [pid](const KProcessList::KProcessInfo& info) - { - return info.pid() == pid; - }); - if (testProcessIterator != processInfoList.end()) { - return *testProcessIterator; - } - return KProcessInfo(); -} diff --git a/src/lib/util/kprocesslist_unix.cpp b/src/lib/util/kprocesslist_unix.cpp index 7be21e4..30c5892 100644 --- a/src/lib/util/kprocesslist_unix.cpp +++ b/src/lib/util/kprocesslist_unix.cpp @@ -1,160 +1,173 @@ /************************************************************************** ** ** This file is part of the KDE Frameworks ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** Copyright (c) 2019 David Hallas ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at info@qt.nokia.com. ** **************************************************************************/ #include "kprocesslist.h" #include #include using namespace KProcessList; namespace { bool isUnixProcessId(const QString &procname) { for (int i = 0; i != procname.size(); ++i) { if (!procname.at(i).isDigit()) return false; } return true; } // Determine UNIX processes by running ps KProcessInfoList unixProcessListPS() { KProcessInfoList rc; QProcess psProcess; const QStringList args { QStringLiteral("-e"), QStringLiteral("-o"), #ifdef Q_OS_MAC // command goes last, otherwise it is cut off QStringLiteral("pid state user comm command"), #else QStringLiteral("pid,state,user,comm,cmd"), #endif }; psProcess.start(QStringLiteral("ps"), args); if (!psProcess.waitForStarted()) return rc; psProcess.waitForFinished(); QByteArray output = psProcess.readAllStandardOutput(); // Split "457 S+ /Users/foo.app" const QStringList lines = QString::fromLocal8Bit(output).split(QLatin1Char('\n')); const int lineCount = lines.size(); const QChar blank = QLatin1Char(' '); for (int l = 1; l < lineCount; l++) { // Skip header const QString line = lines.at(l).simplified(); // we can't just split on blank as the process name might // contain them const int endOfPid = line.indexOf(blank); const int endOfState = line.indexOf(blank, endOfPid+1); const int endOfUser = line.indexOf(blank, endOfState+1); const int endOfName = line.indexOf(blank, endOfUser+1); if (endOfPid >= 0 && endOfState >= 0 && endOfUser >= 0) { qint64 pid = line.leftRef(endOfPid).toUInt(); QString user = line.mid(endOfState+1, endOfUser-endOfState-1); QString name = line.mid(endOfUser+1, endOfName-endOfUser-1); QString command = line.right(line.size()-endOfName-1); rc.push_back(KProcessInfo(pid, command, name, user)); } } return rc; } +bool getProcessInfo(const QString& procId, KProcessInfo& processInfo) +{ + if (!isUnixProcessId(procId)) + return false; +#ifdef Q_OS_FREEBSD + QString statusFileName(QStringLiteral("/status")); +#else + QString statusFileName(QStringLiteral("/stat")); +#endif + QString filename = QStringLiteral("/proc/"); + filename += procId; + filename += statusFileName; + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) + return false; // process may have exited + + const QStringList data = QString::fromLocal8Bit(file.readAll()).split(QLatin1Char(' ')); + qint64 pid = procId.toUInt(); + QString name = data.at(1); + if (name.startsWith(QLatin1Char('(')) && name.endsWith(QLatin1Char(')'))) { + name.chop(1); + name.remove(0, 1); + } + // State is element 2 + // PPID is element 3 + QString user = QFileInfo(file).owner(); + file.close(); + + QString command = name; + + QFile cmdFile(QLatin1String("/proc/") + procId + QLatin1String("/cmdline")); + if (cmdFile.open(QFile::ReadOnly)) { + QByteArray cmd = cmdFile.readAll(); + + if (!cmd.isEmpty()) { + // extract non-truncated name from cmdline + int zeroIndex = cmd.indexOf('\0'); + int processNameStart = cmd.lastIndexOf('/', zeroIndex); + if (processNameStart == -1) { + processNameStart = 0; + } else { + processNameStart++; + } + name = QString::fromLocal8Bit(cmd.mid(processNameStart, zeroIndex - processNameStart)); + + cmd.replace('\0', ' '); + command = QString::fromLocal8Bit(cmd).trimmed(); + } + } + cmdFile.close(); + processInfo = KProcessInfo(pid, command, name, user); + return true; +} + } // unnamed namespace // Determine UNIX processes by reading "/proc". Default to ps if // it does not exist KProcessInfoList KProcessList::processInfoList() { const QDir procDir(QStringLiteral("/proc/")); -#ifdef Q_OS_FREEBSD - QString statusFileName(QStringLiteral("/status")); -#else - QString statusFileName(QStringLiteral("/stat")); -#endif if (!procDir.exists()) return unixProcessListPS(); KProcessInfoList rc; const QStringList procIds = procDir.entryList(); - if (procIds.isEmpty()) - return rc; for (const QString &procId : procIds) { - if (!isUnixProcessId(procId)) - continue; - QString filename = QStringLiteral("/proc/"); - filename += procId; - filename += statusFileName; - QFile file(filename); - if (!file.open(QIODevice::ReadOnly)) - continue; // process may have exited - - const QStringList data = QString::fromLocal8Bit(file.readAll()).split(QLatin1Char(' ')); - qint64 pid = procId.toUInt(); - QString name = data.at(1); - if (name.startsWith(QLatin1Char('(')) && name.endsWith(QLatin1Char(')'))) { - name.chop(1); - name.remove(0, 1); + KProcessInfo processInfo; + if (getProcessInfo(procId, processInfo)) { + rc.push_back(processInfo); } - // State is element 2 - // PPID is element 3 - QString user = QFileInfo(file).owner(); - file.close(); - - QString command = name; - - QFile cmdFile(QLatin1String("/proc/") + procId + QLatin1String("/cmdline")); - if (cmdFile.open(QFile::ReadOnly)) { - QByteArray cmd = cmdFile.readAll(); - - if (!cmd.isEmpty()) { - // extract non-truncated name from cmdline - int zeroIndex = cmd.indexOf('\0'); - int processNameStart = cmd.lastIndexOf('/', zeroIndex); - if (processNameStart == -1) { - processNameStart = 0; - } else { - processNameStart++; - } - name = QString::fromLocal8Bit(cmd.mid(processNameStart, zeroIndex - processNameStart)); - - cmd.replace('\0', ' '); - command = QString::fromLocal8Bit(cmd).trimmed(); - } - } - cmdFile.close(); - - rc.push_back(KProcessInfo(pid, command, name, user)); } return rc; } + +KProcessInfo KProcessList::processInfo(qint64 pid) +{ + KProcessInfo processInfo; + getProcessInfo(QString::number(pid), processInfo); + return processInfo; +} diff --git a/src/lib/util/kprocesslist_win.cpp b/src/lib/util/kprocesslist_win.cpp index 5de851a..f18d31b 100644 --- a/src/lib/util/kprocesslist_win.cpp +++ b/src/lib/util/kprocesslist_win.cpp @@ -1,134 +1,149 @@ /************************************************************************** ** ** This file is part of the KDE Frameworks ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** Copyright (c) 2019 David Hallas ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at info@qt.nokia.com. ** **************************************************************************/ #include "kprocesslist.h" #include +#include // Enable Win API of XP SP1 and later #ifdef Q_OS_WIN # if !defined(_WIN32_WINNT) # define _WIN32_WINNT 0x0502 # endif # include # if !defined(PROCESS_SUSPEND_RESUME) // Check flag for MinGW # define PROCESS_SUSPEND_RESUME (0x0800) # endif // PROCESS_SUSPEND_RESUME #endif // Q_OS_WIN #include #include using namespace KProcessList; // Resolve QueryFullProcessImageNameW out of kernel32.dll due // to incomplete MinGW import libs and it not being present // on Windows XP. static inline BOOL queryFullProcessImageName(HANDLE h, DWORD flags, LPWSTR buffer, DWORD *size) { // Resolve required symbols from the kernel32.dll typedef BOOL (WINAPI *QueryFullProcessImageNameWProtoType)(HANDLE, DWORD, LPWSTR, PDWORD); static QueryFullProcessImageNameWProtoType queryFullProcessImageNameW = 0; if (!queryFullProcessImageNameW) { QLibrary kernel32Lib(QLatin1String("kernel32.dll"), 0); if (kernel32Lib.isLoaded() || kernel32Lib.load()) { queryFullProcessImageNameW = (QueryFullProcessImageNameWProtoType)kernel32Lib.resolve( "QueryFullProcessImageNameW"); } } if (!queryFullProcessImageNameW) return FALSE; // Read out process return (*queryFullProcessImageNameW)(h, flags, buffer, size); } struct ProcessInfo { QString processOwner; }; static inline ProcessInfo winProcessInfo(DWORD processId) { ProcessInfo pi; HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION, TOKEN_READ, processId); if (handle == INVALID_HANDLE_VALUE) return pi; HANDLE processTokenHandle = NULL; if (!OpenProcessToken(handle, TOKEN_READ, &processTokenHandle) || !processTokenHandle) return pi; DWORD size = 0; GetTokenInformation(processTokenHandle, TokenUser, NULL, 0, &size); if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { QByteArray buf; buf.resize(size); PTOKEN_USER userToken = reinterpret_cast(buf.data()); if (userToken && GetTokenInformation(processTokenHandle, TokenUser, userToken, size, &size)) { SID_NAME_USE sidNameUse; TCHAR user[MAX_PATH] = { 0 }; DWORD userNameLength = MAX_PATH; TCHAR domain[MAX_PATH] = { 0 }; DWORD domainNameLength = MAX_PATH; if (LookupAccountSid(NULL, userToken->User.Sid, user, &userNameLength, domain, &domainNameLength, &sidNameUse)) pi.processOwner = QString::fromUtf16(reinterpret_cast(user)); } } CloseHandle(processTokenHandle); CloseHandle(handle); return pi; } KProcessInfoList KProcessList::processInfoList() { KProcessInfoList rc; PROCESSENTRY32 pe; pe.dwSize = sizeof(PROCESSENTRY32); HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot == INVALID_HANDLE_VALUE) return rc; for (bool hasNext = Process32First(snapshot, &pe); hasNext; hasNext = Process32Next(snapshot, &pe)) { const ProcessInfo processInf = winProcessInfo(pe.th32ProcessID); rc.push_back(KProcessInfo(pe.th32ProcessID, QString::fromUtf16(reinterpret_cast(pe.szExeFile)), processInf.processOwner)); } CloseHandle(snapshot); return rc; } + +KProcessInfo KProcessList::processInfo(qint64 pid) +{ + KProcessInfoList processInfoList = KProcessList::processInfoList(); + auto testProcessIterator = std::find_if(processInfoList.begin(), processInfoList.end(), + [pid](const KProcessList::KProcessInfo& info) + { + return info.pid() == pid; + }); + if (testProcessIterator != processInfoList.end()) { + return *testProcessIterator; + } + return KProcessInfo(); +}