diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -29,6 +29,20 @@ add_definitions( -DKDELIBS4CONFIGMIGRATOR_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) +if (WIN32) + set(autotests_OPTIONAL_SRCS + ${autotests_OPTIONAL_SRCS} + klistopenfilesjobtest_win.cpp + ) +endif () + +if (UNIX) + set(autotests_OPTIONAL_SRCS + ${autotests_OPTIONAL_SRCS} + klistopenfilesjobtest_unix.cpp + ) +endif () + ecm_add_tests( kaboutdatatest.cpp kaboutdataapplicationdatatest.cpp @@ -51,6 +65,7 @@ kdelibs4configmigratortest.cpp kprocesslisttest.cpp kfileutilstest.cpp + ${autotests_OPTIONAL_SRCS} LINK_LIBRARIES Qt5::Test KF5::CoreAddons ) diff --git a/autotests/klistopenfilesjobtest_unix.h b/autotests/klistopenfilesjobtest_unix.h new file mode 100644 --- /dev/null +++ b/autotests/klistopenfilesjobtest_unix.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 KLISTOPENFILESJOBTEST_UNIX_H +#define KLISTOPENFILESJOBTEST_UNIX_H + +#include + +class KListOpenFilesJobTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void testOpenFiles(); + void testNoOpenFiles(); + void testNonExistingDir(); + void testLsofNotFound(); +}; + +#endif diff --git a/autotests/klistopenfilesjobtest_unix.cpp b/autotests/klistopenfilesjobtest_unix.cpp new file mode 100644 --- /dev/null +++ b/autotests/klistopenfilesjobtest_unix.cpp @@ -0,0 +1,101 @@ +/* + * 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 "klistopenfilesjobtest_unix.h" +#include "klistopenfilesjob.h" +#include +#include +#include +#include +#include + +QTEST_MAIN(KListOpenFilesJobTest) + +void KListOpenFilesJobTest::testOpenFiles() +{ + QDir path(QCoreApplication::applicationDirPath()); + auto job = new KListOpenFilesJob(path.path()); + QVERIFY(job); + job->exec(); + QCOMPARE(job->error(), KJob::NoError); + QVERIFY(!job->processInfoList().empty()); + auto testProcessIterator = std::find_if(job->processInfoList().begin(), job->processInfoList().end(), + [](const KProcessList::KProcessInfo& info) + { + return info.pid() == QCoreApplication::applicationPid(); + }); + QVERIFY(testProcessIterator != job->processInfoList().end()); + const auto& processInfo = *testProcessIterator; + QVERIFY(processInfo.isValid()); + QCOMPARE(processInfo.pid(), QCoreApplication::applicationPid()); +} + +void KListOpenFilesJobTest::testNoOpenFiles() +{ + QTemporaryDir tempDir; + auto job = new KListOpenFilesJob(tempDir.path()); + QVERIFY(job); + job->exec(); + QCOMPARE(job->error(), KJob::NoError); + QVERIFY(job->processInfoList().empty()); +} + +void KListOpenFilesJobTest::testNonExistingDir() +{ + QString nonExistingDir(QStringLiteral("/does/not/exist")); + auto job = new KListOpenFilesJob(nonExistingDir); + QVERIFY(job); + job->exec(); + QCOMPARE(job->error(), static_cast(KListOpenFilesJob::Error::DoesNotExist)); + QCOMPARE(job->errorText(), QStringLiteral("Path %1 doesn't exist").arg(nonExistingDir)); + QVERIFY(job->processInfoList().empty()); +} + +/** + * @brief Helper class to temporarily set an environment variable and reset it on destruction + */ +class ScopedEnvVariable +{ +public: + ScopedEnvVariable(const QLatin1String& name, const QByteArray& newValue) + : name_(name) + , originalValue_(qgetenv(name_.latin1())) + { + setenv(name_.latin1(), newValue.data(), 1); + } + ~ScopedEnvVariable() + { + setenv(name_.latin1(), originalValue_.data(), 1); + } +private: + const QLatin1String name_; + const QByteArray originalValue_; +}; + +void KListOpenFilesJobTest::testLsofNotFound() +{ + // This test relies on clearing the PATH variable so that lsof is not found + ScopedEnvVariable emptyPathEnvironment(QLatin1String("PATH"), QByteArray()); + QDir path(QCoreApplication::applicationDirPath()); + auto job = new KListOpenFilesJob(path.path()); + QVERIFY(job); + job->exec(); + QCOMPARE(job->error(), static_cast(KListOpenFilesJob::Error::InternalError)); + QVERIFY(job->processInfoList().empty()); +} diff --git a/autotests/klistopenfilesjobtest_win.h b/autotests/klistopenfilesjobtest_win.h new file mode 100644 --- /dev/null +++ b/autotests/klistopenfilesjobtest_win.h @@ -0,0 +1,33 @@ +/* + * 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 KLISTOPENFILESJOBTEST_WIN_H +#define KLISTOPENFILESJOBTEST_WIN_H + +#include + +class KListOpenFilesJobTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void testNotSupported(); +}; + +#endif diff --git a/autotests/klistopenfilesjobtest_win.cpp b/autotests/klistopenfilesjobtest_win.cpp new file mode 100644 --- /dev/null +++ b/autotests/klistopenfilesjobtest_win.cpp @@ -0,0 +1,37 @@ +/* + * 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 "klistopenfilesjobtest_win.h" +#include "klistopenfilesjob.h" +#include +#include +#include + +QTEST_MAIN(KListOpenFilesJobTest) + +void KListOpenFilesJobTest::testNotSupported() +{ + QDir path(QCoreApplication::applicationDirPath()); + auto job = new KListOpenFilesJob(path.path()); + QVERIFY(job); + job->exec(); + QCOMPARE(job->error(), static_cast(KListOpenFilesJob::Error::NotSupported)); + QCOMPARE(job->errorText(), QStringLiteral("KListOpenFilesJob is not supported on Windows")); + QVERIFY(job->processInfoList().empty()); +} diff --git a/autotests/klistopenfilestest_unix.h b/autotests/klistopenfilestest_unix.h new file mode 100644 --- /dev/null +++ b/autotests/klistopenfilestest_unix.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 KLISTOPENFILESTEST_UNIX_H +#define KLISTOPENFILESTEST_UNIX_H + +#include + +class KListOpenFilesTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void testOpenFiles(); + void testNoOpenFiles(); + void testNonExistingDir(); +}; + +#endif diff --git a/autotests/klistopenfilestest_unix.cpp b/autotests/klistopenfilestest_unix.cpp new file mode 100644 --- /dev/null +++ b/autotests/klistopenfilestest_unix.cpp @@ -0,0 +1,92 @@ +/* + * 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 "klistopenfilestest_unix.h" +#include "klistopenfiles.h" +#include +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(KProcessList::KProcessInfoList) + +QTEST_MAIN(KListOpenFilesTest) + +void KListOpenFilesTest::initTestCase() +{ + qRegisterMetaType("KProcessList::KProcessInfoList"); +} + +void KListOpenFilesTest::testOpenFiles() +{ + QDir path(QCoreApplication::applicationDirPath()); + auto job = new KListOpenFilesJob(path.path()); + QVERIFY(job); + QSignalSpy resultSignalSpy(job, SIGNAL(result(KJob*))); + QSignalSpy processInfoAvailableSpy(job, SIGNAL(processInfoAvailable(QString, const KProcessList::KProcessInfoList&))); + job->start(); + QVERIFY(resultSignalSpy.wait()); + QCOMPARE(resultSignalSpy.count(), 1); + QCOMPARE(job->error(), KJob::NoError); + QCOMPARE(processInfoAvailableSpy.count(), 1); + QCOMPARE(processInfoAvailableSpy.at(0).at(0).value(), path.path()); + const KProcessList::KProcessInfoList processInfoList = processInfoAvailableSpy.at(0).at(1).value(); + QVERIFY(!processInfoList.empty()); + auto testProcessIterator = std::find_if(processInfoList.begin(), processInfoList.end(), [](const KProcessList::KProcessInfo& info) + { + return info.pid() == QCoreApplication::applicationPid(); + }); + QVERIFY(testProcessIterator != processInfoList.end()); + const auto& processInfo = *testProcessIterator; + QVERIFY(processInfo.isValid()); + QCOMPARE(processInfo.pid(), QCoreApplication::applicationPid()); +} + +void KListOpenFilesTest::testNoOpenFiles() +{ + QTemporaryDir tempDir; + auto job = new KListOpenFilesJob(tempDir.path()); + QVERIFY(job); + QSignalSpy resultSignalSpy(job, SIGNAL(result(KJob*))); + QSignalSpy processInfoAvailableSpy(job, SIGNAL(processInfoAvailable(QString, const KProcessList::KProcessInfoList&))); + job->start(); + QVERIFY(resultSignalSpy.wait()); + QCOMPARE(resultSignalSpy.count(), 1); + QCOMPARE(job->error(), KJob::NoError); + QCOMPARE(processInfoAvailableSpy.count(), 1); + QCOMPARE(processInfoAvailableSpy.at(0).at(0).value(), tempDir.path()); + KProcessList::KProcessInfoList processInfoList = processInfoAvailableSpy.at(0).at(1).value(); + QVERIFY(processInfoList.empty()); +} + +void KListOpenFilesTest::testNonExistingDir() +{ + QString nonExistingDir(QStringLiteral("/does/not/exist")); + auto job = new KListOpenFilesJob(nonExistingDir); + QVERIFY(job); + QSignalSpy resultSignalSpy(job, SIGNAL(result(KJob*))); + QSignalSpy processInfoAvailableSpy(job, SIGNAL(processInfoAvailable(QString, const KProcessList::KProcessInfoList&))); + job->start(); + QCOMPARE(resultSignalSpy.count(), 1); + QCOMPARE(job->error(), static_cast(KListOpenFilesJob::Error::DoesNotExist)); + QCOMPARE(job->errorText(), QStringLiteral("Path %1 doesn't exist").arg(nonExistingDir)); + QCOMPARE(processInfoAvailableSpy.count(), 0); +} diff --git a/autotests/klistopenfilestest_win.h b/autotests/klistopenfilestest_win.h new file mode 100644 --- /dev/null +++ b/autotests/klistopenfilestest_win.h @@ -0,0 +1,34 @@ +/* + * 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 KLISTOPENFILESTEST_WIN_H +#define KLISTOPENFILESTEST_WIN_H + +#include + +class KListOpenFilesTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void testNotSupported(); +}; + +#endif diff --git a/autotests/klistopenfilestest_win.cpp b/autotests/klistopenfilestest_win.cpp new file mode 100644 --- /dev/null +++ b/autotests/klistopenfilestest_win.cpp @@ -0,0 +1,49 @@ +/* + * 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 "klistopenfilestest_win.h" +#include "klistopenfiles.h" +#include +#include +#include +#include + +Q_DECLARE_METATYPE(KProcessList::KProcessInfoList) + +QTEST_MAIN(KListOpenFilesTest) + +void KListOpenFilesTest::initTestCase() +{ + qRegisterMetaType("KProcessList::KProcessInfoList"); +} + +void KListOpenFilesTest::testNotSupported() +{ + QDir path(QCoreApplication::applicationDirPath()); + auto job = new KListOpenFilesJob(path.path()); + QVERIFY(job); + QSignalSpy resultSignalSpy(job, SIGNAL(result(KJob*))); + QSignalSpy processInfoAvailableSpy(job, SIGNAL(processInfoAvailable(QString, const KProcessList::KProcessInfoList&))); + job->start(); + QVERIFY(resultSignalSpy.wait()); + QCOMPARE(resultSignalSpy.count(), 1); + QCOMPARE(job->error(), static_cast(KListOpenFilesJob::Error::NotSupported)); + QCOMPARE(job->errorText(), QStringLiteral("KListOpenFilesJob is not supported on Windows")); + QCOMPARE(processInfoAvailableSpy.count(), 0); +} diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -45,6 +45,7 @@ set(kcoreaddons_OPTIONAL_SRCS ${kcoreaddons_OPTIONAL_SRCS} text/kmacroexpander_win.cpp + util/klistopenfilesjob_win.cpp util/kprocesslist_win.cpp util/kshell_win.cpp util/kuser_win.cpp @@ -55,6 +56,7 @@ set(kcoreaddons_OPTIONAL_SRCS ${kcoreaddons_OPTIONAL_SRCS} text/kmacroexpander_unix.cpp + util/klistopenfilesjob_unix.cpp util/kprocesslist_unix.cpp util/kuser_unix.cpp util/kshell_unix.cpp @@ -249,6 +251,7 @@ text/ktexttohtml.h text/ktexttohtmlemoticonsinterface.h util/kformat.h + util/klistopenfilesjob.h util/kosrelease.h util/kprocesslist.h util/kuser.h diff --git a/src/lib/util/klistopenfiles.h b/src/lib/util/klistopenfiles.h new file mode 100644 --- /dev/null +++ b/src/lib/util/klistopenfiles.h @@ -0,0 +1,85 @@ +/* + * This file is part of the KDE project + * Copyright (C) 2010 by Jacopo De Simoi + * Copyright (C) 2014 by Lukáš Tinkl + * Copyright (C) 2016 by Kai Uwe Broulik + * 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 KLISTOPENFILES_H +#define KLISTOPENFILES_H + +#include +#include +#include +#include +#include +#include + +class KListOpenFilesJobPrivate; + + +/** + * @brief Provides information about processes that has open files in a given path or subdirectory of path. + * + * Provides information about processes that has open files in a given path or subdirectory of path. This class + * should be created by invoking the listProcessesWithOpenFiles function. + * + * When start() is invoked it starts to collect information about processes that has any files open in path or a + * subdirectory of path. When it is done collecting it emits the processInfoAvaiable signal. + * + * On Unix like systems the lsof utility is used to get the list of processes. + * On Windows the listing always fails with error code NotSupported. + * + * @since 5.62 + */ +class KCOREADDONS_EXPORT KListOpenFilesJob : public KJob +{ + Q_OBJECT +public: + explicit KListOpenFilesJob(const QString &path); + ~KListOpenFilesJob() override; + void start() override; + +Q_SIGNALS: + + /** + * @brief Emitted when process info has been collected. + * @param path The path that was requested process info for + * @param processInfoList The list of processes with open files in path + */ + void processInfoAvailable(QString path, const KProcessList::KProcessInfoList& processInfoList); +public: + /** + * @brief Special error codes emitted by KListOpenFilesJob + * + * The KListOpenFilesJob uses the error codes defined here besides the standard error codes defined by KJob + */ + enum class Error { + /*** Indicates that the platform doesn't support listing open files by processes */ + NotSupported = KJob::UserDefinedError + 1, + /*** Internal error has ocurred */ + InternalError = KJob::UserDefinedError + 2, + /*** The specified path does not exist */ + DoesNotExist = KJob::UserDefinedError + 11, + }; +private: + friend class KListOpenFilesJobPrivate; + QScopedPointer d; +}; + +#endif // KLISTOPENFILES_H diff --git a/src/lib/util/klistopenfiles_unix.cpp b/src/lib/util/klistopenfiles_unix.cpp new file mode 100644 --- /dev/null +++ b/src/lib/util/klistopenfiles_unix.cpp @@ -0,0 +1,94 @@ +/* + * This file is part of the KDE project + * Copyright (C) 2010 by Jacopo De Simoi + * Copyright (C) 2014 by Lukáš Tinkl + * Copyright (C) 2016 by Kai Uwe Broulik + * 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 "klistopenfiles.h" +#include +#include + +class KListOpenFilesJobPrivate +{ +public: + KListOpenFilesJobPrivate(KListOpenFilesJob *job, QDir path) + : job(job) + , path(path) + { + QObject::connect(&lsofProcess, &QProcess::errorOccurred, [this](QProcess::ProcessError error) { + lsofError(error); + }); + QObject::connect(&lsofProcess, QOverload::of(&QProcess::finished), + [this](int exitCode, QProcess::ExitStatus exitStatus) { + lsofFinished(exitCode, exitStatus); + }); + } + void start() + { + if (!path.exists()) { + job->setError(static_cast(KListOpenFilesJob::Error::DoesNotExist)); + job->setErrorText(QObject::tr("Path %1 doesn't exist").arg(path.path())); + job->emitResult(); + return; + } + lsofProcess.start(QStringLiteral("lsof"), {QStringLiteral("-t"), QStringLiteral("+d"), path.path()}); + } +private: + void lsofError(QProcess::ProcessError processError) + { + job->setError(static_cast(KListOpenFilesJob::Error::InternalError)); + job->setErrorText(QObject::tr("Failed to execute `lsof' error code %1").arg(processError)); + job->emitResult(); + } + void lsofFinished(int, QProcess::ExitStatus) + { + QStringList blockApps; + QString out(QString::fromLocal8Bit(lsofProcess.readAll())); + QStringList pidList = out.split(QRegExp(QStringLiteral("\\s+")), QString::SkipEmptyParts); + pidList.removeDuplicates(); + KProcessList::KProcessInfoList processInfoList; + for (const auto& pidStr : qAsConst(pidList)) { + qint64 pid = pidStr.toLongLong(); + if (!pid) { + continue; + } + processInfoList << KProcessList::processInfo(pid); + } + emit job->processInfoAvailable(path.path(), processInfoList); + job->emitResult(); + } +private: + KListOpenFilesJob *job; + const QDir path; + QProcess lsofProcess; +}; + +KListOpenFilesJob::KListOpenFilesJob(const QString& path) + : d(new KListOpenFilesJobPrivate(this, path)) +{ +} + +KListOpenFilesJob::~KListOpenFilesJob() = default; + +void KListOpenFilesJob::start() +{ + d->start(); +} + +#include "moc_klistopenfiles.cpp" diff --git a/src/lib/util/klistopenfiles_win.cpp b/src/lib/util/klistopenfiles_win.cpp new file mode 100644 --- /dev/null +++ b/src/lib/util/klistopenfiles_win.cpp @@ -0,0 +1,43 @@ +/* + * 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 "klistopenfiles.h" +#include + +class KListOpenFilesJobPrivate +{ +}; + +KListOpenFilesJob::KListOpenFilesJob(const QString&) + : d(nullptr) +{ +} + +KListOpenFilesJob::~KListOpenFilesJob() = default; + +void KListOpenFilesJob::start() +{ + QTimer::singleShot(0, [this](){ + setError(static_cast(KListOpenFilesJob::Error::NotSupported)); + setErrorText(QObject::tr("KListOpenFilesJob is not supported on Windows")); + emitResult(); + }); +} + +#include "moc_klistopenfiles.cpp" diff --git a/src/lib/util/klistopenfilesjob.h b/src/lib/util/klistopenfilesjob.h new file mode 100644 --- /dev/null +++ b/src/lib/util/klistopenfilesjob.h @@ -0,0 +1,78 @@ +/* + * This file is part of the KDE project + * Copyright (C) 2010 by Jacopo De Simoi + * Copyright (C) 2014 by Lukáš Tinkl + * Copyright (C) 2016 by Kai Uwe Broulik + * 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 KLISTOPENFILESJOB_H +#define KLISTOPENFILESJOB_H + +#include +#include +#include +#include +#include +#include + +class KListOpenFilesJobPrivate; + + +/** + * @brief Provides information about processes that has open files in a given path or subdirectory of path. + * + * Provides information about processes that has open files in a given path or subdirectory of path. + * + * When start() is invoked it starts to collect information about processes that has any files open in path or a + * subdirectory of path. When it is done the KJob::result signal is emitted and the result can be retrieved with the + * processInfoList function. + * + * On Unix like systems the lsof utility is used to get the list of processes. + * On Windows the listing always fails with error code NotSupported. + * + * @since 5.62 + */ +class KCOREADDONS_EXPORT KListOpenFilesJob : public KJob +{ + Q_OBJECT +public: + explicit KListOpenFilesJob(const QString &path); + ~KListOpenFilesJob() override; + void start() override; + const KProcessList::KProcessInfoList& processInfoList() const; + +public: + /** + * @brief Special error codes emitted by KListOpenFilesJob + * + * The KListOpenFilesJob uses the error codes defined here besides the standard error codes defined by KJob + */ + enum class Error { + /*** Indicates that the platform doesn't support listing open files by processes */ + NotSupported = KJob::UserDefinedError + 1, + /*** Internal error has ocurred */ + InternalError = KJob::UserDefinedError + 2, + /*** The specified path does not exist */ + DoesNotExist = KJob::UserDefinedError + 11, + }; +private: + friend class KListOpenFilesJobPrivate; + QScopedPointer d; +}; + +#endif // KLISTOPENFILESJOB_H diff --git a/src/lib/util/klistopenfilesjob_unix.cpp b/src/lib/util/klistopenfilesjob_unix.cpp new file mode 100644 --- /dev/null +++ b/src/lib/util/klistopenfilesjob_unix.cpp @@ -0,0 +1,115 @@ +/* + * This file is part of the KDE project + * Copyright (C) 2010 by Jacopo De Simoi + * Copyright (C) 2014 by Lukáš Tinkl + * Copyright (C) 2016 by Kai Uwe Broulik + * 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 "klistopenfilesjob.h" +#include +#include + +class KListOpenFilesJobPrivate +{ +public: + KListOpenFilesJobPrivate(KListOpenFilesJob *job, QDir path) + : job(job) + , path(path) + , hasEmittedResult_(false) + { + QObject::connect(&lsofProcess, &QProcess::errorOccurred, [this](QProcess::ProcessError error) { + lsofError(error); + }); + QObject::connect(&lsofProcess, QOverload::of(&QProcess::finished), + [this](int exitCode, QProcess::ExitStatus exitStatus) { + lsofFinished(exitCode, exitStatus); + }); + } + void start() + { + if (!path.exists()) { + emitResult(static_cast(KListOpenFilesJob::Error::DoesNotExist), + QObject::tr("Path %1 doesn't exist").arg(path.path())); + return; + } + lsofProcess.start(QStringLiteral("lsof"), {QStringLiteral("-t"), QStringLiteral("+d"), path.path()}); + } + const KProcessList::KProcessInfoList& processInfoList() const + { + return processInfoList_; + } +private: + void lsofError(QProcess::ProcessError processError) + { + emitResult(static_cast(KListOpenFilesJob::Error::InternalError), + QObject::tr("Failed to execute `lsof' error code %1").arg(processError)); + } + void lsofFinished(int, QProcess::ExitStatus) + { + if (hasEmittedResult_) { + return; + } + QStringList blockApps; + const QString out(QString::fromLocal8Bit(lsofProcess.readAll())); + QStringList pidList = out.split(QRegExp(QStringLiteral("\\s+")), QString::SkipEmptyParts); + pidList.removeDuplicates(); + for (const auto& pidStr : qAsConst(pidList)) { + qint64 pid = pidStr.toLongLong(); + if (!pid) { + continue; + } + processInfoList_ << KProcessList::processInfo(pid); + } + job->emitResult(); + } + void emitResult(int error, const QString& errorText) + { + if (hasEmittedResult_) { + return; + } + job->setError(error); + job->setErrorText(errorText); + job->emitResult(); + hasEmittedResult_ = true; + } +private: + KListOpenFilesJob *job; + const QDir path; + bool hasEmittedResult_; + QProcess lsofProcess; + KProcessList::KProcessInfoList processInfoList_; +}; + +KListOpenFilesJob::KListOpenFilesJob(const QString& path) + : d(new KListOpenFilesJobPrivate(this, path)) +{ +} + +KListOpenFilesJob::~KListOpenFilesJob() = default; + +void KListOpenFilesJob::start() +{ + d->start(); +} + +const KProcessList::KProcessInfoList& KListOpenFilesJob::processInfoList() const +{ + return d->processInfoList(); +} + +#include "moc_klistopenfilesjob.cpp" diff --git a/src/lib/util/klistopenfilesjob_win.cpp b/src/lib/util/klistopenfilesjob_win.cpp new file mode 100644 --- /dev/null +++ b/src/lib/util/klistopenfilesjob_win.cpp @@ -0,0 +1,50 @@ +/* + * 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 "klistopenfilesjob.h" +#include + +class KListOpenFilesJobPrivate +{ +public: + KProcessList::KProcessInfoList processInfoList; +}; + +KListOpenFilesJob::KListOpenFilesJob(const QString&) + : d(new KListOpenFilesJobPrivate) +{ +} + +KListOpenFilesJob::~KListOpenFilesJob() = default; + +void KListOpenFilesJob::start() +{ + QTimer::singleShot(0, [this](){ + setError(static_cast(KListOpenFilesJob::Error::NotSupported)); + setErrorText(QObject::tr("KListOpenFilesJob is not supported on Windows")); + emitResult(); + }); +} + +const KProcessList::KProcessInfoList& KListOpenFilesJob::processInfoList() const +{ + return d->processInfoList; +} + +#include "moc_klistopenfilesjob.cpp"