Solid

fstabwatcher.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Mario Bensi <mbensi@ipsquad.net>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "fstabwatcher.h"
8#include "fstab_debug.h"
9#include "soliddefs_p.h"
10
11#include <QCoreApplication>
12#include <QFile>
13#include <QFileSystemWatcher>
14#include <QSocketNotifier>
15
16namespace Solid
17{
18namespace Backends
19{
20namespace Fstab
21{
22Q_GLOBAL_STATIC(FstabWatcher, globalFstabWatcher)
23
24static const QString s_mtabFile = QStringLiteral("/etc/mtab");
25static const QString s_fstabFile = QStringLiteral("/etc/fstab");
26static const QString s_fstabPath = QStringLiteral("/etc");
27
28FstabWatcher::FstabWatcher()
29{
30#ifdef Q_OS_LINUX
31 auto mountMonitor = mnt_new_monitor();
32
33 if (!mountMonitor) {
34 qCritical(FSTAB_LOG) << "could not start mount monitor";
35 return;
36 }
37 m_mountMonitor = mountMonitor;
38
39 auto r = mnt_monitor_enable_kernel(m_mountMonitor, true);
40 if (r < 0) {
41 mnt_unref_monitor(m_mountMonitor);
42 qCritical(FSTAB_LOG) << "Failed to enable watching of kernel mount events:" << strerror(errno);
43 }
44
45 r = mnt_monitor_enable_userspace(m_mountMonitor, true, NULL);
46 if (r < 0) {
47 mnt_unref_monitor(m_mountMonitor);
48 qCritical(FSTAB_LOG) << "Failed to enable watching of userspace mount events:" << strerror(errno);
49 }
50
51 auto fd = mnt_monitor_get_fd(m_mountMonitor);
52 if (fd < 0) {
53 mnt_unref_monitor(m_mountMonitor);
54 qCritical(FSTAB_LOG) << "Failed to acquire watch file descriptor" << strerror(errno);
55 m_mountMonitor = nullptr;
56 return;
57 }
58
59 m_socketNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
60 connect(m_socketNotifier, &QSocketNotifier::activated, this, &FstabWatcher::onMountChanged);
61
62 if (qApp) {
63 connect(qApp, &QCoreApplication::aboutToQuit, this, &FstabWatcher::onQuit);
64 }
65#else
66 m_isRoutineInstalled = false;
67 m_fileSystemWatcher = new QFileSystemWatcher(this);
68
69 m_mtabFile = new QFile(s_mtabFile, this);
70 if (m_mtabFile && m_mtabFile->symLinkTarget().startsWith(QLatin1String("/proc/")) && m_mtabFile->open(QIODevice::ReadOnly)) {
71 m_socketNotifier = new QSocketNotifier(m_mtabFile->handle(), QSocketNotifier::Exception, this);
72 connect(m_socketNotifier, &QSocketNotifier::activated, this, &FstabWatcher::mtabChanged);
73 } else {
74 m_fileSystemWatcher->addPath(s_mtabFile);
75 }
76
77 m_fileSystemWatcher->addPath(s_fstabPath);
78 connect(m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, [this](const QString &) {
79 if (!m_isFstabWatched) {
80 m_isFstabWatched = m_fileSystemWatcher->addPath(s_fstabFile);
81 if (m_isFstabWatched) {
82 qCDebug(FSTAB_LOG) << "Re-added" << s_fstabFile;
83 Q_EMIT onFileChanged(s_fstabFile);
84 }
85 }
86 });
87
88 m_isFstabWatched = m_fileSystemWatcher->addPath(s_fstabFile);
89 connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &FstabWatcher::onFileChanged);
90#endif
91}
92
93FstabWatcher::~FstabWatcher()
94{
95#ifdef Q_OS_LINUX
96 if (m_mountMonitor) {
97 mnt_unref_monitor(m_mountMonitor);
98 }
99#else
100 // The QFileSystemWatcher doesn't work correctly in a singleton
101 // The solution so far was to destroy the QFileSystemWatcher when the application quits
102 // But we have some crash with this solution.
103 // For the moment to workaround the problem, we detach the QFileSystemWatcher from the parent
104 // effectively leaking it on purpose.
105 m_fileSystemWatcher->setParent(nullptr);
106#endif
107}
108
109void FstabWatcher::onQuit()
110{
111#ifndef Q_OS_LINUX
112 m_fileSystemWatcher->setParent(nullptr);
113#endif
114}
115
116FstabWatcher *FstabWatcher::instance()
117{
118#if 0
119 FstabWatcher *fstabWatcher = globalFstabWatcher;
120
121 if (fstabWatcher && !fstabWatcher->m_isRoutineInstalled) {
122 qAddPostRoutine(globalFstabWatcher.destroy);
123 fstabWatcher->m_isRoutineInstalled = true;
124 }
125 return fstabWatcher;
126#else
127 return globalFstabWatcher;
128#endif
129}
130
131#ifdef Q_OS_LINUX
132void FstabWatcher::onMountChanged()
133{
134 auto mtab = false;
135 auto fstab = false;
136 const char *filename;
137 while (mnt_monitor_next_change(m_mountMonitor, &filename, NULL) == 0) {
138 if (!mtab && ((strcmp(filename, "/proc/self/mountinfo") == 0) || (strcmp(filename, "/run/mount/utab") == 0))) {
139 mtab = true;
140 }
141 if (!fstab && (strcmp(filename, "/etc/fstab") == 0)) {
142 fstab = true;
143 }
144 }
145
146 if (mtab) {
147 Q_EMIT mtabChanged();
148 }
149 if (fstab) {
150 Q_EMIT fstabChanged();
151 }
152}
153#else
154void FstabWatcher::onFileChanged(const QString &path)
155{
156 if (path == s_mtabFile) {
157 Q_EMIT mtabChanged();
158 if (!m_fileSystemWatcher->files().contains(s_mtabFile)) {
159 m_fileSystemWatcher->addPath(s_mtabFile);
160 }
161 }
162 if (path == s_fstabFile) {
163 Q_EMIT fstabChanged();
164 if (!m_fileSystemWatcher->files().contains(s_fstabFile)) {
165 m_isFstabWatched = m_fileSystemWatcher->addPath(s_fstabFile);
166 qCDebug(FSTAB_LOG) << "Fstab removed, re-added:" << m_isFstabWatched;
167 }
168 }
169}
170#endif
171}
172}
173} // namespace Solid:Backends::Fstab
174
175#include "moc_fstabwatcher.cpp"
void directoryChanged(const QString &path)
void fileChanged(const QString &path)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void activated(QSocketDescriptor socket, QSocketNotifier::Type type)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Feb 28 2025 11:49:36 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.