File indexing completed on 2024-05-05 05:48:34
0001 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0002 // SPDX-FileCopyrightText: 2020-2022 Harald Sitter <sitter@kde.org> 0003 0004 #include <sys/types.h> 0005 #include <unistd.h> 0006 0007 #include <chrono> 0008 0009 #include <QPointer> 0010 #include <QTimer> 0011 0012 #include <KAuth/Action> 0013 #include <KAuth/ExecuteJob> 0014 #include <KDEDModule> 0015 #include <KLocalizedString> 0016 #include <KNotification> 0017 #include <KPluginFactory> 0018 0019 #include <entries.h> 0020 0021 using namespace std::chrono_literals; 0022 0023 static constexpr auto warningPercent = 90; 0024 0025 class Notifier : public QObject 0026 { 0027 Q_OBJECT 0028 public: 0029 struct Context { 0030 const qulonglong percent; 0031 const QString eventId; 0032 const QString title; 0033 const QString text; 0034 bool actionable; 0035 const QString actionLabel; 0036 const QString authAction; 0037 }; 0038 0039 using QObject::QObject; 0040 0041 void process(const Context &context) 0042 { 0043 if (context.percent < warningPercent) { 0044 m_notified = false; 0045 m_notification = nullptr; 0046 } else if (!m_notified) { 0047 m_notified = true; 0048 m_notification = new KNotification(context.eventId); 0049 m_notification->setComponentName(QStringLiteral("org.kde.kded.inotify")); 0050 m_notification->setTitle(context.title); 0051 m_notification->setText(context.text); 0052 0053 if (context.actionable) { 0054 m_notification->setFlags(KNotification::Persistent); 0055 const QString authAction = context.authAction; // so we don't need to keep the entire context 0056 auto action = m_notification->addAction(context.actionLabel); 0057 connect(action, &KNotificationAction::activated, this, [this, authAction]() { 0058 m_notification->disconnect(this); 0059 m_notification->deleteLater(); 0060 0061 KAuth::Action action(authAction); 0062 action.setHelperId(QStringLiteral("org.kde.kded.inotify")); 0063 0064 KAuth::ExecuteJob *job = action.execute(); 0065 connect(job, &KJob::result, this, [this, job] { 0066 job->deleteLater(); 0067 Q_EMIT actioned(); 0068 }); 0069 job->start(); 0070 }); 0071 } 0072 m_notification->sendEvent(); 0073 } 0074 } 0075 0076 Q_SIGNALS: 0077 void actioned(); 0078 0079 private: 0080 bool m_notified = false; 0081 QPointer<KNotification> m_notification; // Notifications auto-delete use a QPointer to track them properly. 0082 }; 0083 0084 class InotifyModule : public KDEDModule 0085 { 0086 Q_OBJECT 0087 Q_CLASSINFO("D-Bus Interface", "org.kde.inotify") 0088 public: 0089 explicit InotifyModule(QObject *parent, const QVariantList &args) 0090 : KDEDModule(parent) 0091 { 0092 Q_UNUSED(args); 0093 0094 connect(&m_instanceNotifier, &Notifier::actioned, this, &InotifyModule::refresh); 0095 connect(&m_watchNotifier, &Notifier::actioned, this, &InotifyModule::refresh); 0096 0097 m_timer.setInterval(10min); 0098 connect(&m_timer, &QTimer::timeout, this, &InotifyModule::refresh); 0099 m_timer.start(); 0100 } 0101 0102 public Q_SLOTS: 0103 Q_SCRIPTABLE void refresh() 0104 { 0105 const auto entries = collectEntries(); 0106 0107 quint64 allInstances = 0; 0108 quint64 allWatches = 0; 0109 for (const auto &entry : entries) { 0110 if (entry.uid != getuid()) { 0111 continue; 0112 } 0113 allInstances += entry.instances; 0114 allWatches += entry.watches; 0115 } 0116 0117 const auto capacity = inotifyCapacity(); 0118 const auto instancePercent = 100 * allInstances / capacity.max_user_instances; 0119 const auto watchPercent = 100 * allWatches / capacity.max_user_watches; 0120 const auto maxCapacity = maximumINotifyCapacity(); 0121 0122 m_instanceNotifier.process(Notifier::Context{ 0123 .percent = instancePercent, 0124 .eventId = QStringLiteral("inotifyinstancelow"), 0125 .title = i18nc("@title", "Inotify Instance Capacity Low"), 0126 .text = capacity.max_user_instances < maxCapacity.max_user_instances 0127 ? i18nc("@info", 0128 "You have too many applications wanting to monitor file changes! When the capacity is " 0129 "exhausted it will prevent further file monitoring from working correctly. Either close " 0130 "some applications or increase the limit. " 0131 "Currently using %1% of instances and %2% of watches.", 0132 QString::number(instancePercent), 0133 QString::number(watchPercent)) 0134 : i18nc("@info", 0135 "You have too many applications wanting to monitor file changes! When the capacity is " 0136 "exhausted it will prevent further file monitoring from working correctly. Your capacity cannot " 0137 "be increased. Try closing some applications to recover additional resources. " 0138 "Currently using %1% of instances and %2% of watches.", 0139 QString::number(instancePercent), 0140 QString::number(watchPercent)), 0141 .actionable = capacity.max_user_instances < maxCapacity.max_user_instances, 0142 .actionLabel = i18nc("@action", "Increase Instance Limit"), 0143 .authAction = QStringLiteral("org.kde.kded.inotify.increaseinstancelimit"), 0144 }); 0145 0146 m_watchNotifier.process(Notifier::Context{ 0147 .percent = watchPercent, 0148 .eventId = QStringLiteral("inotifywatchlow"), 0149 .title = i18nc("@title", "Inotify Watch Capacity Low"), 0150 .text = capacity.max_user_watches < maxCapacity.max_user_watches 0151 ? i18nc("@info", 0152 "Your open applications want to watch too many files for changes! When the capacity is " 0153 "exhausted it will prevent further file monitoring from working correctly. Either close " 0154 "some applications or increase the limit. " 0155 "Currently using %1% of instances and %2% of watches.", 0156 QString::number(instancePercent), 0157 QString::number(watchPercent)) 0158 : i18nc("@info", 0159 "Your open applications want to watch too many files for changes! When the capacity is " 0160 "exhausted it will prevent further file monitoring from working correctly. Your capacity cannot " 0161 "be increased. Try closing some applications to recover additional resources. " 0162 "Currently using %1% of instances and %2% of watches.", 0163 QString::number(instancePercent), 0164 QString::number(watchPercent)), 0165 .actionable = capacity.max_user_watches < maxCapacity.max_user_watches, 0166 .actionLabel = i18nc("@action", "Increase Watch Limit"), 0167 .authAction = QStringLiteral("org.kde.kded.inotify.increasewatchlimit"), 0168 }); 0169 } 0170 0171 private: 0172 QTimer m_timer; 0173 Notifier m_instanceNotifier; 0174 Notifier m_watchNotifier; 0175 }; 0176 0177 K_PLUGIN_CLASS_WITH_JSON(InotifyModule, "inotify.json") 0178 0179 #include "kded.moc"