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 return;
56 }
57
58 m_socketNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
59 connect(m_socketNotifier, &QSocketNotifier::activated, this, &FstabWatcher::onMountChanged);
60
61 if (qApp) {
62 connect(qApp, &QCoreApplication::aboutToQuit, this, &FstabWatcher::onQuit);
63 }
64#else
65 m_isRoutineInstalled = false;
66 m_fileSystemWatcher = new QFileSystemWatcher(this);
67
68 m_mtabFile = new QFile(s_mtabFile, this);
69 if (m_mtabFile && m_mtabFile->symLinkTarget().startsWith(QLatin1String("/proc/")) && m_mtabFile->open(QIODevice::ReadOnly)) {
70 m_socketNotifier = new QSocketNotifier(m_mtabFile->handle(), QSocketNotifier::Exception, this);
71 connect(m_socketNotifier, &QSocketNotifier::activated, this, &FstabWatcher::mtabChanged);
72 } else {
73 m_fileSystemWatcher->addPath(s_mtabFile);
74 }
75
76 m_fileSystemWatcher->addPath(s_fstabPath);
77 connect(m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, [this](const QString &) {
78 if (!m_isFstabWatched) {
79 m_isFstabWatched = m_fileSystemWatcher->addPath(s_fstabFile);
80 if (m_isFstabWatched) {
81 qCDebug(FSTAB_LOG) << "Re-added" << s_fstabFile;
82 Q_EMIT onFileChanged(s_fstabFile);
83 }
84 }
85 });
86
87 m_isFstabWatched = m_fileSystemWatcher->addPath(s_fstabFile);
88 connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &FstabWatcher::onFileChanged);
89#endif
90}
91
92FstabWatcher::~FstabWatcher()
93{
94#ifdef Q_OS_LINUX
95 mnt_unref_monitor(m_mountMonitor);
96#else
97 // The QFileSystemWatcher doesn't work correctly in a singleton
98 // The solution so far was to destroy the QFileSystemWatcher when the application quits
99 // But we have some crash with this solution.
100 // For the moment to workaround the problem, we detach the QFileSystemWatcher from the parent
101 // effectively leaking it on purpose.
102 m_fileSystemWatcher->setParent(nullptr);
103#endif
104}
105
106void FstabWatcher::onQuit()
107{
108#ifndef Q_OS_LINUX
109 m_fileSystemWatcher->setParent(nullptr);
110#endif
111}
112
113FstabWatcher *FstabWatcher::instance()
114{
115#if 0
116 FstabWatcher *fstabWatcher = globalFstabWatcher;
117
118 if (fstabWatcher && !fstabWatcher->m_isRoutineInstalled) {
119 qAddPostRoutine(globalFstabWatcher.destroy);
120 fstabWatcher->m_isRoutineInstalled = true;
121 }
122 return fstabWatcher;
123#else
124 return globalFstabWatcher;
125#endif
126}
127
128#ifdef Q_OS_LINUX
129void FstabWatcher::onMountChanged()
130{
131 auto mtab = false;
132 auto fstab = false;
133 const char *filename;
134 while (mnt_monitor_next_change(m_mountMonitor, &filename, NULL) == 0) {
135 if (!mtab && ((strcmp(filename, "/proc/self/mountinfo") == 0) || (strcmp(filename, "/run/mount/utab") == 0))) {
136 mtab = true;
137 }
138 if (!fstab && (strcmp(filename, "/etc/fstab") == 0)) {
139 fstab = true;
140 }
141 }
142
143 if (mtab) {
144 Q_EMIT mtabChanged();
145 }
146 if (fstab) {
147 Q_EMIT fstabChanged();
148 }
149}
150#else
151void FstabWatcher::onFileChanged(const QString &path)
152{
153 if (path == s_mtabFile) {
154 Q_EMIT mtabChanged();
155 if (!m_fileSystemWatcher->files().contains(s_mtabFile)) {
156 m_fileSystemWatcher->addPath(s_mtabFile);
157 }
158 }
159 if (path == s_fstabFile) {
160 Q_EMIT fstabChanged();
161 if (!m_fileSystemWatcher->files().contains(s_fstabFile)) {
162 m_isFstabWatched = m_fileSystemWatcher->addPath(s_fstabFile);
163 qCDebug(FSTAB_LOG) << "Fstab removed, re-added:" << m_isFstabWatched;
164 }
165 }
166}
167#endif
168}
169}
170} // namespace Solid:Backends::Fstab
171
172#include "moc_fstabwatcher.cpp"
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
QString symLinkTarget(const QString &fileName)
int handle() const const
bool addPath(const QString &path)
void directoryChanged(const QString &path)
void fileChanged(const QString &path)
QStringList files() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void setParent(QObject *parent)
void activated(QSocketDescriptor socket, QSocketNotifier::Type type)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 22 2024 12:01:48 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.