1 | /* $Id: HostDnsServiceLinux.cpp 98068 2023-01-13 04:14:20Z 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.215389.xyz.
|
---|
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 |
|
---|
29 | /*********************************************************************************************************************************
|
---|
30 | * Header Files *
|
---|
31 | *********************************************************************************************************************************/
|
---|
32 | #define LOG_GROUP LOG_GROUP_MAIN_HOST
|
---|
33 | #include <iprt/assert.h>
|
---|
34 | #include <iprt/errcore.h>
|
---|
35 | #include <iprt/initterm.h>
|
---|
36 | #include <iprt/file.h>
|
---|
37 | #include <VBox/log.h>
|
---|
38 | #include <iprt/stream.h>
|
---|
39 | #include <iprt/string.h>
|
---|
40 | #include <iprt/semaphore.h>
|
---|
41 | #include <iprt/thread.h>
|
---|
42 |
|
---|
43 | #include <errno.h>
|
---|
44 | #include <poll.h>
|
---|
45 | #include <string.h>
|
---|
46 | #include <unistd.h>
|
---|
47 |
|
---|
48 | #include <fcntl.h>
|
---|
49 |
|
---|
50 | #include <linux/limits.h>
|
---|
51 |
|
---|
52 | /* Workaround for <sys/cdef.h> defining __flexarr to [] which beats us in
|
---|
53 | * struct inotify_event (char name __flexarr). */
|
---|
54 | #include <sys/cdefs.h>
|
---|
55 | #undef __flexarr
|
---|
56 | #define __flexarr [0]
|
---|
57 | #include <sys/inotify.h>
|
---|
58 | #include <sys/types.h>
|
---|
59 | #include <sys/socket.h>
|
---|
60 | #include <sys/stat.h>
|
---|
61 |
|
---|
62 | #include "../HostDnsService.h"
|
---|
63 |
|
---|
64 |
|
---|
65 | /*********************************************************************************************************************************
|
---|
66 | * Global Variables *
|
---|
67 | *********************************************************************************************************************************/
|
---|
68 | static int g_DnsMonitorStop[2];
|
---|
69 |
|
---|
70 | static const char g_szEtcFolder[] = "/etc";
|
---|
71 | static const char g_szResolvConfPath[] = "/etc/resolv.conf";
|
---|
72 | static const char g_szResolvConfFilename[] = "resolv.conf";
|
---|
73 |
|
---|
74 |
|
---|
75 | class FileDescriptor
|
---|
76 | {
|
---|
77 | public:
|
---|
78 | FileDescriptor(int d = -1)
|
---|
79 | : fd(d)
|
---|
80 | {}
|
---|
81 |
|
---|
82 | virtual ~FileDescriptor() {
|
---|
83 | if (fd != -1)
|
---|
84 | close(fd);
|
---|
85 | }
|
---|
86 |
|
---|
87 | int fileDescriptor() const {return fd;}
|
---|
88 |
|
---|
89 | protected:
|
---|
90 | int fd;
|
---|
91 | };
|
---|
92 |
|
---|
93 |
|
---|
94 | class AutoNotify : public FileDescriptor
|
---|
95 | {
|
---|
96 | public:
|
---|
97 | AutoNotify()
|
---|
98 | {
|
---|
99 | FileDescriptor::fd = inotify_init();
|
---|
100 | AssertReturnVoid(FileDescriptor::fd != -1);
|
---|
101 | }
|
---|
102 | };
|
---|
103 |
|
---|
104 | HostDnsServiceLinux::~HostDnsServiceLinux()
|
---|
105 | {
|
---|
106 | }
|
---|
107 |
|
---|
108 | HRESULT HostDnsServiceLinux::init(HostDnsMonitorProxy *pProxy)
|
---|
109 | {
|
---|
110 | return HostDnsServiceResolvConf::init(pProxy, "/etc/resolv.conf");
|
---|
111 | }
|
---|
112 |
|
---|
113 | int HostDnsServiceLinux::monitorThreadShutdown(RTMSINTERVAL uTimeoutMs)
|
---|
114 | {
|
---|
115 | RT_NOREF(uTimeoutMs);
|
---|
116 |
|
---|
117 | send(g_DnsMonitorStop[0], "", 1, 0);
|
---|
118 |
|
---|
119 | /** @todo r=andy Do we have to wait for something here? Can this fail? */
|
---|
120 | return VINF_SUCCESS;
|
---|
121 | }
|
---|
122 |
|
---|
123 | #ifdef LOG_ENABLED
|
---|
124 | /**
|
---|
125 | * Format the notifcation event mask into a buffer for logging purposes.
|
---|
126 | */
|
---|
127 | static const char *InotifyMaskToStr(char *psz, size_t cb, uint32_t fMask)
|
---|
128 | {
|
---|
129 | static struct { const char *pszName; uint32_t cchName, fFlag; } const s_aFlags[] =
|
---|
130 | {
|
---|
131 | # define ENTRY(fFlag) { #fFlag, sizeof(#fFlag) - 1, fFlag }
|
---|
132 | ENTRY(IN_ACCESS),
|
---|
133 | ENTRY(IN_MODIFY),
|
---|
134 | ENTRY(IN_ATTRIB),
|
---|
135 | ENTRY(IN_CLOSE_WRITE),
|
---|
136 | ENTRY(IN_CLOSE_NOWRITE),
|
---|
137 | ENTRY(IN_OPEN),
|
---|
138 | ENTRY(IN_MOVED_FROM),
|
---|
139 | ENTRY(IN_MOVED_TO),
|
---|
140 | ENTRY(IN_CREATE),
|
---|
141 | ENTRY(IN_DELETE),
|
---|
142 | ENTRY(IN_DELETE_SELF),
|
---|
143 | ENTRY(IN_MOVE_SELF),
|
---|
144 | ENTRY(IN_Q_OVERFLOW),
|
---|
145 | ENTRY(IN_IGNORED),
|
---|
146 | ENTRY(IN_UNMOUNT),
|
---|
147 | ENTRY(IN_ISDIR),
|
---|
148 | };
|
---|
149 | size_t offDst = 0;
|
---|
150 | for (size_t i = 0; i < RT_ELEMENTS(s_aFlags); i++)
|
---|
151 | if (fMask & s_aFlags[i].fFlag)
|
---|
152 | {
|
---|
153 | if (offDst && offDst < cb)
|
---|
154 | psz[offDst++] = ' ';
|
---|
155 | if (offDst < cb)
|
---|
156 | {
|
---|
157 | size_t cbToCopy = RT_MIN(s_aFlags[i].cchName, cb - offDst);
|
---|
158 | memcpy(&psz[offDst], s_aFlags[i].pszName, cbToCopy);
|
---|
159 | offDst += cbToCopy;
|
---|
160 | }
|
---|
161 |
|
---|
162 | fMask &= ~s_aFlags[i].fFlag;
|
---|
163 | if (!fMask)
|
---|
164 | break;
|
---|
165 | }
|
---|
166 | if (fMask && offDst < cb)
|
---|
167 | RTStrPrintf(&psz[offDst], cb - offDst, offDst ? " %#x" : "%#x", fMask);
|
---|
168 | else
|
---|
169 | psz[RT_MIN(offDst, cb - 1)] = '\0';
|
---|
170 | return psz;
|
---|
171 | }
|
---|
172 | #endif
|
---|
173 |
|
---|
174 |
|
---|
175 | /**
|
---|
176 | * Helper for HostDnsServiceLinux::monitorThreadProc.
|
---|
177 | */
|
---|
178 | static int monitorSymlinkedDir(int iInotifyFd, char szRealResolvConf[PATH_MAX], size_t *poffFilename)
|
---|
179 | {
|
---|
180 | RT_BZERO(szRealResolvConf, PATH_MAX);
|
---|
181 |
|
---|
182 | /* Check that it's a symlink first. */
|
---|
183 | struct stat st;
|
---|
184 | if ( lstat(g_szResolvConfPath, &st) >= 0
|
---|
185 | && S_ISLNK(st.st_mode))
|
---|
186 | {
|
---|
187 | /* If realpath fails, the file must've been deleted while we were busy: */
|
---|
188 | if ( realpath(g_szResolvConfPath, szRealResolvConf)
|
---|
189 | && strchr(szRealResolvConf, '/'))
|
---|
190 | {
|
---|
191 | /* Cut of the filename part. We only need that for deletion checks and such. */
|
---|
192 | size_t const offFilename = strrchr(szRealResolvConf, '/') - &szRealResolvConf[0];
|
---|
193 | *poffFilename = offFilename + 1;
|
---|
194 | szRealResolvConf[offFilename] = '\0';
|
---|
195 |
|
---|
196 | /* Try set up directory monitoring. (File monitoring is done via the symlink.) */
|
---|
197 | return inotify_add_watch(iInotifyFd, szRealResolvConf, IN_MOVE | IN_CREATE | IN_DELETE);
|
---|
198 | }
|
---|
199 | }
|
---|
200 |
|
---|
201 | *poffFilename = 0;
|
---|
202 | szRealResolvConf[0] = '\0';
|
---|
203 | return -1;
|
---|
204 | }
|
---|
205 |
|
---|
206 | int HostDnsServiceLinux::monitorThreadProc(void)
|
---|
207 | {
|
---|
208 | /*
|
---|
209 | * inotify initialization.
|
---|
210 | *
|
---|
211 | * The order here helps keep the descriptor values stable.
|
---|
212 | *
|
---|
213 | * Note! Ignoring failures here is safe, because poll will ignore entires
|
---|
214 | * with negative fd values.
|
---|
215 | */
|
---|
216 | AutoNotify Notify;
|
---|
217 |
|
---|
218 | /* Monitor the /etc directory so we can detect moves, unliking and creations
|
---|
219 | involving /etc/resolv.conf: */
|
---|
220 | int const iWdDir = inotify_add_watch(Notify.fileDescriptor(), g_szEtcFolder, IN_MOVE | IN_CREATE | IN_DELETE);
|
---|
221 |
|
---|
222 | /* In case g_szResolvConfPath is a symbolic link, monitor the target directory
|
---|
223 | too for changes to what it links to. */
|
---|
224 | char szRealResolvConf[PATH_MAX];
|
---|
225 | size_t offRealResolvConfName = 0;
|
---|
226 | int iWdSymDir = ::monitorSymlinkedDir(Notify.fileDescriptor(), szRealResolvConf, &offRealResolvConfName);
|
---|
227 |
|
---|
228 | /* Monitor the resolv.conf itself if it exists, following all symlinks. */
|
---|
229 | int iWdFile = inotify_add_watch(Notify.fileDescriptor(), g_szResolvConfPath, IN_CLOSE_WRITE | IN_DELETE_SELF);
|
---|
230 |
|
---|
231 | Log5Func(("iWdDir=%d iWdSymDir=%d iWdFile=%d\n", iWdDir, iWdSymDir, iWdFile));
|
---|
232 |
|
---|
233 | /*
|
---|
234 | * Create a socket pair for signalling shutdown via (see monitorThreadShutdown).
|
---|
235 | */
|
---|
236 | int rc = socketpair(AF_LOCAL, SOCK_DGRAM, 0, g_DnsMonitorStop);
|
---|
237 | AssertMsgReturn(rc == 0, ("socketpair: failed (%d: %s)\n", errno, strerror(errno)), E_FAIL);
|
---|
238 |
|
---|
239 | /* automatic cleanup tricks */
|
---|
240 | FileDescriptor stopper0(g_DnsMonitorStop[0]);
|
---|
241 | FileDescriptor stopper1(g_DnsMonitorStop[1]);
|
---|
242 |
|
---|
243 | /*
|
---|
244 | * poll initialization:
|
---|
245 | */
|
---|
246 | pollfd aFdPolls[2];
|
---|
247 | RT_ZERO(aFdPolls);
|
---|
248 |
|
---|
249 | aFdPolls[0].fd = Notify.fileDescriptor();
|
---|
250 | aFdPolls[0].events = POLLIN;
|
---|
251 |
|
---|
252 | aFdPolls[1].fd = g_DnsMonitorStop[1];
|
---|
253 | aFdPolls[1].events = POLLIN;
|
---|
254 |
|
---|
255 | onMonitorThreadInitDone();
|
---|
256 |
|
---|
257 | /*
|
---|
258 | * The monitoring loop.
|
---|
259 | */
|
---|
260 | for (;;)
|
---|
261 | {
|
---|
262 | /*
|
---|
263 | * Wait for something to happen.
|
---|
264 | */
|
---|
265 | rc = poll(aFdPolls, RT_ELEMENTS(aFdPolls), -1 /*infinite timeout*/);
|
---|
266 | if (rc == -1)
|
---|
267 | {
|
---|
268 | LogRelMax(32, ("HostDnsServiceLinux::monitorThreadProc: poll failed %d: errno=%d\n", rc, errno));
|
---|
269 | RTThreadSleep(1);
|
---|
270 | continue;
|
---|
271 | }
|
---|
272 | Log5Func(("poll returns %d: [0]=%#x [1]=%#x\n", rc, aFdPolls[1].revents, aFdPolls[0].revents));
|
---|
273 |
|
---|
274 | AssertMsgReturn( (aFdPolls[0].revents & (POLLERR | POLLNVAL)) == 0
|
---|
275 | && (aFdPolls[1].revents & (POLLERR | POLLNVAL)) == 0, ("Debug Me"), VERR_INTERNAL_ERROR);
|
---|
276 |
|
---|
277 |
|
---|
278 | /*
|
---|
279 | * Check for shutdown first.
|
---|
280 | */
|
---|
281 | if (aFdPolls[1].revents & POLLIN)
|
---|
282 | return VINF_SUCCESS;
|
---|
283 |
|
---|
284 | if (aFdPolls[0].revents & POLLIN)
|
---|
285 | {
|
---|
286 | /*
|
---|
287 | * Read the notification event.
|
---|
288 | */
|
---|
289 | #define INOTIFY_EVENT_SIZE (RT_UOFFSETOF(struct inotify_event, name))
|
---|
290 | union
|
---|
291 | {
|
---|
292 | uint8_t abBuf[(INOTIFY_EVENT_SIZE * 2 - 1 + NAME_MAX) / INOTIFY_EVENT_SIZE * INOTIFY_EVENT_SIZE * 4];
|
---|
293 | uint64_t uAlignTrick[2];
|
---|
294 | } uEvtBuf;
|
---|
295 |
|
---|
296 | ssize_t cbEvents = read(Notify.fileDescriptor(), &uEvtBuf, sizeof(uEvtBuf));
|
---|
297 | Log5Func(("read(inotify) -> %zd\n", cbEvents));
|
---|
298 | if (cbEvents > 0)
|
---|
299 | Log5(("%.*Rhxd\n", cbEvents, &uEvtBuf));
|
---|
300 |
|
---|
301 | /*
|
---|
302 | * Process the events.
|
---|
303 | *
|
---|
304 | * We'll keep the old watch descriptor number till after we're done
|
---|
305 | * parsing this block of events. Even so, the removal of watches
|
---|
306 | * isn't race free, as they'll get automatically removed when what
|
---|
307 | * is being watched is unliked.
|
---|
308 | */
|
---|
309 | int iWdFileNew = iWdFile;
|
---|
310 | int iWdSymDirNew = iWdSymDir;
|
---|
311 | bool fTryReRead = false;
|
---|
312 | struct inotify_event const *pCurEvt = (struct inotify_event const *)&uEvtBuf;
|
---|
313 | while (cbEvents >= (ssize_t)INOTIFY_EVENT_SIZE)
|
---|
314 | {
|
---|
315 | #ifdef LOG_ENABLED
|
---|
316 | char szTmp[64];
|
---|
317 | if (pCurEvt->len == 0)
|
---|
318 | Log5Func(("event: wd=%#x mask=%#x (%s) cookie=%#x\n", pCurEvt->wd, pCurEvt->mask,
|
---|
319 | InotifyMaskToStr(szTmp, sizeof(szTmp), pCurEvt->mask), pCurEvt->cookie));
|
---|
320 | else
|
---|
321 | Log5Func(("event: wd=%#x mask=%#x (%s) cookie=%#x len=%#x '%s'\n",
|
---|
322 | pCurEvt->wd, pCurEvt->mask, InotifyMaskToStr(szTmp, sizeof(szTmp), pCurEvt->mask),
|
---|
323 | pCurEvt->cookie, pCurEvt->len, pCurEvt->name));
|
---|
324 | #endif
|
---|
325 |
|
---|
326 | /*
|
---|
327 | * The file itself (symlinks followed, remember):
|
---|
328 | */
|
---|
329 | if (pCurEvt->wd == iWdFile)
|
---|
330 | {
|
---|
331 | if (pCurEvt->mask & IN_CLOSE_WRITE)
|
---|
332 | {
|
---|
333 | Log5Func(("file: close-after-write => trigger re-read\n"));
|
---|
334 | fTryReRead = true;
|
---|
335 | }
|
---|
336 | else if (pCurEvt->mask & IN_DELETE_SELF)
|
---|
337 | {
|
---|
338 | Log5Func(("file: deleted self\n"));
|
---|
339 | if (iWdFileNew != -1)
|
---|
340 | {
|
---|
341 | rc = inotify_rm_watch(Notify.fileDescriptor(), iWdFileNew);
|
---|
342 | AssertMsg(rc >= 0, ("%d/%d\n", rc, errno));
|
---|
343 | iWdFileNew = -1;
|
---|
344 | }
|
---|
345 | }
|
---|
346 | else if (pCurEvt->mask & IN_IGNORED)
|
---|
347 | iWdFileNew = -1; /* file deleted */
|
---|
348 | else
|
---|
349 | AssertMsgFailed(("file: mask=%#x\n", pCurEvt->mask));
|
---|
350 | }
|
---|
351 | /*
|
---|
352 | * The /etc directory
|
---|
353 | *
|
---|
354 | * We only care about events relating to the creation, deletion and
|
---|
355 | * renaming of 'resolv.conf'. We'll restablish both the direct file
|
---|
356 | * watching and the watching of any symlinked directory on all of
|
---|
357 | * these events, although for the former we'll delay the re-starting
|
---|
358 | * of the watching till all events have been processed.
|
---|
359 | */
|
---|
360 | else if (pCurEvt->wd == iWdDir)
|
---|
361 | {
|
---|
362 | if ( pCurEvt->len > 0
|
---|
363 | && strcmp(g_szResolvConfFilename, pCurEvt->name) == 0)
|
---|
364 | {
|
---|
365 | if (pCurEvt->mask & (IN_MOVE | IN_CREATE | IN_DELETE))
|
---|
366 | {
|
---|
367 | if (iWdFileNew >= 0)
|
---|
368 | {
|
---|
369 | rc = inotify_rm_watch(Notify.fileDescriptor(), iWdFileNew);
|
---|
370 | Log5Func(("dir: moved / created / deleted: dropped file watch (%d - rc=%d/err=%d)\n",
|
---|
371 | iWdFileNew, rc, errno));
|
---|
372 | iWdFileNew = -1;
|
---|
373 | }
|
---|
374 | if (iWdSymDirNew >= 0)
|
---|
375 | {
|
---|
376 | rc = inotify_rm_watch(Notify.fileDescriptor(), iWdSymDirNew);
|
---|
377 | Log5Func(("dir: moved / created / deleted: dropped symlinked dir watch (%d - %s/%s - rc=%d/err=%d)\n",
|
---|
378 | iWdSymDirNew, szRealResolvConf, &szRealResolvConf[offRealResolvConfName], rc, errno));
|
---|
379 | iWdSymDirNew = -1;
|
---|
380 | offRealResolvConfName = 0;
|
---|
381 | }
|
---|
382 | if (pCurEvt->mask & (IN_MOVED_TO | IN_CREATE))
|
---|
383 | {
|
---|
384 | Log5Func(("dir: moved_to / created: trigger re-read\n"));
|
---|
385 | fTryReRead = true;
|
---|
386 |
|
---|
387 | iWdSymDirNew = ::monitorSymlinkedDir(Notify.fileDescriptor(),
|
---|
388 | szRealResolvConf, &offRealResolvConfName);
|
---|
389 | if (iWdSymDirNew < 0)
|
---|
390 | Log5Func(("dir: moved_to / created: re-stablished symlinked-directory monitoring: iWdSymDir=%d (%s/%s)\n",
|
---|
391 | iWdSymDirNew, szRealResolvConf, &szRealResolvConf[offRealResolvConfName]));
|
---|
392 | }
|
---|
393 | }
|
---|
394 | else
|
---|
395 | AssertMsgFailed(("dir: %#x\n", pCurEvt->mask));
|
---|
396 | }
|
---|
397 | }
|
---|
398 | /*
|
---|
399 | * The directory of a symlinked resolv.conf.
|
---|
400 | *
|
---|
401 | * Where we only care when the symlink target is created, moved_to,
|
---|
402 | * deleted or moved_from - i.e. a minimal version of the /etc event
|
---|
403 | * processing above.
|
---|
404 | *
|
---|
405 | * Note! Since we re-statablish monitoring above, szRealResolvConf
|
---|
406 | * might not match the event we're processing. Fortunately,
|
---|
407 | * this shouldn't be important except for debug logging.
|
---|
408 | */
|
---|
409 | else if (pCurEvt->wd == iWdSymDir)
|
---|
410 | {
|
---|
411 | if ( pCurEvt->len > 0
|
---|
412 | && offRealResolvConfName > 0
|
---|
413 | && strcmp(&szRealResolvConf[offRealResolvConfName], pCurEvt->name) == 0)
|
---|
414 | {
|
---|
415 | if (iWdFileNew >= 0)
|
---|
416 | {
|
---|
417 | rc = inotify_rm_watch(Notify.fileDescriptor(), iWdFileNew);
|
---|
418 | Log5Func(("symdir: moved / created / deleted: drop file watch (%d - rc=%d/err=%d)\n",
|
---|
419 | iWdFileNew, rc, errno));
|
---|
420 | iWdFileNew = -1;
|
---|
421 | }
|
---|
422 | if (pCurEvt->mask & (IN_MOVED_TO | IN_CREATE))
|
---|
423 | {
|
---|
424 | Log5Func(("symdir: moved_to / created: trigger re-read\n"));
|
---|
425 | fTryReRead = true;
|
---|
426 | }
|
---|
427 | }
|
---|
428 | }
|
---|
429 | /* We can get here it seems if our inotify_rm_watch calls above takes
|
---|
430 | place after new events relating to the two descriptors happens. */
|
---|
431 | else
|
---|
432 | Log5Func(("Unknown (obsoleted) wd value: %d (mask=%#x cookie=%#x len=%#x)\n",
|
---|
433 | pCurEvt->wd, pCurEvt->mask, pCurEvt->cookie, pCurEvt->len));
|
---|
434 |
|
---|
435 | /* advance to the next event */
|
---|
436 | Assert(pCurEvt->len / INOTIFY_EVENT_SIZE * INOTIFY_EVENT_SIZE == pCurEvt->len);
|
---|
437 | size_t const cbCurEvt = INOTIFY_EVENT_SIZE + pCurEvt->len;
|
---|
438 | pCurEvt = (struct inotify_event const *)((uintptr_t)pCurEvt + cbCurEvt);
|
---|
439 | cbEvents -= cbCurEvt;
|
---|
440 | }
|
---|
441 |
|
---|
442 | /*
|
---|
443 | * Commit the new watch descriptor numbers now that we're
|
---|
444 | * done processing event using the old ones.
|
---|
445 | */
|
---|
446 | iWdFile = iWdFileNew;
|
---|
447 | iWdSymDir = iWdSymDirNew;
|
---|
448 |
|
---|
449 | /*
|
---|
450 | * If the resolv.conf watch descriptor is -1, try restablish it here.
|
---|
451 | */
|
---|
452 | if (iWdFile == -1)
|
---|
453 | {
|
---|
454 | iWdFile = inotify_add_watch(Notify.fileDescriptor(), g_szResolvConfPath, IN_CLOSE_WRITE | IN_DELETE_SELF);
|
---|
455 | if (iWdFile >= 0)
|
---|
456 | {
|
---|
457 | Log5Func(("Re-established file watcher: iWdFile=%d\n", iWdFile));
|
---|
458 | fTryReRead = true;
|
---|
459 | }
|
---|
460 | }
|
---|
461 |
|
---|
462 | /*
|
---|
463 | * If any of the events indicate that we should re-read the file, we
|
---|
464 | * do so now. Should reduce number of unnecessary re-reads.
|
---|
465 | */
|
---|
466 | if (fTryReRead)
|
---|
467 | {
|
---|
468 | Log5Func(("Calling readResolvConf()...\n"));
|
---|
469 | readResolvConf();
|
---|
470 | }
|
---|
471 | }
|
---|
472 | }
|
---|
473 | }
|
---|
474 |
|
---|