File indexing completed on 2024-05-12 03:54:59
0001 /* 0002 This file is part of the KDE project 0003 0004 SPDX-FileCopyrightText: 2010 Jacopo De Simoi <wilderkde@gmail.com> 0005 SPDX-FileCopyrightText: 2014 Lukáš Tinkl <ltinkl@redhat.com> 0006 SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de> 0007 SPDX-FileCopyrightText: 2019 David Hallas <david@davidhallas.dk> 0008 0009 SPDX-License-Identifier: LGPL-2.0-only 0010 */ 0011 0012 #include "klistopenfilesjob.h" 0013 0014 #include <QDir> 0015 #include <QList> 0016 #include <QProcess> 0017 #include <QRegularExpression> 0018 #include <QStandardPaths> 0019 0020 class KListOpenFilesJobPrivate 0021 { 0022 public: 0023 KListOpenFilesJobPrivate(KListOpenFilesJob *Job, const QDir &Path) 0024 : job(Job) 0025 , path(Path) 0026 { 0027 QObject::connect(&lsofProcess, &QProcess::errorOccurred, job, [this](QProcess::ProcessError error) { 0028 lsofError(error); 0029 }); 0030 0031 QObject::connect(&lsofProcess, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), job, [this](int exitCode, QProcess::ExitStatus exitStatus) { 0032 lsofFinished(exitCode, exitStatus); 0033 }); 0034 } 0035 0036 void start() 0037 { 0038 if (!path.exists()) { 0039 emitResult(static_cast<int>(KListOpenFilesJob::Error::DoesNotExist), QObject::tr("Path %1 doesn't exist").arg(path.path())); 0040 return; 0041 } 0042 0043 const QString lsofExec = QStandardPaths::findExecutable(QStringLiteral("lsof")); 0044 if (lsofExec.isEmpty()) { 0045 const QString envPath = QString::fromLocal8Bit(qgetenv("PATH")); 0046 emitResult(static_cast<int>(KListOpenFilesJob::Error::InternalError), QObject::tr("Could not find lsof executable in PATH:").arg(envPath)); 0047 return; 0048 } 0049 0050 lsofProcess.start(lsofExec, {QStringLiteral("-t"), QStringLiteral("+d"), path.path()}); 0051 } 0052 0053 void lsofError(QProcess::ProcessError processError) 0054 { 0055 emitResult(static_cast<int>(KListOpenFilesJob::Error::InternalError), QObject::tr("Failed to execute `lsof'. Error code %1").arg(processError)); 0056 } 0057 0058 void lsofFinished(int, QProcess::ExitStatus); 0059 void emitResult(int error, const QString &errorText); 0060 0061 KListOpenFilesJob *job; 0062 const QDir path; 0063 bool hasEmittedResult = false; 0064 QProcess lsofProcess; 0065 0066 KProcessList::KProcessInfoList processInfoList; 0067 }; 0068 0069 static KProcessList::KProcessInfo findInfoForPid(qint64 pid) 0070 { 0071 #ifdef HAVE_PROCSTAT 0072 // If HAVE_PROCSTAT is defined, then we're on a BSD, and there is a KProcessList implementation 0073 // that efficiently lists all processes, but KProcessList::processInfo() is slow because 0074 // it recalculates the list-of-all-processes on each iteration. 0075 const auto allProcesses = KProcessList::processInfoList(); 0076 auto it = std::find_if(allProcesses.cbegin(), allProcesses.cend(), [pid](const KProcessList::KProcessInfo &info) { 0077 return info.pid() == pid; 0078 }); 0079 return it != allProcesses.cend() ? *it : KProcessList::KProcessInfo{}; 0080 #else 0081 // Presumably Linux: processInfo(pid) is fine because it goes 0082 // straight to /proc/<pid> for information. 0083 return KProcessList::processInfo(pid); 0084 #endif 0085 } 0086 0087 void KListOpenFilesJobPrivate::lsofFinished(int, QProcess::ExitStatus) 0088 { 0089 if (hasEmittedResult) { 0090 return; 0091 } 0092 const QString out(QString::fromLocal8Bit(lsofProcess.readAll())); 0093 0094 const QRegularExpression re(QStringLiteral("\\s+")); 0095 const QList<QStringView> pidList = QStringView(out).split(re, Qt::SkipEmptyParts); 0096 0097 for (const auto &pidStr : pidList) { 0098 const qint64 pid = pidStr.toLongLong(); 0099 if (pid) { 0100 processInfoList << findInfoForPid(pid); 0101 } 0102 } 0103 job->emitResult(); 0104 } 0105 0106 void KListOpenFilesJobPrivate::emitResult(int error, const QString &errorText) 0107 { 0108 if (hasEmittedResult) { 0109 return; 0110 } 0111 job->setError(error); 0112 job->setErrorText(errorText); 0113 job->emitResult(); 0114 hasEmittedResult = true; 0115 } 0116 0117 KListOpenFilesJob::KListOpenFilesJob(const QString &path) 0118 : d(new KListOpenFilesJobPrivate(this, path)) 0119 { 0120 } 0121 0122 KListOpenFilesJob::~KListOpenFilesJob() = default; 0123 0124 void KListOpenFilesJob::start() 0125 { 0126 d->start(); 0127 } 0128 0129 KProcessList::KProcessInfoList KListOpenFilesJob::processInfoList() const 0130 { 0131 return d->processInfoList; 0132 } 0133 0134 #include "moc_klistopenfilesjob.cpp"