QHostInfo: adapt to glibc 2.26 no longer requiring us to res_init

From the announcement[1]
  - The GNU C Library will now detect when /etc/resolv.conf has been
    modified and reload the changed configuration.  The new resolver
    option “no-reload” (RES_NORELOAD) disables this behavior.

Since glibc can do it, we don't have to call res_init before every
single getaddrinfo() call. And since we don't need to call res_init, we
don't need to load libresolv.so at all, until we need res_ninit.

We won't do it even if the user configured "no-reload" in
/etc/resolv.conf or RES_NORELOAD in the res variable -- let's assume
that it is the intent, like when /etc/resolv.conf is known to never
change.

[1] https://sourceware.org/ml/libc-alpha/2017-08/msg00010.html

Change-Id: I3868166e5efc45538544fffd14d738d40c375fd1
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
Thiago Macieira 2017-08-02 20:38:04 -07:00
parent 3ad8295451
commit 11604b5366

View File

@ -83,6 +83,11 @@ QT_BEGIN_NAMESPACE
# define Q_ADDRCONFIG AI_ADDRCONFIG # define Q_ADDRCONFIG AI_ADDRCONFIG
#endif #endif
enum LibResolvFeature {
NeedResInit,
NeedResNInit
};
typedef struct __res_state *res_state_ptr; typedef struct __res_state *res_state_ptr;
typedef int (*res_init_proto)(void); typedef int (*res_init_proto)(void);
@ -93,9 +98,29 @@ typedef void (*res_nclose_proto)(res_state_ptr);
static res_nclose_proto local_res_nclose = 0; static res_nclose_proto local_res_nclose = 0;
static res_state_ptr local_res = 0; static res_state_ptr local_res = 0;
static bool resolveLibraryInternal()
{
#if QT_CONFIG(library) && !defined(Q_OS_QNX) #if QT_CONFIG(library) && !defined(Q_OS_QNX)
namespace {
struct LibResolv
{
enum {
#ifdef RES_NORELOAD
// If RES_NORELOAD is defined, then the libc is capable of watching
// /etc/resolv.conf for changes and reloading as necessary. So accept
// whatever is configured.
ReinitNecessary = false
#else
ReinitNecessary = true
#endif
};
QLibrary lib;
LibResolv();
~LibResolv() { lib.unload(); }
};
}
LibResolv::LibResolv()
{
QLibrary lib; QLibrary lib;
#ifdef LIBRESOLV_SO #ifdef LIBRESOLV_SO
lib.setFileName(QStringLiteral(LIBRESOLV_SO)); lib.setFileName(QStringLiteral(LIBRESOLV_SO));
@ -104,32 +129,45 @@ static bool resolveLibraryInternal()
{ {
lib.setFileName(QLatin1String("resolv")); lib.setFileName(QLatin1String("resolv"));
if (!lib.load()) if (!lib.load())
return false; return;
} }
local_res_init = res_init_proto(lib.resolve("__res_init")); // res_ninit is required for localDomainName()
if (!local_res_init)
local_res_init = res_init_proto(lib.resolve("res_init"));
local_res_ninit = res_ninit_proto(lib.resolve("__res_ninit")); local_res_ninit = res_ninit_proto(lib.resolve("__res_ninit"));
if (!local_res_ninit) if (!local_res_ninit)
local_res_ninit = res_ninit_proto(lib.resolve("res_ninit")); local_res_ninit = res_ninit_proto(lib.resolve("res_ninit"));
if (local_res_ninit) {
if (!local_res_ninit) { // we must now find res_nclose
// if we can't get a thread-safe context, we have to use the global _res state
local_res = res_state_ptr(lib.resolve("_res"));
} else {
local_res_nclose = res_nclose_proto(lib.resolve("res_nclose")); local_res_nclose = res_nclose_proto(lib.resolve("res_nclose"));
if (!local_res_nclose) if (!local_res_nclose)
local_res_nclose = res_nclose_proto(lib.resolve("__res_nclose")); local_res_nclose = res_nclose_proto(lib.resolve("__res_nclose"));
if (!local_res_nclose) if (!local_res_nclose)
local_res_ninit = 0; local_res_ninit = nullptr;
} }
#endif
return true; if (ReinitNecessary || !local_res_ninit) {
local_res_init = res_init_proto(lib.resolve("__res_init"));
if (!local_res_init)
local_res_init = res_init_proto(lib.resolve("res_init"));
if (local_res_init && !local_res_ninit) {
// if we can't get a thread-safe context, we have to use the global _res state
local_res = res_state_ptr(lib.resolve("_res"));
}
}
} }
Q_GLOBAL_STATIC_WITH_ARGS(bool, resolveLibrary, (resolveLibraryInternal())) Q_GLOBAL_STATIC(LibResolv, libResolv)
static void resolveLibrary(LibResolvFeature f)
{
if (LibResolv::ReinitNecessary || f == NeedResNInit)
libResolv();
}
#else // QT_CONFIG(library) || Q_OS_QNX
static void resolveLibrary(LibResolvFeature)
{
}
#endif // QT_CONFIG(library) || Q_OS_QNX
QHostInfo QHostInfoAgent::fromName(const QString &hostName) QHostInfo QHostInfoAgent::fromName(const QString &hostName)
{ {
@ -141,7 +179,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
#endif #endif
// Load res_init on demand. // Load res_init on demand.
resolveLibrary(); resolveLibrary(NeedResInit);
// If res_init is available, poll it. // If res_init is available, poll it.
if (local_res_init) if (local_res_init)
@ -320,7 +358,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
QString QHostInfo::localDomainName() QString QHostInfo::localDomainName()
{ {
#if !defined(Q_OS_VXWORKS) && !defined(Q_OS_ANDROID) #if !defined(Q_OS_VXWORKS) && !defined(Q_OS_ANDROID)
resolveLibrary(); resolveLibrary(NeedResNInit);
if (local_res_ninit) { if (local_res_ninit) {
// using thread-safe version // using thread-safe version
res_state_ptr state = res_state_ptr(malloc(sizeof(*state))); res_state_ptr state = res_state_ptr(malloc(sizeof(*state)));