1 | /* $Id: HostDnsServiceLinux.cpp 54662 2015-03-06 05:29:44Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * Linux specific DNS information fetching.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2013-2014 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/err.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 | #include <sys/inotify.h>
|
---|
38 | #include <sys/types.h>
|
---|
39 | #include <sys/socket.h>
|
---|
40 |
|
---|
41 | #include <string>
|
---|
42 | #include <vector>
|
---|
43 | #include "../HostDnsService.h"
|
---|
44 |
|
---|
45 |
|
---|
46 | static int g_DnsMonitorStop[2];
|
---|
47 |
|
---|
48 | static const std::string g_EtcFolder = "/etc";
|
---|
49 | static const std::string g_ResolvConf = "resolv.conf";
|
---|
50 | static const std::string g_ResolvConfFullPath = "/etc/resolv.conf";
|
---|
51 |
|
---|
52 | class FileDescriptor
|
---|
53 | {
|
---|
54 | public:
|
---|
55 | FileDescriptor(int d = -1):fd(d){}
|
---|
56 |
|
---|
57 | virtual ~FileDescriptor() {
|
---|
58 | if (fd != -1)
|
---|
59 | close(fd);
|
---|
60 | }
|
---|
61 |
|
---|
62 | int fileDescriptor() const {return fd;}
|
---|
63 |
|
---|
64 | protected:
|
---|
65 | int fd;
|
---|
66 | };
|
---|
67 |
|
---|
68 |
|
---|
69 | class AutoNotify:public FileDescriptor
|
---|
70 | {
|
---|
71 | public:
|
---|
72 | AutoNotify()
|
---|
73 | {
|
---|
74 | FileDescriptor::fd = inotify_init();
|
---|
75 | AssertReturnVoid(FileDescriptor::fd != -1);
|
---|
76 | }
|
---|
77 | };
|
---|
78 |
|
---|
79 | struct InotifyEventWithName
|
---|
80 | {
|
---|
81 | struct inotify_event e;
|
---|
82 | char name[NAME_MAX];
|
---|
83 | };
|
---|
84 |
|
---|
85 | HostDnsServiceLinux::~HostDnsServiceLinux()
|
---|
86 | {
|
---|
87 | monitorThreadShutdown();
|
---|
88 | }
|
---|
89 |
|
---|
90 |
|
---|
91 | int HostDnsServiceLinux::monitorWorker()
|
---|
92 | {
|
---|
93 |
|
---|
94 | AutoNotify a;
|
---|
95 |
|
---|
96 | int rc = socketpair(AF_LOCAL, SOCK_DGRAM, 0, g_DnsMonitorStop);
|
---|
97 | AssertMsgReturn(rc == 0, ("socketpair: failed (%d: %s)\n", errno, strerror(errno)), E_FAIL);
|
---|
98 |
|
---|
99 | FileDescriptor stopper0(g_DnsMonitorStop[0]);
|
---|
100 | FileDescriptor stopper1(g_DnsMonitorStop[1]);
|
---|
101 |
|
---|
102 | pollfd polls[2];
|
---|
103 | RT_ZERO(polls);
|
---|
104 |
|
---|
105 | polls[0].fd = a.fileDescriptor();
|
---|
106 | polls[0].events = POLLIN;
|
---|
107 |
|
---|
108 | polls[1].fd = g_DnsMonitorStop[1];
|
---|
109 | polls[1].events = POLLIN;
|
---|
110 |
|
---|
111 | monitorThreadInitializationDone();
|
---|
112 |
|
---|
113 | int wd[2];
|
---|
114 | wd[0] = wd[1] = -1;
|
---|
115 | /* inotify inialization */
|
---|
116 | wd[0] = inotify_add_watch(a.fileDescriptor(),
|
---|
117 | g_ResolvConfFullPath.c_str(), IN_CLOSE_WRITE|IN_DELETE_SELF);
|
---|
118 |
|
---|
119 | /**
|
---|
120 | * If /etc/resolv.conf exists we want to listen for movements: because
|
---|
121 | * # mv /etc/resolv.conf ...
|
---|
122 | * won't arm IN_DELETE_SELF on wd[0] instead it will fire IN_MOVE_FROM on wd[1].
|
---|
123 | *
|
---|
124 | * Because on some distributions /etc/resolv.conf is link, wd[0] can't detect deletion,
|
---|
125 | * it's recognizible on directory level (wd[1]) only.
|
---|
126 | */
|
---|
127 | wd[1] = inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(),
|
---|
128 | wd[0] == -1 ? IN_MOVED_TO|IN_CREATE : IN_MOVED_FROM|IN_DELETE);
|
---|
129 |
|
---|
130 | struct InotifyEventWithName combo;
|
---|
131 | while(true)
|
---|
132 | {
|
---|
133 | rc = poll(polls, 2, -1);
|
---|
134 | if (rc == -1)
|
---|
135 | continue;
|
---|
136 |
|
---|
137 | AssertMsgReturn( ((polls[0].revents & (POLLERR|POLLNVAL)) == 0)
|
---|
138 | && ((polls[1].revents & (POLLERR|POLLNVAL)) == 0),
|
---|
139 | ("Debug Me"), VERR_INTERNAL_ERROR);
|
---|
140 |
|
---|
141 | if (polls[1].revents & POLLIN)
|
---|
142 | return VINF_SUCCESS; /* time to shutdown */
|
---|
143 |
|
---|
144 | if (polls[0].revents & POLLIN)
|
---|
145 | {
|
---|
146 | RT_ZERO(combo);
|
---|
147 | ssize_t r = read(polls[0].fd, static_cast<void *>(&combo), sizeof(combo));
|
---|
148 | NOREF(r);
|
---|
149 |
|
---|
150 | if (combo.e.wd == wd[0])
|
---|
151 | {
|
---|
152 | if (combo.e.mask & IN_CLOSE_WRITE)
|
---|
153 | {
|
---|
154 | readResolvConf();
|
---|
155 | }
|
---|
156 | else if (combo.e.mask & IN_DELETE_SELF)
|
---|
157 | {
|
---|
158 | inotify_rm_watch(a.fileDescriptor(), wd[0]); /* removes file watcher */
|
---|
159 | inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(),
|
---|
160 | IN_MOVED_TO|IN_CREATE); /* alter folder watcher */
|
---|
161 | }
|
---|
162 | else if (combo.e.mask & IN_IGNORED)
|
---|
163 | {
|
---|
164 | wd[0] = -1; /* we want receive any events on this watch */
|
---|
165 | }
|
---|
166 | else
|
---|
167 | {
|
---|
168 | /**
|
---|
169 | * It shouldn't happen, in release we will just ignore in debug
|
---|
170 | * we will have to chance to look at into inotify_event
|
---|
171 | */
|
---|
172 | AssertMsgFailed(("Debug Me!!!"));
|
---|
173 | }
|
---|
174 | }
|
---|
175 | else if (combo.e.wd == wd[1])
|
---|
176 | {
|
---|
177 | if ( combo.e.mask & IN_MOVED_FROM
|
---|
178 | || combo.e.mask & IN_DELETE)
|
---|
179 | {
|
---|
180 | if (g_ResolvConf == combo.e.name)
|
---|
181 | {
|
---|
182 | /**
|
---|
183 | * Our file has been moved so we should change watching mode.
|
---|
184 | */
|
---|
185 | inotify_rm_watch(a.fileDescriptor(), wd[0]);
|
---|
186 | wd[1] = inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(),
|
---|
187 | IN_MOVED_TO|IN_CREATE);
|
---|
188 | AssertMsg(wd[1] != -1,
|
---|
189 | ("It shouldn't happen, further investigation is needed\n"));
|
---|
190 | }
|
---|
191 | }
|
---|
192 | else
|
---|
193 | {
|
---|
194 | AssertMsg(combo.e.mask & (IN_MOVED_TO|IN_CREATE),
|
---|
195 | ("%RX32 event isn't expected, we are waiting for IN_MOVED|IN_CREATE\n",
|
---|
196 | combo.e.mask));
|
---|
197 | if (g_ResolvConf == combo.e.name)
|
---|
198 | {
|
---|
199 | AssertMsg(wd[0] == -1, ("We haven't removed file watcher first\n"));
|
---|
200 |
|
---|
201 | /* alter folder watcher*/
|
---|
202 | wd[1] = inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(),
|
---|
203 | IN_MOVED_FROM|IN_DELETE);
|
---|
204 | AssertMsg(wd[1] != -1, ("It shouldn't happen.\n"));
|
---|
205 |
|
---|
206 | wd[0] = inotify_add_watch(a.fileDescriptor(),
|
---|
207 | g_ResolvConfFullPath.c_str(),
|
---|
208 | IN_CLOSE_WRITE | IN_DELETE_SELF);
|
---|
209 | AssertMsg(wd[0] != -1, ("Adding watcher to file (%s) has been failed!\n",
|
---|
210 | g_ResolvConfFullPath.c_str()));
|
---|
211 |
|
---|
212 | /* Notify our listeners */
|
---|
213 | readResolvConf();
|
---|
214 | }
|
---|
215 | }
|
---|
216 | }
|
---|
217 | else
|
---|
218 | {
|
---|
219 | /* It shouldn't happen */
|
---|
220 | AssertMsgFailed(("Shouldn't happen! Please debug me!"));
|
---|
221 | }
|
---|
222 | }
|
---|
223 | }
|
---|
224 | }
|
---|
225 |
|
---|
226 |
|
---|
227 | void HostDnsServiceLinux::monitorThreadShutdown()
|
---|
228 | {
|
---|
229 | send(g_DnsMonitorStop[0], "", 1, 0);
|
---|
230 | }
|
---|