diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -51,6 +51,7 @@ kdelibs4configmigratortest.cpp kprocesslisttest.cpp kfileutilstest.cpp + klistopenfilestest.cpp LINK_LIBRARIES Qt5::Test KF5::CoreAddons ) diff --git a/autotests/klistopenfilestest.h b/autotests/klistopenfilestest.h new file mode 100644 --- /dev/null +++ b/autotests/klistopenfilestest.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_H +#define KLISTOPENFILESTEST_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.cpp b/autotests/klistopenfilestest.cpp new file mode 100644 --- /dev/null +++ b/autotests/klistopenfilestest.cpp @@ -0,0 +1,93 @@ +/* + * 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.h" +#include "klistopenfiles.h" +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QDir) +Q_DECLARE_METATYPE(KProcessList::KProcessInfoList) + +QTEST_MAIN(KListOpenFilesTest) + +void KListOpenFilesTest::initTestCase() +{ + qRegisterMetaType("QDir"); + qRegisterMetaType("KProcessList::KProcessInfoList"); +} + +void KListOpenFilesTest::testOpenFiles() +{ + QDir path(QCoreApplication::applicationDirPath()); + auto job = KListOpenFiles::listProcessesWithOpenFiles(path); + QVERIFY(job); + QSignalSpy resultSignalSpy(job, SIGNAL(result(KJob*))); + QSignalSpy processInfoAvailableSpy(job, SIGNAL(processInfoAvailable(QDir, 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); + 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 = KListOpenFiles::listProcessesWithOpenFiles(tempDir.path()); + QVERIFY(job); + QSignalSpy resultSignalSpy(job, SIGNAL(result(KJob*))); + QSignalSpy processInfoAvailableSpy(job, SIGNAL(processInfoAvailable(QDir, 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() +{ + QTemporaryDir tempDir; + tempDir.remove(); + auto job = KListOpenFiles::listProcessesWithOpenFiles(tempDir.path()); + QVERIFY(job); + QSignalSpy resultSignalSpy(job, SIGNAL(result(KJob*))); + QSignalSpy processInfoAvailableSpy(job, SIGNAL(processInfoAvailable(QDir, const KProcessList::KProcessInfoList&))); + job->start(); + QCOMPARE(resultSignalSpy.count(), 1); + QCOMPARE(job->error(), KJob::UserDefinedError); + 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 @@ -89,6 +89,7 @@ util/kdelibs4configmigrator.cpp util/kformat.cpp util/kformatprivate.cpp + util/klistopenfiles.cpp util/kosrelease.cpp util/kprocesslist.cpp util/kshell.cpp @@ -249,6 +250,7 @@ text/ktexttohtml.h text/ktexttohtmlemoticonsinterface.h util/kformat.h + util/klistopenfiles.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,69 @@ +/* + * 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 + +namespace KListOpenFiles +{ + +class ListOpenFilesJobPrivate; + +class KCOREADDONS_EXPORT ListOpenFilesJob : public KJob +{ + Q_OBJECT +public: + explicit ListOpenFilesJob(QDir path); + ~ListOpenFilesJob() 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 + * @since 5.62 + */ + void processInfoAvailable(QDir path, const KProcessList::KProcessInfoList& processInfoList); +private: + friend class ListOpenFilesJobPrivate; + ListOpenFilesJobPrivate *d; +}; + +/** + * @brief Retrieves list of processes that has open files in path or any subdirectory of path. The function + * will collect all processes and emit ListOpenFilesJob::processInfoAvailable once it is done. + * @param path The path to list processes with open files from + * @since 5.62 + */ +KCOREADDONS_EXPORT ListOpenFilesJob *listProcessesWithOpenFiles(QDir path); + +} // KListOpenFiles namespace + +#endif // KLISTOPENFILES_H diff --git a/src/lib/util/klistopenfiles.cpp b/src/lib/util/klistopenfiles.cpp new file mode 100644 --- /dev/null +++ b/src/lib/util/klistopenfiles.cpp @@ -0,0 +1,100 @@ +/* + * 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 + +namespace KListOpenFiles { + +class ListOpenFilesJobPrivate : public QObject +{ + Q_OBJECT +public: + ListOpenFilesJobPrivate(ListOpenFilesJob *job, QDir path) + : QObject(job) + , job(job) + , path(path) + { + connect(&lsofProcess, &QProcess::errorOccurred, this, &ListOpenFilesJobPrivate::lsofError); + connect(&lsofProcess, QOverload::of(&QProcess::finished), this, &ListOpenFilesJobPrivate::lsofFinished); + } + void start() + { + if (!path.exists()) { + job->setError(KJob::UserDefinedError); + job->emitResult(); + return; + } + lsofProcess.start(QStringLiteral("lsof"), {QStringLiteral("-t"), QStringLiteral("+d"), path.path()}); + } +private Q_SLOTS: + void lsofError(QProcess::ProcessError) + { + job->setError(KJob::UserDefinedError); + 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, processInfoList); + job->emitResult(); + } +private: + ListOpenFilesJob *job; + const QDir path; + QProcess lsofProcess; +}; + +ListOpenFilesJob::ListOpenFilesJob(QDir path) + : d(new ListOpenFilesJobPrivate(this, path)) +{ +} + +ListOpenFilesJob::~ListOpenFilesJob() +{ +} + +void ListOpenFilesJob::start() +{ + d->start(); +} + +ListOpenFilesJob *listProcessesWithOpenFiles(QDir path) +{ + return new ListOpenFilesJob(path); +} + +} // namespace KListOpenFiles + +#include "klistopenfiles.moc"