File indexing completed on 2024-04-21 05:45:52
0001 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0002 // SPDX-FileCopyrightText: 2021-2022 Harald Sitter <sitter@kde.org> 0003 0004 #include "entries.h" 0005 0006 #include <sys/stat.h> 0007 #include <sys/types.h> 0008 #include <unistd.h> 0009 0010 #include <cctype> 0011 #include <filesystem> 0012 0013 #include <QDebug> 0014 #include <QFile> 0015 0016 static constexpr auto kernelMaxWatches = 1048576UL; 0017 static constexpr auto kernelDefaultWatches = 8192UL; 0018 static constexpr auto kernelDefaultInstances = 128UL; 0019 0020 qulonglong procULongLong(const QString &path) 0021 { 0022 if (QFile file(path); file.open(QFile::ReadOnly)) { 0023 bool ok = false; 0024 const qulonglong value = file.readAll().simplified().toULongLong(&ok); 0025 if (ok) { 0026 return value; 0027 } 0028 } 0029 return 0; 0030 } 0031 0032 std::vector<Entry> collectEntries(const std::string_view &procPath) 0033 { 0034 std::vector<Entry> ret; 0035 std::filesystem::path proc(procPath); 0036 for (const auto &procEntry : std::filesystem::directory_iterator(proc)) { 0037 if (!procEntry.is_directory()) { 0038 continue; 0039 } 0040 0041 bool isInt = false; 0042 Entry entry; 0043 entry.pid = QString::fromStdString(procEntry.path().filename()).toInt(&isInt); 0044 if (!isInt) { 0045 continue; 0046 } 0047 0048 struct stat statbuf { 0049 }; 0050 if (lstat(procEntry.path().c_str(), &statbuf) != 0) { 0051 qWarning() << "Failed to stat" << procEntry.path().c_str() << strerror(errno); 0052 continue; 0053 } 0054 entry.uid = statbuf.st_uid; 0055 0056 std::error_code error; 0057 for (const auto &fdEntry : std::filesystem::directory_iterator(procEntry.path() / u"fd", error)) { 0058 if (!fdEntry.is_symlink()) { 0059 continue; 0060 } 0061 const auto symlinkTarget = std::filesystem::read_symlink(fdEntry, error); 0062 if (error) { 0063 continue; 0064 } 0065 if (symlinkTarget != u"anon_inode:inotify") { 0066 continue; 0067 } 0068 ++entry.instances; 0069 0070 const QString fdinfoPath = QString::fromStdString(procEntry.path() / u"fdinfo" / fdEntry.path().filename()); 0071 if (QFile fdinfo(fdinfoPath); fdinfo.open(QFile::ReadOnly)) { 0072 // This being a pseudo filesystem we want to read this all at once as canReadLine and friends will 0073 // not necessarily work correctly. 0074 const QList<QByteArray> lines = fdinfo.readAll().split('\n'); 0075 for (const auto &line : lines) { 0076 if (line.startsWith(QByteArrayLiteral("inotify "))) { 0077 ++entry.watches; 0078 } 0079 } 0080 } else { 0081 qWarning() << "Failed to open fdinfo" << fdinfoPath; 0082 continue; 0083 } 0084 } 0085 if (error) { 0086 continue; 0087 } 0088 if (entry.instances <= 0) { 0089 continue; 0090 } 0091 0092 const QString cmdlinePath = QString::fromStdString(procEntry.path() / u"cmdline"); 0093 if (QFile cmdline(cmdlinePath); cmdline.open(QFile::ReadOnly)) { 0094 entry.cmdline = cmdline.readAll().simplified(); 0095 static constexpr auto limit = 70; 0096 static constexpr auto limitWithEllipsis = 70 + 3; 0097 if (entry.cmdline.size() > limitWithEllipsis) { 0098 entry.cmdline = entry.cmdline.left(limit) + QLatin1String("..."); 0099 } 0100 } 0101 0102 ret.push_back(entry); 0103 } 0104 return ret; 0105 } 0106 0107 INotifyCapacity inotifyCapacity(const std::string_view &procPath) 0108 { 0109 return { 0110 .max_user_instances = procULongLong(QString::fromStdU16String(max_user_instances_path(procPath).u16string())), 0111 .max_user_watches = procULongLong(QString::fromStdU16String(max_user_watches_path(procPath).u16string())), 0112 }; 0113 } 0114 0115 INotifyCapacity maximumINotifyCapacity() 0116 { 0117 return { 0118 .max_user_instances = kernelMaxWatches / kernelDefaultInstances, 0119 .max_user_watches = kernelMaxWatches, 0120 }; 0121 } 0122 0123 qulonglong instanceStepSize() 0124 { 0125 return kernelDefaultInstances; 0126 } 0127 0128 qulonglong watchStepSize() 0129 { 0130 return kernelDefaultWatches; 0131 } 0132 0133 std::filesystem::path max_user_instances_path(const std::string_view &procPath) 0134 { 0135 return std::filesystem::path(procPath) / std::filesystem::path("sys/fs/inotify/max_user_instances"); 0136 } 0137 0138 std::filesystem::path max_user_watches_path(const std::string_view &procPath) 0139 { 0140 return std::filesystem::path(procPath) / std::filesystem::path("sys/fs/inotify/max_user_watches"); 0141 }