VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/linux/HostDnsServiceLinux.cpp@ 98067

Last change on this file since 98067 was 98067, checked in by vboxsync, 2 years ago

Main/HostDnsServiceLinux.cpp: Some @todos and cleanups. bugref:10255

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 8.6 KB
Line 
1/* $Id: HostDnsServiceLinux.cpp 98067 2023-01-12 23:04:34Z vboxsync $ */
2/** @file
3 * Linux specific DNS information fetching.
4 */
5
6/*
7 * Copyright (C) 2013-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#include <iprt/assert.h>
29#include <iprt/errcore.h>
30#include <iprt/initterm.h>
31#include <iprt/file.h>
32#include <iprt/log.h>
33#include <iprt/stream.h>
34#include <iprt/string.h>
35#include <iprt/semaphore.h>
36#include <iprt/thread.h>
37
38#include <errno.h>
39#include <poll.h>
40#include <string.h>
41#include <unistd.h>
42
43#include <fcntl.h>
44
45#include <linux/limits.h>
46
47/* Workaround for <sys/cdef.h> defining __flexarr to [] which beats us in
48 * struct inotify_event (char name __flexarr). */
49#include <sys/cdefs.h>
50#undef __flexarr
51#define __flexarr [0]
52#include <sys/inotify.h>
53#include <sys/types.h>
54#include <sys/socket.h>
55
56#include <iprt/sanitized/string>
57#include <vector>
58#include "../HostDnsService.h"
59
60
61static int g_DnsMonitorStop[2];
62
63static const std::string g_EtcFolder = "/etc";
64static const std::string g_ResolvConf = "resolv.conf";
65static const std::string g_ResolvConfFullPath = "/etc/resolv.conf";
66
67class FileDescriptor
68{
69public:
70 FileDescriptor(int d = -1)
71 : fd(d)
72 {}
73
74 virtual ~FileDescriptor() {
75 if (fd != -1)
76 close(fd);
77 }
78
79 int fileDescriptor() const {return fd;}
80
81protected:
82 int fd;
83};
84
85
86class AutoNotify : public FileDescriptor
87{
88public:
89 AutoNotify()
90 {
91 FileDescriptor::fd = inotify_init();
92 AssertReturnVoid(FileDescriptor::fd != -1);
93 }
94};
95
96struct InotifyEventWithName
97{
98 struct inotify_event e;
99 char name[NAME_MAX];
100};
101
102HostDnsServiceLinux::~HostDnsServiceLinux()
103{
104}
105
106HRESULT HostDnsServiceLinux::init(HostDnsMonitorProxy *pProxy)
107{
108 return HostDnsServiceResolvConf::init(pProxy, "/etc/resolv.conf");
109}
110
111int HostDnsServiceLinux::monitorThreadShutdown(RTMSINTERVAL uTimeoutMs)
112{
113 RT_NOREF(uTimeoutMs);
114
115 send(g_DnsMonitorStop[0], "", 1, 0);
116
117 /** @todo r=andy Do we have to wait for something here? Can this fail? */
118 return VINF_SUCCESS;
119}
120
121int HostDnsServiceLinux::monitorThreadProc(void)
122{
123 /*
124 * inotify initialization
125 *
126 * Note! Ignoring failures here is safe, because poll will ignore entires
127 * with negative fd values.
128 */
129 AutoNotify a;
130 int wd[2];
131 wd[0] = inotify_add_watch(a.fileDescriptor(),
132 g_ResolvConfFullPath.c_str(), IN_CLOSE_WRITE | IN_DELETE_SELF);
133
134 /* If /etc/resolv.conf exists we want to listen for movements: because
135 # mv /etc/resolv.conf ...
136 won't arm IN_DELETE_SELF on wd[0] instead it will fire IN_MOVE_FROM on wd[1].
137
138 Because on some distributions /etc/resolv.conf is a symlink, wd[0] can't detect
139 deletion, it's recognizible on directory level (wd[1]) only. */
140 wd[1] = inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(),
141 wd[0] == -1 ? IN_MOVED_TO | IN_CREATE : IN_MOVED_FROM | IN_DELETE);
142
143 /*
144 * Create a socket pair for signalling shutdown via (see monitorThreadShutdown).
145 */
146 int rc = socketpair(AF_LOCAL, SOCK_DGRAM, 0, g_DnsMonitorStop);
147 AssertMsgReturn(rc == 0, ("socketpair: failed (%d: %s)\n", errno, strerror(errno)), E_FAIL);
148
149 /* automatic cleanup tricks */
150 FileDescriptor stopper0(g_DnsMonitorStop[0]);
151 FileDescriptor stopper1(g_DnsMonitorStop[1]);
152
153 /*
154 * poll initialization:
155 */
156 pollfd polls[2];
157 RT_ZERO(polls);
158
159 polls[0].fd = a.fileDescriptor();
160 polls[0].events = POLLIN;
161
162 polls[1].fd = g_DnsMonitorStop[1];
163 polls[1].events = POLLIN;
164
165 onMonitorThreadInitDone();
166
167 /*
168 * The monitoring loop.
169 */
170 for (;;)
171 {
172 rc = poll(polls, RT_ELEMENTS(polls), -1 /*infinite timeout*/);
173 if (rc == -1)
174 continue;
175
176 AssertMsgReturn( (polls[0].revents & (POLLERR | POLLNVAL)) == 0
177 && (polls[1].revents & (POLLERR | POLLNVAL)) == 0, ("Debug Me"), VERR_INTERNAL_ERROR);
178
179 if (polls[1].revents & POLLIN)
180 return VINF_SUCCESS; /* time to shutdown */
181
182 if (polls[0].revents & POLLIN)
183 {
184 /*
185 * Read the notification event.
186 */
187 /** @todo r=bird: This is buggy in that it somehow assumes we'll only get
188 * one event here. But since we're waiting on two different DELETE
189 * events for both a specific file and its parent directory, we're likely
190 * to get two DELETE events at the same time. */
191 struct InotifyEventWithName combo;
192 RT_ZERO(combo);
193 combo.e.wd = -42; /* avoid confusion on the offchance that wd[0] or wd[1] is zero. */
194
195 ssize_t r = read(polls[0].fd, &combo, sizeof(combo));
196 RT_NOREF(r);
197
198 if (combo.e.wd == wd[0])
199 {
200 if (combo.e.mask & IN_CLOSE_WRITE)
201 readResolvConf();
202 else if (combo.e.mask & IN_DELETE_SELF)
203 {
204 inotify_rm_watch(a.fileDescriptor(), wd[0]); /* removes file watcher */
205 int wd2 = inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(),
206 IN_MOVED_TO|IN_CREATE); /* alter folder watcher */
207 Assert(wd2 == wd[1]); RT_NOREF(wd2); /* ASSUMES wd[1] will be updated */
208 }
209 else if (combo.e.mask & IN_IGNORED)
210 wd[0] = -1; /* we want receive any events on this watch */
211 else
212 {
213 /*
214 * It shouldn't happen, in release we will just ignore in debug
215 * we will have to chance to look at into inotify_event
216 */
217 AssertMsgFailed(("Debug Me!!!"));
218 }
219 }
220 else if (combo.e.wd == wd[1])
221 {
222 if (combo.e.mask & (IN_DELETE | IN_MOVED_FROM))
223 {
224 if (g_ResolvConf == combo.e.name)
225 {
226 /*
227 * Our file has been moved or deleted so we should change watching mode.
228 */
229 inotify_rm_watch(a.fileDescriptor(), wd[0]);
230 wd[1] = inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(),
231 IN_MOVED_TO | IN_CREATE);
232 AssertMsg(wd[1] != -1,
233 ("It shouldn't happen, further investigation is needed\n"));
234 }
235 }
236 else
237 {
238 AssertMsg(combo.e.mask & (IN_MOVED_TO | IN_CREATE),
239 ("%RX32 event isn't expected, we are waiting for IN_MOVED|IN_CREATE\n", combo.e.mask));
240 if (g_ResolvConf == combo.e.name)
241 {
242 AssertMsg(wd[0] == -1, ("We haven't removed file watcher first\n"));
243
244 /* alter folder watcher: */
245 wd[1] = inotify_add_watch(a.fileDescriptor(),
246 g_EtcFolder.c_str(),
247 IN_MOVED_FROM | IN_DELETE);
248 AssertMsg(wd[1] != -1, ("It shouldn't happen.\n"));
249
250 wd[0] = inotify_add_watch(a.fileDescriptor(),
251 g_ResolvConfFullPath.c_str(),
252 IN_CLOSE_WRITE | IN_DELETE_SELF);
253 AssertMsg(wd[0] != -1, ("Adding watcher to file (%s) has been failed!\n",
254 g_ResolvConfFullPath.c_str()));
255
256 /* Notify our listeners */
257 readResolvConf();
258 }
259 }
260 }
261 else
262 {
263 /* It shouldn't happen */
264 AssertMsgFailed(("Shouldn't happen! Please debug me!"));
265 }
266 }
267 }
268}
269
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette