1 | /* $Id: HostDnsServiceLinux.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * Linux specific DNS information fetching.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2013-2022 Oracle Corporation
|
---|
8 | *
|
---|
9 | * This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
10 | * available from http://www.virtualbox.org. This file is free software;
|
---|
11 | * you can redistribute it and/or modify it under the terms of the GNU
|
---|
12 | * General Public License (GPL) as published by the Free Software
|
---|
13 | * Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
14 | * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
15 | * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
16 | */
|
---|
17 |
|
---|
18 | #include <iprt/assert.h>
|
---|
19 | #include <iprt/errcore.h>
|
---|
20 | #include <iprt/initterm.h>
|
---|
21 | #include <iprt/file.h>
|
---|
22 | #include <iprt/log.h>
|
---|
23 | #include <iprt/stream.h>
|
---|
24 | #include <iprt/string.h>
|
---|
25 | #include <iprt/semaphore.h>
|
---|
26 | #include <iprt/thread.h>
|
---|
27 |
|
---|
28 | #include <errno.h>
|
---|
29 | #include <poll.h>
|
---|
30 | #include <string.h>
|
---|
31 | #include <unistd.h>
|
---|
32 |
|
---|
33 | #include <fcntl.h>
|
---|
34 |
|
---|
35 | #include <linux/limits.h>
|
---|
36 |
|
---|
37 | /* Workaround for <sys/cdef.h> defining __flexarr to [] which beats us in
|
---|
38 | * struct inotify_event (char name __flexarr). */
|
---|
39 | #include <sys/cdefs.h>
|
---|
40 | #undef __flexarr
|
---|
41 | #define __flexarr [0]
|
---|
42 | #include <sys/inotify.h>
|
---|
43 | #include <sys/types.h>
|
---|
44 | #include <sys/socket.h>
|
---|
45 |
|
---|
46 | #include <iprt/sanitized/string>
|
---|
47 | #include <vector>
|
---|
48 | #include "../HostDnsService.h"
|
---|
49 |
|
---|
50 |
|
---|
51 | static int g_DnsMonitorStop[2];
|
---|
52 |
|
---|
53 | static const std::string g_EtcFolder = "/etc";
|
---|
54 | static const std::string g_ResolvConf = "resolv.conf";
|
---|
55 | static const std::string g_ResolvConfFullPath = "/etc/resolv.conf";
|
---|
56 |
|
---|
57 | class FileDescriptor
|
---|
58 | {
|
---|
59 | public:
|
---|
60 | FileDescriptor(int d = -1):fd(d){}
|
---|
61 |
|
---|
62 | virtual ~FileDescriptor() {
|
---|
63 | if (fd != -1)
|
---|
64 | close(fd);
|
---|
65 | }
|
---|
66 |
|
---|
67 | int fileDescriptor() const {return fd;}
|
---|
68 |
|
---|
69 | protected:
|
---|
70 | int fd;
|
---|
71 | };
|
---|
72 |
|
---|
73 |
|
---|
74 | class AutoNotify:public FileDescriptor
|
---|
75 | {
|
---|
76 | public:
|
---|
77 | AutoNotify()
|
---|
78 | {
|
---|
79 | FileDescriptor::fd = inotify_init();
|
---|
80 | AssertReturnVoid(FileDescriptor::fd != -1);
|
---|
81 | }
|
---|
82 | };
|
---|
83 |
|
---|
84 | struct InotifyEventWithName
|
---|
85 | {
|
---|
86 | struct inotify_event e;
|
---|
87 | char name[NAME_MAX];
|
---|
88 | };
|
---|
89 |
|
---|
90 | HostDnsServiceLinux::~HostDnsServiceLinux()
|
---|
91 | {
|
---|
92 | }
|
---|
93 |
|
---|
94 | HRESULT HostDnsServiceLinux::init(HostDnsMonitorProxy *pProxy)
|
---|
95 | {
|
---|
96 | return HostDnsServiceResolvConf::init(pProxy, "/etc/resolv.conf");
|
---|
97 | }
|
---|
98 |
|
---|
99 | int HostDnsServiceLinux::monitorThreadShutdown(RTMSINTERVAL uTimeoutMs)
|
---|
100 | {
|
---|
101 | RT_NOREF(uTimeoutMs);
|
---|
102 |
|
---|
103 | send(g_DnsMonitorStop[0], "", 1, 0);
|
---|
104 |
|
---|
105 | /** @todo r=andy Do we have to wait for something here? Can this fail? */
|
---|
106 | return VINF_SUCCESS;
|
---|
107 | }
|
---|
108 |
|
---|
109 | int HostDnsServiceLinux::monitorThreadProc(void)
|
---|
110 | {
|
---|
111 | AutoNotify a;
|
---|
112 |
|
---|
113 | int rc = socketpair(AF_LOCAL, SOCK_DGRAM, 0, g_DnsMonitorStop);
|
---|
114 | AssertMsgReturn(rc == 0, ("socketpair: failed (%d: %s)\n", errno, strerror(errno)), E_FAIL);
|
---|
115 |
|
---|
116 | FileDescriptor stopper0(g_DnsMonitorStop[0]);
|
---|
117 | FileDescriptor stopper1(g_DnsMonitorStop[1]);
|
---|
118 |
|
---|
119 | pollfd polls[2];
|
---|
120 | RT_ZERO(polls);
|
---|
121 |
|
---|
122 | polls[0].fd = a.fileDescriptor();
|
---|
123 | polls[0].events = POLLIN;
|
---|
124 |
|
---|
125 | polls[1].fd = g_DnsMonitorStop[1];
|
---|
126 | polls[1].events = POLLIN;
|
---|
127 |
|
---|
128 | onMonitorThreadInitDone();
|
---|
129 |
|
---|
130 | int wd[2];
|
---|
131 | wd[0] = wd[1] = -1;
|
---|
132 | /* inotify inialization */
|
---|
133 | wd[0] = inotify_add_watch(a.fileDescriptor(),
|
---|
134 | g_ResolvConfFullPath.c_str(), IN_CLOSE_WRITE|IN_DELETE_SELF);
|
---|
135 |
|
---|
136 | /**
|
---|
137 | * If /etc/resolv.conf exists we want to listen for movements: because
|
---|
138 | * # mv /etc/resolv.conf ...
|
---|
139 | * won't arm IN_DELETE_SELF on wd[0] instead it will fire IN_MOVE_FROM on wd[1].
|
---|
140 | *
|
---|
141 | * Because on some distributions /etc/resolv.conf is link, wd[0] can't detect deletion,
|
---|
142 | * it's recognizible on directory level (wd[1]) only.
|
---|
143 | */
|
---|
144 | wd[1] = inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(),
|
---|
145 | wd[0] == -1 ? IN_MOVED_TO|IN_CREATE : IN_MOVED_FROM|IN_DELETE);
|
---|
146 |
|
---|
147 | struct InotifyEventWithName combo;
|
---|
148 | while(true)
|
---|
149 | {
|
---|
150 | rc = poll(polls, 2, -1);
|
---|
151 | if (rc == -1)
|
---|
152 | continue;
|
---|
153 |
|
---|
154 | AssertMsgReturn( ((polls[0].revents & (POLLERR|POLLNVAL)) == 0)
|
---|
155 | && ((polls[1].revents & (POLLERR|POLLNVAL)) == 0),
|
---|
156 | ("Debug Me"), VERR_INTERNAL_ERROR);
|
---|
157 |
|
---|
158 | if (polls[1].revents & POLLIN)
|
---|
159 | return VINF_SUCCESS; /* time to shutdown */
|
---|
160 |
|
---|
161 | if (polls[0].revents & POLLIN)
|
---|
162 | {
|
---|
163 | RT_ZERO(combo);
|
---|
164 | ssize_t r = read(polls[0].fd, static_cast<void *>(&combo), sizeof(combo));
|
---|
165 | RT_NOREF(r);
|
---|
166 |
|
---|
167 | if (combo.e.wd == wd[0])
|
---|
168 | {
|
---|
169 | if (combo.e.mask & IN_CLOSE_WRITE)
|
---|
170 | {
|
---|
171 | readResolvConf();
|
---|
172 | }
|
---|
173 | else if (combo.e.mask & IN_DELETE_SELF)
|
---|
174 | {
|
---|
175 | inotify_rm_watch(a.fileDescriptor(), wd[0]); /* removes file watcher */
|
---|
176 | inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(),
|
---|
177 | IN_MOVED_TO|IN_CREATE); /* alter folder watcher */
|
---|
178 | }
|
---|
179 | else if (combo.e.mask & IN_IGNORED)
|
---|
180 | {
|
---|
181 | wd[0] = -1; /* we want receive any events on this watch */
|
---|
182 | }
|
---|
183 | else
|
---|
184 | {
|
---|
185 | /**
|
---|
186 | * It shouldn't happen, in release we will just ignore in debug
|
---|
187 | * we will have to chance to look at into inotify_event
|
---|
188 | */
|
---|
189 | AssertMsgFailed(("Debug Me!!!"));
|
---|
190 | }
|
---|
191 | }
|
---|
192 | else if (combo.e.wd == wd[1])
|
---|
193 | {
|
---|
194 | if ( combo.e.mask & IN_MOVED_FROM
|
---|
195 | || combo.e.mask & IN_DELETE)
|
---|
196 | {
|
---|
197 | if (g_ResolvConf == combo.e.name)
|
---|
198 | {
|
---|
199 | /**
|
---|
200 | * Our file has been moved so we should change watching mode.
|
---|
201 | */
|
---|
202 | inotify_rm_watch(a.fileDescriptor(), wd[0]);
|
---|
203 | wd[1] = inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(),
|
---|
204 | IN_MOVED_TO|IN_CREATE);
|
---|
205 | AssertMsg(wd[1] != -1,
|
---|
206 | ("It shouldn't happen, further investigation is needed\n"));
|
---|
207 | }
|
---|
208 | }
|
---|
209 | else
|
---|
210 | {
|
---|
211 | AssertMsg(combo.e.mask & (IN_MOVED_TO|IN_CREATE),
|
---|
212 | ("%RX32 event isn't expected, we are waiting for IN_MOVED|IN_CREATE\n",
|
---|
213 | combo.e.mask));
|
---|
214 | if (g_ResolvConf == combo.e.name)
|
---|
215 | {
|
---|
216 | AssertMsg(wd[0] == -1, ("We haven't removed file watcher first\n"));
|
---|
217 |
|
---|
218 | /* alter folder watcher*/
|
---|
219 | wd[1] = inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(),
|
---|
220 | IN_MOVED_FROM|IN_DELETE);
|
---|
221 | AssertMsg(wd[1] != -1, ("It shouldn't happen.\n"));
|
---|
222 |
|
---|
223 | wd[0] = inotify_add_watch(a.fileDescriptor(),
|
---|
224 | g_ResolvConfFullPath.c_str(),
|
---|
225 | IN_CLOSE_WRITE | IN_DELETE_SELF);
|
---|
226 | AssertMsg(wd[0] != -1, ("Adding watcher to file (%s) has been failed!\n",
|
---|
227 | g_ResolvConfFullPath.c_str()));
|
---|
228 |
|
---|
229 | /* Notify our listeners */
|
---|
230 | readResolvConf();
|
---|
231 | }
|
---|
232 | }
|
---|
233 | }
|
---|
234 | else
|
---|
235 | {
|
---|
236 | /* It shouldn't happen */
|
---|
237 | AssertMsgFailed(("Shouldn't happen! Please debug me!"));
|
---|
238 | }
|
---|
239 | }
|
---|
240 | }
|
---|
241 | }
|
---|
242 |
|
---|