diff options
Diffstat (limited to 'src/VBox/Main/src-server/linux/HostDnsServiceLinux.cpp')
-rw-r--r-- | src/VBox/Main/src-server/linux/HostDnsServiceLinux.cpp | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/linux/HostDnsServiceLinux.cpp b/src/VBox/Main/src-server/linux/HostDnsServiceLinux.cpp new file mode 100644 index 00000000..736b19b6 --- /dev/null +++ b/src/VBox/Main/src-server/linux/HostDnsServiceLinux.cpp @@ -0,0 +1,252 @@ +/* $Id: HostDnsServiceLinux.cpp $ */ +/** @file + * Linux specific DNS information fetching. + */ + +/* + * Copyright (C) 2013-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include <iprt/initterm.h> +#include <iprt/file.h> +#include <iprt/log.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/semaphore.h> +#include <iprt/thread.h> + +#include <errno.h> +#include <poll.h> +#include <string.h> +#include <unistd.h> + +#include <fcntl.h> + +#include <linux/limits.h> + +/* Workaround for <sys/cdef.h> defining __flexarr to [] which beats us in + * struct inotify_event (char name __flexarr). */ +#include <sys/cdefs.h> +#undef __flexarr +#define __flexarr [0] +#include <sys/inotify.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <iprt/sanitized/string> +#include <vector> +#include "../HostDnsService.h" + + +static int g_DnsMonitorStop[2]; + +static const std::string g_EtcFolder = "/etc"; +static const std::string g_ResolvConf = "resolv.conf"; +static const std::string g_ResolvConfFullPath = "/etc/resolv.conf"; + +class FileDescriptor +{ + public: + FileDescriptor(int d = -1):fd(d){} + + virtual ~FileDescriptor() { + if (fd != -1) + close(fd); + } + + int fileDescriptor() const {return fd;} + + protected: + int fd; +}; + + +class AutoNotify:public FileDescriptor +{ + public: + AutoNotify() + { + FileDescriptor::fd = inotify_init(); + AssertReturnVoid(FileDescriptor::fd != -1); + } +}; + +struct InotifyEventWithName +{ + struct inotify_event e; + char name[NAME_MAX]; +}; + +HostDnsServiceLinux::~HostDnsServiceLinux() +{ +} + +HRESULT HostDnsServiceLinux::init(HostDnsMonitorProxy *pProxy) +{ + return HostDnsServiceResolvConf::init(pProxy, "/etc/resolv.conf"); +} + +int HostDnsServiceLinux::monitorThreadShutdown(RTMSINTERVAL uTimeoutMs) +{ + RT_NOREF(uTimeoutMs); + + send(g_DnsMonitorStop[0], "", 1, 0); + + /** @todo r=andy Do we have to wait for something here? Can this fail? */ + return VINF_SUCCESS; +} + +int HostDnsServiceLinux::monitorThreadProc(void) +{ + AutoNotify a; + + int rc = socketpair(AF_LOCAL, SOCK_DGRAM, 0, g_DnsMonitorStop); + AssertMsgReturn(rc == 0, ("socketpair: failed (%d: %s)\n", errno, strerror(errno)), E_FAIL); + + FileDescriptor stopper0(g_DnsMonitorStop[0]); + FileDescriptor stopper1(g_DnsMonitorStop[1]); + + pollfd polls[2]; + RT_ZERO(polls); + + polls[0].fd = a.fileDescriptor(); + polls[0].events = POLLIN; + + polls[1].fd = g_DnsMonitorStop[1]; + polls[1].events = POLLIN; + + onMonitorThreadInitDone(); + + int wd[2]; + wd[0] = wd[1] = -1; + /* inotify inialization */ + wd[0] = inotify_add_watch(a.fileDescriptor(), + g_ResolvConfFullPath.c_str(), IN_CLOSE_WRITE|IN_DELETE_SELF); + + /** + * If /etc/resolv.conf exists we want to listen for movements: because + * # mv /etc/resolv.conf ... + * won't arm IN_DELETE_SELF on wd[0] instead it will fire IN_MOVE_FROM on wd[1]. + * + * Because on some distributions /etc/resolv.conf is link, wd[0] can't detect deletion, + * it's recognizible on directory level (wd[1]) only. + */ + wd[1] = inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(), + wd[0] == -1 ? IN_MOVED_TO|IN_CREATE : IN_MOVED_FROM|IN_DELETE); + + struct InotifyEventWithName combo; + while(true) + { + rc = poll(polls, 2, -1); + if (rc == -1) + continue; + + AssertMsgReturn( ((polls[0].revents & (POLLERR|POLLNVAL)) == 0) + && ((polls[1].revents & (POLLERR|POLLNVAL)) == 0), + ("Debug Me"), VERR_INTERNAL_ERROR); + + if (polls[1].revents & POLLIN) + return VINF_SUCCESS; /* time to shutdown */ + + if (polls[0].revents & POLLIN) + { + RT_ZERO(combo); + ssize_t r = read(polls[0].fd, static_cast<void *>(&combo), sizeof(combo)); + RT_NOREF(r); + + if (combo.e.wd == wd[0]) + { + if (combo.e.mask & IN_CLOSE_WRITE) + { + readResolvConf(); + } + else if (combo.e.mask & IN_DELETE_SELF) + { + inotify_rm_watch(a.fileDescriptor(), wd[0]); /* removes file watcher */ + inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(), + IN_MOVED_TO|IN_CREATE); /* alter folder watcher */ + } + else if (combo.e.mask & IN_IGNORED) + { + wd[0] = -1; /* we want receive any events on this watch */ + } + else + { + /** + * It shouldn't happen, in release we will just ignore in debug + * we will have to chance to look at into inotify_event + */ + AssertMsgFailed(("Debug Me!!!")); + } + } + else if (combo.e.wd == wd[1]) + { + if ( combo.e.mask & IN_MOVED_FROM + || combo.e.mask & IN_DELETE) + { + if (g_ResolvConf == combo.e.name) + { + /** + * Our file has been moved so we should change watching mode. + */ + inotify_rm_watch(a.fileDescriptor(), wd[0]); + wd[1] = inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(), + IN_MOVED_TO|IN_CREATE); + AssertMsg(wd[1] != -1, + ("It shouldn't happen, further investigation is needed\n")); + } + } + else + { + AssertMsg(combo.e.mask & (IN_MOVED_TO|IN_CREATE), + ("%RX32 event isn't expected, we are waiting for IN_MOVED|IN_CREATE\n", + combo.e.mask)); + if (g_ResolvConf == combo.e.name) + { + AssertMsg(wd[0] == -1, ("We haven't removed file watcher first\n")); + + /* alter folder watcher*/ + wd[1] = inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(), + IN_MOVED_FROM|IN_DELETE); + AssertMsg(wd[1] != -1, ("It shouldn't happen.\n")); + + wd[0] = inotify_add_watch(a.fileDescriptor(), + g_ResolvConfFullPath.c_str(), + IN_CLOSE_WRITE | IN_DELETE_SELF); + AssertMsg(wd[0] != -1, ("Adding watcher to file (%s) has been failed!\n", + g_ResolvConfFullPath.c_str())); + + /* Notify our listeners */ + readResolvConf(); + } + } + } + else + { + /* It shouldn't happen */ + AssertMsgFailed(("Shouldn't happen! Please debug me!")); + } + } + } +} + |