diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index f251511..6889c8d 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -1,99 +1,100 @@ include(ECMAddTests) include(ConfigureChecks.cmake) #configure checks for QFileSystemWatcher find_package(Qt5Test ${REQUIRED_QT_VERSION} CONFIG QUIET) if(NOT Qt5Test_FOUND) message(STATUS "Qt5Test not found, autotests will not be built.") return() endif() if(NOT CMAKE_BUILD_TYPE MATCHES "[Dd]ebug$") set(ENABLE_BENCHMARKS 1) endif() configure_file(config-tests.h.in config-tests.h) macro(build_plugin pname) add_library(${pname} MODULE ${ARGN}) ecm_mark_as_test(${pname}) target_link_libraries(${pname} KF5::CoreAddons) endmacro() # Build some sample plugins build_plugin(jsonplugin jsonplugin.cpp) build_plugin(jsonplugin2 jsonplugin2.cpp) build_plugin(versionedplugin versionedplugin.cpp) build_plugin(unversionedplugin unversionedplugin.cpp) build_plugin(multiplugin multiplugin.cpp) build_plugin(alwaysunloadplugin alwaysunloadplugin.cpp) add_definitions( -DKDELIBS4CONFIGMIGRATOR_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) ecm_add_tests( kaboutdatatest.cpp kaboutdataapplicationdatatest.cpp kautosavefiletest.cpp kcompositejobtest.cpp kformattest.cpp kjobtest.cpp kosreleasetest.cpp kpluginfactorytest.cpp kpluginloadertest.cpp kpluginmetadatatest.cpp kprocesstest.cpp krandomtest.cpp kshareddatacachetest.cpp kshelltest.cpp kurlmimedatatest.cpp kstringhandlertest.cpp kusertest.cpp kdelibs4migrationtest.cpp kdelibs4configmigratortest.cpp + kprocesslisttest.cpp LINK_LIBRARIES Qt5::Test KF5::CoreAddons ) if(NOT CMAKE_CROSSCOMPILING) ecm_add_tests(desktoptojsontest.cpp LINK_LIBRARIES Qt5::Test KF5::CoreAddons) target_compile_definitions(desktoptojsontest PRIVATE DESKTOP_TO_JSON_EXE="$" ) endif() set(ktexttohtmltest_SRCS ktexttohtmltest.cpp ${CMAKE_SOURCE_DIR}/src/lib/text/ktexttohtml.cpp) ecm_add_test(${ktexttohtmltest_SRCS} TEST_NAME ktexttohtmltest LINK_LIBRARIES Qt5::Test) # include the binary dir in order to get kcoreaddons_export.h target_include_directories(ktexttohtmltest PRIVATE ${KCoreAddons_BINARY_DIR}/src/lib) # fake static linking to prevent the export macros on Windows from kicking in target_compile_definitions(ktexttohtmltest PRIVATE -DKCOREADDONS_STATIC_DEFINE=1) target_compile_definitions(kpluginloadertest PRIVATE JSONPLUGIN_FILE="$" VERSIONEDPLUGIN_FILE="$" UNVERSIONEDPLUGIN_FILE="$" MULTIPLUGIN_FILE="$" ALWAYSUNLOADPLUGIN_FILE="$" ) set(KDIRWATCH_BACKENDS_TO_TEST Stat)#Stat is always compiled if (HAVE_SYS_INOTIFY_H) list(APPEND KDIRWATCH_BACKENDS_TO_TEST INotify) endif() if (HAVE_FAM) list(APPEND KDIRWATCH_BACKENDS_TO_TEST Fam) endif() if (HAVE_QFILESYSTEMWATCHER) list(APPEND KDIRWATCH_BACKENDS_TO_TEST QFSWatch) endif() foreach(_backendName ${KDIRWATCH_BACKENDS_TO_TEST}) string(TOLOWER ${_backendName} _lowercaseBackendName) set(BACKEND_TEST_TARGET kdirwatch_${_lowercaseBackendName}_unittest) add_executable(${BACKEND_TEST_TARGET} kdirwatch_unittest.cpp) target_link_libraries(${BACKEND_TEST_TARGET} Qt5::Test KF5::CoreAddons) ecm_mark_as_test(${BACKEND_TEST_TARGET}) add_test(NAME ${BACKEND_TEST_TARGET} COMMAND ${BACKEND_TEST_TARGET}) target_compile_definitions(${BACKEND_TEST_TARGET} PUBLIC -DKDIRWATCH_TEST_METHOD=\"${_backendName}\") endforeach() diff --git a/autotests/kprocesslisttest.cpp b/autotests/kprocesslisttest.cpp new file mode 100644 index 0000000..d5741fa --- /dev/null +++ b/autotests/kprocesslisttest.cpp @@ -0,0 +1,94 @@ +/* + * This file is part of the KDE project + * Copyright (C) 2019 David Hallas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kprocesslisttest.h" +#include "kprocesslist.h" +#include "kuser.h" +#include +#include +#include + +namespace { + +QString getTestExeName() +{ + static QString testExeName = QCoreApplication::instance()->applicationFilePath().section(QLatin1Char('/'), -1); + return testExeName; +} + +} + +QTEST_MAIN(KProcessListTest) + +void KProcessListTest::testKProcessInfoConstructionAssignment() +{ + KProcessList::KProcessInfo processInfoDefaultConstructed; + QVERIFY(processInfoDefaultConstructed.isValid() == false); + const qint64 pid(42); + const QString name(QStringLiteral("/bin/some_exe")); + const QString user(QStringLiteral("some_user")); + KProcessList::KProcessInfo processInfo(pid, name, user); + QVERIFY(processInfo.isValid() == true); + QCOMPARE(processInfo.pid(), pid); + QCOMPARE(processInfo.name(), name); + QCOMPARE(processInfo.user(), user); + KProcessList::KProcessInfo processInfoCopy(processInfo); + QVERIFY(processInfoCopy.isValid() == true); + QCOMPARE(processInfoCopy.pid(), pid); + QCOMPARE(processInfoCopy.name(), name); + QCOMPARE(processInfoCopy.user(), user); + KProcessList::KProcessInfo processInfoAssignment; + processInfoAssignment = processInfo; + QVERIFY(processInfoAssignment.isValid() == true); + QCOMPARE(processInfoAssignment.pid(), pid); + QCOMPARE(processInfoAssignment.name(), name); + QCOMPARE(processInfoAssignment.user(), user); +} + +void KProcessListTest::testProcessInfoList() +{ + KProcessList::KProcessInfoList processInfoList = KProcessList::processInfoList(); + QVERIFY(processInfoList.empty() == false); + auto testProcessIterator = std::find_if(processInfoList.begin(), processInfoList.end(), [](const KProcessList::KProcessInfo& info) + { + return info.name().endsWith(QStringLiteral("/") + getTestExeName()); + }); + QVERIFY(testProcessIterator != processInfoList.end()); + const auto& processInfo = *testProcessIterator; + QVERIFY(processInfo.isValid() == true); + QVERIFY(processInfo.name().endsWith(QStringLiteral("/") + getTestExeName())); + QCOMPARE(processInfo.pid(), QCoreApplication::applicationPid()); + QCOMPARE(processInfo.user(), KUser().loginName()); +} + +void KProcessListTest::testProcessInfo() +{ + const qint64 testExePid = QCoreApplication::applicationPid(); + KProcessList::KProcessInfo processInfo = KProcessList::processInfo(testExePid); + QVERIFY(processInfo.isValid() == true); + QVERIFY(processInfo.name().endsWith(QStringLiteral("/") + getTestExeName())); + QCOMPARE(processInfo.pid(), testExePid); + QCOMPARE(processInfo.user(), KUser().loginName()); +} + +void KProcessListTest::testProcessInfoNotFound() +{ + KProcessList::KProcessInfo processInfo = KProcessList::processInfo(-1); + QVERIFY(processInfo.isValid() == false); +} diff --git a/autotests/kprocesslisttest.h b/autotests/kprocesslisttest.h new file mode 100644 index 0000000..326c0b0 --- /dev/null +++ b/autotests/kprocesslisttest.h @@ -0,0 +1,36 @@ +/* + * This file is part of the KDE project + * Copyright (C) 2019 David Hallas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KPROCESSLISTTEST_H +#define KPROCESSLISTTEST_H + +#include + +class KProcessListTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void testKProcessInfoConstructionAssignment(); + void testProcessInfoList(); + void testProcessInfo(); + void testProcessInfoNotFound(); +}; + +#endif diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index e867020..d6d3879 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -1,289 +1,294 @@ # Configure checks for the caching subdir include(CheckIncludeFiles) check_include_files("sys/types.h;sys/mman.h" HAVE_SYS_MMAN_H) configure_file(caching/config-caching.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-caching.h) include(CheckSymbolExists) check_symbol_exists("getgrouplist" "grp.h" HAVE_GETGROUPLIST) configure_file(util/config-getgrouplist.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-getgrouplist.h) set (KDE4_DEFAULT_HOME ".kde${_KDE4_DEFAULT_HOME_POSTFIX}" CACHE STRING "The default KDE home directory" ) configure_file(util/config-kde4home.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kde4home.h) set (ACCOUNTS_SERVICE_ICON_DIR "/var/lib/AccountsService/icons" CACHE STRING "Accounts Services icon storage directory") configure_file(util/config-accountsservice.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-accountsservice.h) ecm_create_qm_loader(kcoreaddons_QM_LOADER kcoreaddons5_qt) set(kcoreaddons_OPTIONAL_SRCS ) set(kcoreaddons_OPTIONAL_LIBS ) if (FAM_FOUND) include_directories(${FAM_INCLUDE_DIR}) set(kcoreaddons_OPTIONAL_LIBS ${kcoreaddons_OPTIONAL_LIBS} ${FAM_LIBRARIES}) endif () if (Inotify_FOUND) include_directories(${Inotify_INCLUDE_DIRS}) set(kcoreaddons_OPTIONAL_LIBS ${kcoreaddons_OPTIONAL_LIBS} ${Inotify_LIBRARIES}) endif () if(NOT WIN32) set(kcoreaddons_OPTIONAL_SRCS caching/kshareddatacache.cpp) set(kcoreaddons_OPTIONAL_LIBS ${kcoreaddons_OPTIONAL_LIBS} ${CMAKE_THREAD_LIBS_INIT}) set_source_files_properties(caching/kshareddatacache.cpp PROPERTIES COMPILE_FLAGS -fexceptions) else() set(kcoreaddons_OPTIONAL_SRCS caching/kshareddatacache_win.cpp ) endif() if (WIN32) set(kcoreaddons_OPTIONAL_SRCS ${kcoreaddons_OPTIONAL_SRCS} text/kmacroexpander_win.cpp + util/kprocesslist_win.cpp util/kshell_win.cpp util/kuser_win.cpp ) endif () if (UNIX) set(kcoreaddons_OPTIONAL_SRCS ${kcoreaddons_OPTIONAL_SRCS} text/kmacroexpander_unix.cpp + util/kprocesslist_unix.cpp util/kuser_unix.cpp util/kshell_unix.cpp ) endif () set(libkcoreaddons_SRCS kaboutdata.cpp kcoreaddons.cpp io/kautosavefile.cpp io/kdirwatch.cpp io/kfilesystemtype.cpp io/kmessage.cpp io/kprocess.cpp io/kbackup.cpp io/kurlmimedata.cpp jobs/kcompositejob.cpp jobs/kjob.cpp jobs/kjobtrackerinterface.cpp jobs/kjobuidelegate.cpp plugin/kpluginfactory.cpp plugin/kpluginloader.cpp plugin/kpluginmetadata.cpp plugin/desktopfileparser.cpp randomness/krandom.cpp randomness/krandomsequence.cpp text/kmacroexpander.cpp text/kstringhandler.cpp text/ktexttohtml.cpp util/kdelibs4migration.cpp util/kdelibs4configmigrator.cpp util/kformat.cpp util/kformatprivate.cpp util/kosrelease.cpp + util/kprocesslist.cpp util/kshell.cpp ${kcoreaddons_OPTIONAL_SRCS} ${kcoreaddons_QM_LOADER} ) set(kcoreaddons_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/../.. # for kcoreaddons_version.h ${CMAKE_CURRENT_SOURCE_DIR}/caching/ ${CMAKE_CURRENT_BINARY_DIR}/io/ ${CMAKE_CURRENT_SOURCE_DIR}/io/ ${CMAKE_CURRENT_SOURCE_DIR}/jobs/ ${CMAKE_CURRENT_SOURCE_DIR}/plugin/ ${CMAKE_CURRENT_SOURCE_DIR}/randomness/ ${CMAKE_CURRENT_SOURCE_DIR}/text/ ${CMAKE_CURRENT_SOURCE_DIR}/util/ ) ecm_qt_declare_logging_category(libkcoreaddons_SRCS HEADER kcoreaddons_debug.h IDENTIFIER KCOREADDONS_DEBUG CATEGORY_NAME org.kde.kcoreaddons) add_library(KF5CoreAddons ${libkcoreaddons_SRCS}) generate_export_header(KF5CoreAddons BASE_NAME KCoreAddons) add_library(KF5::CoreAddons ALIAS KF5CoreAddons) target_include_directories(KF5CoreAddons PUBLIC "$") target_link_libraries(KF5CoreAddons PUBLIC Qt5::Core PRIVATE ${kcoreaddons_OPTIONAL_LIBS} ) target_link_libraries(KF5CoreAddons PRIVATE ${CMAKE_THREAD_LIBS_INIT}) if(WIN32) target_link_libraries(KF5CoreAddons PRIVATE netapi32 userenv) endif() target_include_directories(KF5CoreAddons INTERFACE "$" ) target_compile_definitions(KF5CoreAddons INTERFACE "$") set_target_properties(KF5CoreAddons PROPERTIES VERSION ${KCOREADDONS_VERSION_STRING} SOVERSION ${KCOREADDONS_SOVERSION} EXPORT_NAME CoreAddons ) ecm_generate_headers(KCoreAddons_HEADERS HEADER_NAMES KAboutData KCoreAddons REQUIRED_HEADERS KCoreAddons_HEADERS ) ecm_generate_headers(KCoreAddons_HEADERS HEADER_NAMES KSharedDataCache RELATIVE caching REQUIRED_HEADERS KCoreAddons_HEADERS ) ecm_generate_headers(KCoreAddons_HEADERS HEADER_NAMES KAutoSaveFile KDirWatch KMessage KProcess KBackup KUrlMimeData KFileSystemType RELATIVE io REQUIRED_HEADERS KCoreAddons_HEADERS ) ecm_generate_headers(KCoreAddons_HEADERS HEADER_NAMES KCompositeJob KJob KJobTrackerInterface KJobUiDelegate RELATIVE jobs REQUIRED_HEADERS KCoreAddons_HEADERS ) ecm_generate_headers(KCoreAddons_HEADERS HEADER_NAMES KExportPlugin KPluginFactory KPluginLoader KPluginMetaData RELATIVE plugin REQUIRED_HEADERS KCoreAddons_HEADERS ) ecm_generate_headers(KCoreAddons_HEADERS HEADER_NAMES KRandom KRandomSequence RELATIVE randomness REQUIRED_HEADERS KCoreAddons_HEADERS ) ecm_generate_headers(KCoreAddons_HEADERS HEADER_NAMES KMacroExpander KStringHandler KTextToHTML KTextToHTMLEmoticonsInterface RELATIVE text REQUIRED_HEADERS KCoreAddons_HEADERS ) ecm_generate_headers(KCoreAddons_HEADERS HEADER_NAMES KFormat KOSRelease KUser KShell + KProcessList Kdelibs4Migration Kdelibs4ConfigMigrator RELATIVE util REQUIRED_HEADERS KCoreAddons_HEADERS ) find_package(PythonModuleGeneration) if (PythonModuleGeneration_FOUND) ecm_generate_python_binding( TARGET KF5::CoreAddons PYTHONNAMESPACE PyKF5 MODULENAME KCoreAddons RULES_FILE "${CMAKE_SOURCE_DIR}/cmake/rules_PyKF5.py" SIP_DEPENDS QtCore/QtCoremod.sip HEADERS kaboutdata.h kcoreaddons.h caching/kshareddatacache.h io/kautosavefile.h io/kdirwatch.h io/kmessage.h io/kprocess.h io/kbackup.h io/kurlmimedata.h io/kfilesystemtype.h jobs/kcompositejob.h jobs/kjob.h jobs/kjobtrackerinterface.h jobs/kjobuidelegate.h plugin/kexportplugin.h plugin/kpluginfactory.h plugin/kpluginloader.h plugin/kpluginmetadata.h randomness/krandom.h randomness/krandomsequence.h text/kmacroexpander.h text/kstringhandler.h text/ktexttohtml.h text/ktexttohtmlemoticonsinterface.h util/kformat.h util/kosrelease.h + util/kprocesslist.h util/kuser.h util/kshell.h util/kdelibs4migration.h util/kdelibs4configmigrator.h ) endif() install(TARGETS KF5CoreAddons EXPORT KF5CoreAddonsTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${KCoreAddons_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/kcoreaddons_export.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KCoreAddons COMPONENT Devel ) # Includes CMake code to install open-source license texts for KAboutData. add_subdirectory(licenses) if(BUILD_QCH) ecm_add_qch( KF5CoreAddons_QCH NAME KCoreAddons BASE_NAME KF5CoreAddons VERSION ${KF5_VERSION} ORG_DOMAIN org.kde SOURCES # using only public headers, to cover only public API ${KCoreAddons_HEADERS} MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md" LINK_QCHS Qt5Core_QCH BLANK_MACROS KCOREADDONS_EXPORT KCOREADDONS_DEPRECATED KCOREADDONS_DEPRECATED_EXPORT TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} COMPONENT Devel ) endif() include(ECMGeneratePriFile) ecm_generate_pri_file(BASE_NAME KCoreAddons LIB_NAME KF5CoreAddons DEPS "core" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KCoreAddons) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) diff --git a/src/lib/util/kprocesslist.cpp b/src/lib/util/kprocesslist.cpp new file mode 100644 index 0000000..16162be --- /dev/null +++ b/src/lib/util/kprocesslist.cpp @@ -0,0 +1,105 @@ +/************************************************************************** +** +** 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& name, const QString& user) : + d_ptr(new KProcessInfoPrivate) +{ + d_ptr->valid = true; + d_ptr->pid = pid; + d_ptr->name = name; + 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::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.h b/src/lib/util/kprocesslist.h new file mode 100644 index 0000000..327dc84 --- /dev/null +++ b/src/lib/util/kprocesslist.h @@ -0,0 +1,96 @@ +/************************************************************************** +** +** 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. +** +**************************************************************************/ + +#ifndef KPROCESSLIST_H +#define KPROCESSLIST_H + +#include +#include +#include +#include + +namespace KProcessList +{ + +class KProcessInfoPrivate; + +/** + * @brief Contains information about a process. This class is usually not used alone but rather returned by + * processInfoList and processInfo. To check if the data contained in this class is valid use the isValid method. + * @since 5.58 + */ +class KCOREADDONS_EXPORT KProcessInfo { +public: + KProcessInfo(); + KProcessInfo(qint64 pid, const QString &name, const QString &user); + KProcessInfo(const KProcessInfo &other); + ~KProcessInfo(); + KProcessInfo &operator=(const KProcessInfo &other); + /** + * @brief If the KProcessInfo contains valid information. If it returns true the pid, name and user function + * returns valid information, otherwise they return value is undefined. + */ + bool isValid() const; + /** + * @brief The pid of the process + */ + qint64 pid() const; + /** + * @brief The name of the process. The class will try to get the full path to the executable file for the process + * but if it is not available the name of the process will be used instead. + */ + QString name() const; + /** + * @brief The username the process is running under. + */ + QString user() const; +private: + QSharedDataPointer d_ptr; +}; + +using KProcessInfoList = QList; + +/** + * @brief Retrieves the list of currently active processes. + * @since 5.58 + */ +KCOREADDONS_EXPORT KProcessInfoList processInfoList(); + +/** + * @brief Retrieves process information for a specific process-id. If the process is not found a KProcessInfo with + * isValid == false will be returned. + * @param pid The process-id to retrieve information for. + * @since 5.58 + */ +KCOREADDONS_EXPORT KProcessInfo processInfo(qint64 pid); + +} // KProcessList namespace + +#endif // KPROCESSLIST_H diff --git a/src/lib/util/kprocesslist_p.h b/src/lib/util/kprocesslist_p.h new file mode 100644 index 0000000..678bf46 --- /dev/null +++ b/src/lib/util/kprocesslist_p.h @@ -0,0 +1,52 @@ +/************************************************************************** +** +** 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. +** +**************************************************************************/ + +#ifndef KPROCESSLIST_P_H +#define KPROCESSLIST_P_H + +#include +#include "kprocesslist.h" + +namespace KProcessList +{ + +class KProcessInfoPrivate : public QSharedData { +public: + KProcessInfoPrivate(); + + bool valid; + qint64 pid; + QString name; + QString user; +}; + +} // KProcessList namespace + +#endif // KPROCESSLIST_P_H diff --git a/src/lib/util/kprocesslist_unix.cpp b/src/lib/util/kprocesslist_unix.cpp new file mode 100644 index 0000000..aeb5a87 --- /dev/null +++ b/src/lib/util/kprocesslist_unix.cpp @@ -0,0 +1,140 @@ +/************************************************************************** +** +** 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() +{ +#ifdef Q_OS_MAC + // command goes last, otherwise it is cut off + static const char formatC[] = "pid state user command"; +#else + static const char formatC[] = "pid,state,user,cmd"; +#endif + KProcessInfoList rc; + QProcess psProcess; + QStringList args; + args << QStringLiteral("-e") << QStringLiteral("-o") << QLatin1String(formatC); + 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); + if (endOfPid >= 0 && endOfState >= 0 && endOfUser >= 0) { + qint64 pid = line.left(endOfPid).toUInt(); + QString user = line.mid(endOfState+1, endOfUser-endOfState-1); + QString name = line.right(line.size()-endOfUser-1); + rc.push_back(KProcessInfo(pid, name, user)); + } + } + + return rc; +} + +} // 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.truncate(name.size() - 1); + name.remove(0, 1); + } + // State is element 2 + // PPID is element 3 + QString user = QFileInfo(file).owner(); + file.close(); + + QFile cmdFile(QLatin1String("/proc/") + procId + QLatin1String("/cmdline")); + if (cmdFile.open(QFile::ReadOnly)) { + QByteArray cmd = cmdFile.readAll(); + cmd.replace('\0', ' '); + if (!cmd.isEmpty()) + name = QString::fromLocal8Bit(cmd).trimmed(); + } + cmdFile.close(); + rc.push_back(KProcessInfo(pid, name, user)); + } + return rc; +} diff --git a/src/lib/util/kprocesslist_win.cpp b/src/lib/util/kprocesslist_win.cpp new file mode 100644 index 0000000..10915c6 --- /dev/null +++ b/src/lib/util/kprocesslist_win.cpp @@ -0,0 +1,132 @@ +/************************************************************************** +** +** 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 + +// 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 + +// 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 processInfo(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; +} + +KProcessList GetProcessList() +{ + KProcessList 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 = processInfo(pe.th32ProcessID); + rc.push_back(KProcessInfo(pe.th32ProcessID, QString::fromUtf16(reinterpret_cast(pe.szExeFile)), processInf.processOwner)); + } + CloseHandle(snapshot); + return rc; +}