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 | |
16 | namespace Solid |
17 | { |
18 | namespace Backends |
19 | { |
20 | namespace Fstab |
21 | { |
22 | Q_GLOBAL_STATIC(FstabWatcher, globalFstabWatcher) |
23 | |
24 | static const QString s_mtabFile = QStringLiteral("/etc/mtab" ); |
25 | static const QString s_fstabFile = QStringLiteral("/etc/fstab" ); |
26 | static const QString s_fstabPath = QStringLiteral("/etc" ); |
27 | |
28 | FstabWatcher::FstabWatcher() |
29 | { |
30 | #ifdef Q_OS_LINUX |
31 | auto mountMonitor = mnt_new_monitor(); |
32 | |
33 | if (!mountMonitor) { |
34 | qCritical(catFunc: FSTAB_LOG) << "could not start mount monitor" ; |
35 | return; |
36 | } |
37 | m_mountMonitor = mountMonitor; |
38 | |
39 | auto r = mnt_monitor_enable_kernel(mn: m_mountMonitor, enable: true); |
40 | if (r < 0) { |
41 | mnt_unref_monitor(mn: m_mountMonitor); |
42 | qCritical(catFunc: FSTAB_LOG) << "Failed to enable watching of kernel mount events:" << strerror(errno); |
43 | } |
44 | |
45 | r = mnt_monitor_enable_userspace(mn: m_mountMonitor, enable: true, NULL); |
46 | if (r < 0) { |
47 | mnt_unref_monitor(mn: m_mountMonitor); |
48 | qCritical(catFunc: FSTAB_LOG) << "Failed to enable watching of userspace mount events:" << strerror(errno); |
49 | } |
50 | |
51 | auto fd = mnt_monitor_get_fd(mn: m_mountMonitor); |
52 | if (fd < 0) { |
53 | mnt_unref_monitor(mn: m_mountMonitor); |
54 | qCritical(catFunc: 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(sender: m_socketNotifier, signal: &QSocketNotifier::activated, context: this, slot: &FstabWatcher::onMountChanged); |
61 | |
62 | if (qApp) { |
63 | connect(qApp, signal: &QCoreApplication::aboutToQuit, context: this, slot: &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 | |
93 | FstabWatcher::~FstabWatcher() |
94 | { |
95 | #ifdef Q_OS_LINUX |
96 | if (m_mountMonitor) { |
97 | mnt_unref_monitor(mn: 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 | |
109 | void FstabWatcher::onQuit() |
110 | { |
111 | #ifndef Q_OS_LINUX |
112 | m_fileSystemWatcher->setParent(nullptr); |
113 | #endif |
114 | } |
115 | |
116 | FstabWatcher *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 |
132 | void FstabWatcher::onMountChanged() |
133 | { |
134 | auto mtab = false; |
135 | auto fstab = false; |
136 | const char *filename; |
137 | while (mnt_monitor_next_change(mn: m_mountMonitor, filename: &filename, NULL) == 0) { |
138 | if (!mtab && ((strcmp(s1: filename, s2: "/proc/self/mountinfo" ) == 0) || (strcmp(s1: filename, s2: "/run/mount/utab" ) == 0))) { |
139 | mtab = true; |
140 | } |
141 | if (!fstab && (strcmp(s1: filename, s2: "/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 |
154 | void 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" |
176 | |