KCoreAddons

klistopenfilesjob_unix.cpp
1 /*
2  This file is part of the KDE project
3 
4  SPDX-FileCopyrightText: 2010 Jacopo De Simoi <[email protected]>
5  SPDX-FileCopyrightText: 2014 Lukáš Tinkl <[email protected]>
6  SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <[email protected]>
7  SPDX-FileCopyrightText: 2019 David Hallas <[email protected]>
8 
9  SPDX-License-Identifier: LGPL-2.0-only
10 */
11 
12 #include "klistopenfilesjob.h"
13 
14 #include <QDir>
15 #include <QProcess>
16 #include <QRegularExpression>
17 #include <QStandardPaths>
18 #include <QVector>
19 
20 class KListOpenFilesJobPrivate
21 {
22 public:
23  KListOpenFilesJobPrivate(KListOpenFilesJob *Job, const QDir &Path)
24  : job(Job)
25  , path(Path)
26  {
27  QObject::connect(&lsofProcess, &QProcess::errorOccurred, job, [this](QProcess::ProcessError error) {
28  lsofError(error);
29  });
30 
31  QObject::connect(&lsofProcess, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), job, [this](int exitCode, QProcess::ExitStatus exitStatus) {
32  lsofFinished(exitCode, exitStatus);
33  });
34  }
35 
36  void start()
37  {
38  if (!path.exists()) {
39  emitResult(static_cast<int>(KListOpenFilesJob::Error::DoesNotExist), QObject::tr("Path %1 doesn't exist").arg(path.path()));
40  return;
41  }
42 
43  const QString lsofExec = QStandardPaths::findExecutable(QStringLiteral("lsof"));
44  if (lsofExec.isEmpty()) {
45  const QString envPath = QString::fromLocal8Bit(qgetenv("PATH"));
46  emitResult(static_cast<int>(KListOpenFilesJob::Error::InternalError), QObject::tr("Could not find lsof executable in PATH:").arg(envPath));
47  return;
48  }
49 
50  lsofProcess.start(lsofExec, {QStringLiteral("-t"), QStringLiteral("+d"), path.path()});
51  }
52 
53  void lsofError(QProcess::ProcessError processError)
54  {
55  emitResult(static_cast<int>(KListOpenFilesJob::Error::InternalError), QObject::tr("Failed to execute `lsof'. Error code %1").arg(processError));
56  }
57 
58  void lsofFinished(int, QProcess::ExitStatus);
59  void emitResult(int error, const QString &errorText);
60 
61  KListOpenFilesJob *job;
62  const QDir path;
63  bool hasEmittedResult = false;
64  QProcess lsofProcess;
65 
66  KProcessList::KProcessInfoList processInfoList;
67 };
68 
69 static KProcessList::KProcessInfo findInfoForPid(qint64 pid)
70 {
71 #ifdef HAVE_PROCSTAT
72  // If HAVE_PROCSTAT is defined, then we're on a BSD, and there is a KProcessList implementation
73  // that efficiently lists all processes, but KProcessList::processInfo() is slow because
74  // it recalculates the list-of-all-processes on each iteration.
75  const auto allProcesses = KProcessList::processInfoList();
76  auto it = std::find_if(allProcesses.cbegin(), allProcesses.cend(), [pid](const KProcessList::KProcessInfo &info) {
77  return info.pid() == pid;
78  });
79  return it != allProcesses.cend() ? *it : KProcessList::KProcessInfo{};
80 #else
81  // Presumably Linux: processInfo(pid) is fine because it goes
82  // straight to /proc/<pid> for information.
83  return KProcessList::processInfo(pid);
84 #endif
85 }
86 
87 void KListOpenFilesJobPrivate::lsofFinished(int, QProcess::ExitStatus)
88 {
89  if (hasEmittedResult) {
90  return;
91  }
92  const QString out(QString::fromLocal8Bit(lsofProcess.readAll()));
93 
94  const QRegularExpression re(QStringLiteral("\\s+"));
95 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
96  const QVector<QStringView> pidList = QStringView(out).split(re, Qt::SkipEmptyParts);
97 #else
98  const QVector<QStringRef> pidList = out.splitRef(re, Qt::SkipEmptyParts);
99 #endif
100 
101  for (const auto &pidStr : pidList) {
102  const qint64 pid = pidStr.toLongLong();
103  if (pid) {
104  processInfoList << findInfoForPid(pid);
105  }
106  }
107  job->emitResult();
108 }
109 
110 void KListOpenFilesJobPrivate::emitResult(int error, const QString &errorText)
111 {
112  if (hasEmittedResult) {
113  return;
114  }
115  job->setError(error);
116  job->setErrorText(errorText);
117  job->emitResult();
118  hasEmittedResult = true;
119 }
120 
121 KListOpenFilesJob::KListOpenFilesJob(const QString &path)
122  : d(new KListOpenFilesJobPrivate(this, path))
123 {
124 }
125 
126 KListOpenFilesJob::~KListOpenFilesJob() = default;
127 
129 {
130  d->start();
131 }
132 
134 {
135  return d->processInfoList;
136 }
137 
138 #include "moc_klistopenfilesjob.cpp"
Q_SCRIPTABLE Q_NOREPLY void start()
void start() override
Starts the job asynchronously.
KProcessList::KProcessInfoList processInfoList() const
Returns the list of processes with open files for the requested path.
void finished(int exitCode)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QString findExecutable(const QString &executableName, const QStringList &paths)
QString fromLocal8Bit(const char *str, int size)
SkipEmptyParts
bool isEmpty() const const
Contains information about a process.
Definition: kprocesslist.h:27
QString path(const QString &relativePath)
Absolute libexec path resolved in relative relation to the current shared object.
Definition: klibexec.h:48
void errorOccurred(QProcess::ProcessError error)
QString tr(const char *sourceText, const char *disambiguation, int n)
Provides information about processes that have open files in a given path or subdirectory of path.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon May 8 2023 04:04:52 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.