1/*
2 This file is part of the KDE project
3
4 SPDX-FileCopyrightText: 2010 Jacopo De Simoi <wilderkde@gmail.com>
5 SPDX-FileCopyrightText: 2014 Lukáš Tinkl <ltinkl@redhat.com>
6 SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
7 SPDX-FileCopyrightText: 2019 David Hallas <david@davidhallas.dk>
8
9 SPDX-License-Identifier: LGPL-2.0-only
10*/
11
12#include "klistopenfilesjob.h"
13
14#include <QDir>
15#include <QList>
16#include <QProcess>
17#include <QRegularExpression>
18#include <QStandardPaths>
19
20class KListOpenFilesJobPrivate
21{
22public:
23 KListOpenFilesJobPrivate(KListOpenFilesJob *Job, const QDir &Path)
24 : job(Job)
25 , path(Path)
26 {
27 QObject::connect(sender: &lsofProcess, signal: &QProcess::errorOccurred, context: job, slot: [this](QProcess::ProcessError error) {
28 lsofError(processError: error);
29 });
30
31 QObject::connect(sender: &lsofProcess, signal: qOverload<int, QProcess::ExitStatus>(&QProcess::finished), context: job, slot: [this](int exitCode, QProcess::ExitStatus exitStatus) {
32 lsofFinished(exitCode, exitStatus);
33 });
34 }
35
36 void start()
37 {
38 if (!path.exists()) {
39 emitResult(error: static_cast<int>(KListOpenFilesJob::Error::DoesNotExist), errorText: QObject::tr(s: "Path %1 doesn't exist").arg(a: path.path()));
40 return;
41 }
42
43 const QString lsofExec = QStandardPaths::findExecutable(QStringLiteral("lsof"));
44 if (lsofExec.isEmpty()) {
45 const QString envPath = QString::fromLocal8Bit(ba: qgetenv(varName: "PATH"));
46 emitResult(error: static_cast<int>(KListOpenFilesJob::Error::InternalError), errorText: QObject::tr(s: "Could not find lsof executable in PATH:").arg(a: envPath));
47 return;
48 }
49
50 lsofProcess.start(program: lsofExec, arguments: {QStringLiteral("-t"), QStringLiteral("+d"), path.path()});
51 }
52
53 void lsofError(QProcess::ProcessError processError)
54 {
55 emitResult(error: static_cast<int>(KListOpenFilesJob::Error::InternalError), errorText: QObject::tr(s: "Failed to execute `lsof'. Error code %1").arg(a: 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
69static 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
87void KListOpenFilesJobPrivate::lsofFinished(int, QProcess::ExitStatus)
88{
89 if (hasEmittedResult) {
90 return;
91 }
92 const QString out(QString::fromLocal8Bit(ba: lsofProcess.readAll()));
93
94 const QRegularExpression re(QStringLiteral("\\s+"));
95 const QList<QStringView> pidList = QStringView(out).split(sep: re, behavior: Qt::SkipEmptyParts);
96
97 for (const auto &pidStr : pidList) {
98 const qint64 pid = pidStr.toLongLong();
99 if (pid) {
100 processInfoList << findInfoForPid(pid);
101 }
102 }
103 job->emitResult();
104}
105
106void KListOpenFilesJobPrivate::emitResult(int error, const QString &errorText)
107{
108 if (hasEmittedResult) {
109 return;
110 }
111 job->setError(error);
112 job->setErrorText(errorText);
113 job->emitResult();
114 hasEmittedResult = true;
115}
116
117KListOpenFilesJob::KListOpenFilesJob(const QString &path)
118 : d(new KListOpenFilesJobPrivate(this, path))
119{
120}
121
122KListOpenFilesJob::~KListOpenFilesJob() = default;
123
124void KListOpenFilesJob::start()
125{
126 d->start();
127}
128
129KProcessList::KProcessInfoList KListOpenFilesJob::processInfoList() const
130{
131 return d->processInfoList;
132}
133
134#include "moc_klistopenfilesjob.cpp"
135

source code of kcoreaddons/src/lib/util/klistopenfilesjob_unix.cpp