Merge "Add POSIX getsubopt(3)."
diff --git a/libc/Android.mk b/libc/Android.mk
index 6d358cc..aed7087 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -174,12 +174,17 @@
bionic/sigdelset.cpp \
bionic/sigemptyset.cpp \
bionic/sigfillset.cpp \
+ bionic/sighold.cpp \
+ bionic/sigignore.cpp \
bionic/sigismember.cpp \
bionic/signal.cpp \
bionic/signalfd.cpp \
+ bionic/sigpause.cpp \
bionic/sigpending.cpp \
bionic/sigprocmask.cpp \
bionic/sigqueue.cpp \
+ bionic/sigrelse.cpp \
+ bionic/sigset.cpp \
bionic/sigsuspend.cpp \
bionic/sigtimedwait.cpp \
bionic/sigwait.cpp \
diff --git a/libc/bionic/bionic_time_conversions.cpp b/libc/bionic/bionic_time_conversions.cpp
index f3ca46a..ade3a55 100644
--- a/libc/bionic/bionic_time_conversions.cpp
+++ b/libc/bionic/bionic_time_conversions.cpp
@@ -51,13 +51,3 @@
tv.tv_sec = ts.tv_sec;
tv.tv_usec = ts.tv_nsec / 1000;
}
-
-void absolute_timespec_from_timespec(timespec& abs_ts, const timespec& ts, clockid_t clock) {
- clock_gettime(clock, &abs_ts);
- abs_ts.tv_sec += ts.tv_sec;
- abs_ts.tv_nsec += ts.tv_nsec;
- if (abs_ts.tv_nsec >= NS_PER_S) {
- abs_ts.tv_nsec -= NS_PER_S;
- abs_ts.tv_sec++;
- }
-}
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index e8be4ae..1d9e03e 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -128,6 +128,14 @@
__LIBC_HIDDEN__ void pthread_key_clean_all(void);
+#if defined(__LP64__)
+// SIGSTKSZ is not big enough for 64-bit arch.
+// See https://code.google.com/p/android/issues/detail?id=187064.
+#define SIGNAL_STACK_SIZE_WITHOUT_GUARD_PAGE (16 * 1024)
+#else
+#define SIGNAL_STACK_SIZE_WITHOUT_GUARD_PAGE SIGSTKSZ
+#endif
+
/*
* Traditionally we gave threads a 1MiB stack. When we started
* allocating per-thread alternate signal stacks to ease debugging of
@@ -135,15 +143,10 @@
* from the default thread stack size. This should keep memory usage
* roughly constant.
*/
-#define PTHREAD_STACK_SIZE_DEFAULT ((1 * 1024 * 1024) - SIGSTKSZ)
+#define PTHREAD_STACK_SIZE_DEFAULT ((1 * 1024 * 1024) - SIGNAL_STACK_SIZE_WITHOUT_GUARD_PAGE)
// Leave room for a guard page in the internally created signal stacks.
-#if defined(__LP64__)
-// SIGSTKSZ is not big enough for 64-bit arch. See http://b/23041777.
-#define SIGNAL_STACK_SIZE (16 * 1024 + PAGE_SIZE)
-#else
-#define SIGNAL_STACK_SIZE (SIGSTKSZ + PAGE_SIZE)
-#endif
+#define SIGNAL_STACK_SIZE (SIGNAL_STACK_SIZE_WITHOUT_GUARD_PAGE + PAGE_SIZE)
/* Needed by fork. */
__LIBC_HIDDEN__ extern void __bionic_atfork_run_prepare();
diff --git a/libc/bionic/sighold.cpp b/libc/bionic/sighold.cpp
new file mode 100644
index 0000000..e9c8ca1
--- /dev/null
+++ b/libc/bionic/sighold.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <signal.h>
+
+int sighold(int sig) {
+ sigset_t set;
+ if (sigemptyset(&set) == -1) return -1;
+ if (sigaddset(&set, sig) == -1) return -1;
+ return sigprocmask(SIG_BLOCK, &set, nullptr);
+}
diff --git a/libc/bionic/sigignore.cpp b/libc/bionic/sigignore.cpp
new file mode 100644
index 0000000..06f458e
--- /dev/null
+++ b/libc/bionic/sigignore.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <signal.h>
+#include <string.h>
+
+int sigignore(int sig) {
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ if (sigemptyset(&sa.sa_mask) == -1) return -1;
+ sa.sa_handler = SIG_IGN;
+ return sigaction(sig, &sa, nullptr);
+}
diff --git a/libc/bionic/sigpause.cpp b/libc/bionic/sigpause.cpp
new file mode 100644
index 0000000..8ba42d4
--- /dev/null
+++ b/libc/bionic/sigpause.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <signal.h>
+
+int sigpause(int sig) {
+ sigset_t set;
+ if (sigprocmask(SIG_SETMASK, nullptr, &set) == -1) return -1;
+ if (sigdelset(&set, sig) == -1) return -1;
+ return sigsuspend(&set);
+}
diff --git a/libc/bionic/sigrelse.cpp b/libc/bionic/sigrelse.cpp
new file mode 100644
index 0000000..ab5554e
--- /dev/null
+++ b/libc/bionic/sigrelse.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <signal.h>
+
+int sigrelse(int sig) {
+ sigset_t set;
+ if (sigemptyset(&set) == -1) return -1;
+ if (sigaddset(&set, sig) == -1) return -1;
+ return sigprocmask(SIG_UNBLOCK, &set, nullptr);
+}
diff --git a/libc/bionic/sigset.cpp b/libc/bionic/sigset.cpp
new file mode 100644
index 0000000..e3f3e72
--- /dev/null
+++ b/libc/bionic/sigset.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <signal.h>
+#include <string.h>
+
+sighandler_t sigset(int sig, sighandler_t disp) {
+ struct sigaction new_sa;
+ if (disp != SIG_HOLD) {
+ memset(&new_sa, 0, sizeof(new_sa));
+ new_sa.sa_handler = disp;
+ sigemptyset(&new_sa.sa_mask);
+ }
+
+ struct sigaction old_sa;
+ if (sigaction(sig, disp == SIG_HOLD ? nullptr : &new_sa, &old_sa) == -1) {
+ return SIG_ERR;
+ }
+
+ sigset_t new_proc_mask;
+ sigemptyset(&new_proc_mask);
+ sigaddset(&new_proc_mask, sig);
+
+ sigset_t old_proc_mask;
+ if (sigprocmask(disp == SIG_HOLD ? SIG_BLOCK : SIG_UNBLOCK,
+ &new_proc_mask, &old_proc_mask) == -1) {
+ return SIG_ERR;
+ }
+
+ return sigismember(&old_proc_mask, sig) ? SIG_HOLD : old_sa.sa_handler;
+}
diff --git a/libc/dns/include/resolv_netid.h b/libc/dns/include/resolv_netid.h
index d364645..09c5498 100644
--- a/libc/dns/include/resolv_netid.h
+++ b/libc/dns/include/resolv_netid.h
@@ -49,6 +49,7 @@
__BEGIN_DECLS
+struct __res_params;
struct addrinfo;
#define __used_in_netd __attribute__((visibility ("default")))
@@ -86,8 +87,8 @@
const struct android_net_context *, struct addrinfo **) __used_in_netd;
/* set name servers for a network */
-extern void _resolv_set_nameservers_for_net(unsigned netid,
- const char** servers, int numservers, const char *domains) __used_in_netd;
+extern void _resolv_set_nameservers_for_net(unsigned netid, const char** servers, int numservers,
+ const char *domains, const struct __res_params* params) __used_in_netd;
/* flush the cache associated with a certain network */
extern void _resolv_flush_cache_for_net(unsigned netid) __used_in_netd;
diff --git a/libc/dns/include/resolv_params.h b/libc/dns/include/resolv_params.h
new file mode 100644
index 0000000..353ae4d
--- /dev/null
+++ b/libc/dns/include/resolv_params.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _RESOLV_PARAMS_H
+#define _RESOLV_PARAMS_H
+
+#include <stdint.h>
+
+/* Hard-coded defines */
+#define MAXNS 3 /* max # name servers we'll track */
+#define MAXNSSAMPLES 64 /* max # samples to store per server */
+
+/* Defaults used for initializing __res_params */
+#define SUCCESS_THRESHOLD 75 /* if successes * 100 / total_samples is less than
+ * this value, the server is considered failing
+ */
+#define NSSAMPLE_VALIDITY 1800 /* Sample validity in seconds.
+ * Set to -1 to disable skipping failing
+ * servers.
+ */
+
+/* per-netid configuration parameters passed from netd to the resolver */
+struct __res_params {
+ uint16_t sample_validity; // sample lifetime in s
+ // threshold of success / total samples below which a server is considered broken
+ uint8_t success_threshold; // 0: disable, value / 100 otherwise
+ uint8_t min_samples; // min # samples needed for statistics to be considered meaningful
+ uint8_t max_samples; // max # samples taken into account for statistics
+} __attribute__((__packed__));
+
+#endif // _RESOLV_PARAMS_H
diff --git a/libc/dns/include/resolv_private.h b/libc/dns/include/resolv_private.h
index 9484d0e..8cdcc2e 100644
--- a/libc/dns/include/resolv_private.h
+++ b/libc/dns/include/resolv_private.h
@@ -58,7 +58,10 @@
#include <resolv.h>
#include "resolv_static.h"
+#include "resolv_params.h"
+#include "resolv_stats.h"
#include <net/if.h>
+#include <time.h>
/* Despite this file's name, it's part of libresolv. On Android, that means it's part of libc :-( */
#pragma GCC visibility push(default)
@@ -136,7 +139,6 @@
/*
* Global defines and variables for resolver stub.
*/
-#define MAXNS 3 /* max # name servers we'll track */
#define MAXDFLSRCH 3 /* # default domain levels to try */
#define MAXDNSRCH 6 /* max # domains in search path */
#define LOCALDOMAINPARTS 2 /* min levels in name that is "local" */
@@ -205,6 +207,24 @@
typedef struct __res_state *res_state;
+/* Retrieve a local copy of the stats for the given netid. The buffer must have space for
+ * MAXNS __resolver_stats. Returns the revision id of the resolvers used.
+ */
+__LIBC_HIDDEN__
+extern int
+_resolv_cache_get_resolver_stats( unsigned netid, struct __res_params* params,
+ struct __res_stats stats[MAXNS]);
+
+/* Add a sample to the shared struct for the given netid and server, provided that the
+ * revision_id of the stored servers has not changed.
+ */
+__LIBC_HIDDEN__
+extern void
+_resolv_cache_add_resolver_stats_sample( unsigned netid, int revision_id, int ns,
+ const struct __res_sample* sample, int max_samples);
+
+/* End of stats related definitions */
+
union res_sockaddr_union {
struct sockaddr_in sin;
#ifdef IN6ADDR_ANY_INIT
diff --git a/libc/dns/include/resolv_stats.h b/libc/dns/include/resolv_stats.h
new file mode 100644
index 0000000..aaf6bd8
--- /dev/null
+++ b/libc/dns/include/resolv_stats.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _RES_STATS_H
+#define _RES_STATS_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <time.h>
+
+#include "resolv_params.h"
+
+#define RCODE_INTERNAL_ERROR 254
+#define RCODE_TIMEOUT 255
+
+/*
+ * Resolver reachability statistics and run-time parameters.
+ */
+
+struct __res_sample {
+ time_t at; // time in s at which the sample was recorded
+ uint16_t rtt; // round-trip time in ms
+ uint8_t rcode; // the DNS rcode or RCODE_XXX defined above
+};
+
+struct __res_stats {
+ // Stats of the last <sample_count> queries.
+ struct __res_sample samples[MAXNSSAMPLES];
+ // The number of samples stored.
+ uint8_t sample_count;
+ // The next sample to modify.
+ uint8_t sample_next;
+};
+
+/* Calculate the round-trip-time from start time t0 and end time t1. */
+int
+_res_stats_calculate_rtt(const struct timespec* t1, const struct timespec* t0);
+
+/* Initialize a sample for calculating server reachability statistics. */
+extern void
+_res_stats_set_sample(struct __res_sample* sample, time_t now, int rcode, int rtt);
+
+/* Aggregates the reachability statistics for the given server based on on the stored samples. */
+extern void
+_res_stats_aggregate(struct __res_stats* stats, int* successes, int* errors, int* timeouts,
+ int* internal_errors, int* rtt_avg, time_t* last_sample_time);
+
+/* Returns true if the server is considered unusable, i.e. if the success rate is not lower than the
+ * threshold for the stored stored samples. If not enough samples are stored, the server is
+ * considered usable.
+ */
+extern bool
+_res_stats_usable_server(const struct __res_params* params, struct __res_stats* stats);
+
+/* Returns an array of bools indicating which servers are considered good */
+extern void
+_res_stats_get_usable_servers(const struct __res_params* params, struct __res_stats stats[],
+ int nscount, bool valid_servers[]);
+
+#endif // _RES_STATS_H
diff --git a/libc/dns/resolv/res_cache.c b/libc/dns/resolv/res_cache.c
index ae8debb..15f9aa4 100644
--- a/libc/dns/resolv/res_cache.c
+++ b/libc/dns/resolv/res_cache.c
@@ -1228,12 +1228,18 @@
PendingReqInfo pending_requests;
} Cache;
+// The nameservers[], nsaddrinfo[] and nsstats[] are containing MAXNS + 1 elements, because the
+// number of nameservers is not known and the code relies on the n+1-st entry to be null to
+// recognize the end.
struct resolv_cache_info {
unsigned netid;
Cache* cache;
struct resolv_cache_info* next;
- char* nameservers[MAXNS +1];
+ char* nameservers[MAXNS + 1];
struct addrinfo* nsaddrinfo[MAXNS + 1];
+ int revision_id; // # times the nameservers have been replaced
+ struct __res_params params;
+ struct __res_stats nsstats[MAXNS + 1];
char defdname[256];
int dnsrch_offset[MAXDNSRCH+1]; // offsets into defdname
};
@@ -1361,6 +1367,8 @@
pthread_mutex_unlock(&_res_cache_list_lock);
}
+static struct resolv_cache_info* _find_cache_info_locked(unsigned netid);
+
static void
_cache_flush_locked( Cache* cache )
{
@@ -1801,6 +1809,8 @@
* currently attached to the provided cache_info */
static int _resolv_is_nameservers_equal_locked(struct resolv_cache_info* cache_info,
const char** servers, int numservers);
+/* clears the stats samples contained withing the given cache_info */
+static void _res_cache_clear_stats_locked(struct resolv_cache_info* cache_info);
static void
_res_cache_init(void)
@@ -1854,6 +1864,10 @@
if (cache) {
_cache_flush_locked(cache);
}
+
+ // Also clear the NS statistics.
+ struct resolv_cache_info* cache_info = _find_cache_info_locked(netid);
+ _res_cache_clear_stats_locked(cache_info);
}
void _resolv_delete_cache_for_net(unsigned netid)
@@ -1928,8 +1942,16 @@
}
void
+_resolv_set_default_params(struct __res_params* params) {
+ params->sample_validity = NSSAMPLE_VALIDITY;
+ params->success_threshold = SUCCESS_THRESHOLD;
+ params->min_samples = 0;
+ params->max_samples = 0;
+}
+
+void
_resolv_set_nameservers_for_net(unsigned netid, const char** servers, int numservers,
- const char *domains)
+ const char *domains, const struct __res_params* params)
{
int i, rt, index;
struct addrinfo hints;
@@ -1945,54 +1967,76 @@
struct resolv_cache_info* cache_info = _find_cache_info_locked(netid);
- if (cache_info != NULL &&
- !_resolv_is_nameservers_equal_locked(cache_info, servers, numservers)) {
- // free current before adding new
- _free_nameservers_locked(cache_info);
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = PF_UNSPEC;
- hints.ai_socktype = SOCK_DGRAM; /*dummy*/
- hints.ai_flags = AI_NUMERICHOST;
- snprintf(sbuf, sizeof(sbuf), "%u", NAMESERVER_PORT);
-
- index = 0;
- for (i = 0; i < numservers && i < MAXNS; i++) {
- rt = getaddrinfo(servers[i], sbuf, &hints, &cache_info->nsaddrinfo[index]);
- if (rt == 0) {
- cache_info->nameservers[index] = strdup(servers[i]);
- index++;
- XLOG("%s: netid = %u, addr = %s\n", __FUNCTION__, netid, servers[i]);
- } else {
- cache_info->nsaddrinfo[index] = NULL;
- }
+ if (cache_info != NULL) {
+ uint8_t old_max_samples = cache_info->params.max_samples;
+ if (params != NULL) {
+ cache_info->params = *params;
+ } else {
+ _resolv_set_default_params(&cache_info->params);
}
- // code moved from res_init.c, load_domain_search_list
- strlcpy(cache_info->defdname, domains, sizeof(cache_info->defdname));
- if ((cp = strchr(cache_info->defdname, '\n')) != NULL)
- *cp = '\0';
- cp = cache_info->defdname;
- offset = cache_info->dnsrch_offset;
- while (offset < cache_info->dnsrch_offset + MAXDNSRCH) {
- while (*cp == ' ' || *cp == '\t') /* skip leading white space */
- cp++;
- if (*cp == '\0') /* stop if nothing more to do */
- break;
- *offset++ = cp - cache_info->defdname; /* record this search domain */
- while (*cp) { /* zero-terminate it */
- if (*cp == ' '|| *cp == '\t') {
- *cp++ = '\0';
- break;
+ if (!_resolv_is_nameservers_equal_locked(cache_info, servers, numservers)) {
+ // free current before adding new
+ _free_nameservers_locked(cache_info);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_flags = AI_NUMERICHOST;
+ snprintf(sbuf, sizeof(sbuf), "%u", NAMESERVER_PORT);
+
+ index = 0;
+ for (i = 0; i < numservers && i < MAXNS; i++) {
+ rt = getaddrinfo(servers[i], sbuf, &hints, &cache_info->nsaddrinfo[index]);
+ if (rt == 0) {
+ cache_info->nameservers[index] = strdup(servers[i]);
+ index++;
+ XLOG("%s: netid = %u, addr = %s\n", __FUNCTION__, netid, servers[i]);
+ } else {
+ cache_info->nsaddrinfo[index] = NULL;
}
- cp++;
}
- }
- *offset = -1; /* cache_info->dnsrch_offset has MAXDNSRCH+1 items */
- // flush cache since new settings
- _flush_cache_for_net_locked(netid);
+ // code moved from res_init.c, load_domain_search_list
+ strlcpy(cache_info->defdname, domains, sizeof(cache_info->defdname));
+ if ((cp = strchr(cache_info->defdname, '\n')) != NULL)
+ *cp = '\0';
+ cp = cache_info->defdname;
+ offset = cache_info->dnsrch_offset;
+ while (offset < cache_info->dnsrch_offset + MAXDNSRCH) {
+ while (*cp == ' ' || *cp == '\t') /* skip leading white space */
+ cp++;
+ if (*cp == '\0') /* stop if nothing more to do */
+ break;
+ *offset++ = cp - cache_info->defdname; /* record this search domain */
+ while (*cp) { /* zero-terminate it */
+ if (*cp == ' '|| *cp == '\t') {
+ *cp++ = '\0';
+ break;
+ }
+ cp++;
+ }
+ }
+ *offset = -1; /* cache_info->dnsrch_offset has MAXDNSRCH+1 items */
+
+ // Flush the cache and reset the stats.
+ _flush_cache_for_net_locked(netid);
+
+ // increment the revision id to ensure that sample state is not written back if the
+ // servers change; in theory it would suffice to do so only if the servers or
+ // max_samples actually change, in practice the overhead of checking is higher than the
+ // cost, and overflows are unlikely
+ ++cache_info->revision_id;
+ } else if (cache_info->params.max_samples != old_max_samples) {
+ // If the maximum number of samples changes, the overhead of keeping the most recent
+ // samples around is not considered worth the effort, so they are cleared instead. All
+ // other parameters do not affect shared state: Changing these parameters does not
+ // invalidate the samples, as they only affect aggregation and the conditions under which
+ // servers are considered usable.
+ _res_cache_clear_stats_locked(cache_info);
+ ++cache_info->revision_id;
+ }
}
pthread_mutex_unlock(&_res_cache_list_lock);
@@ -2049,7 +2093,11 @@
freeaddrinfo(cache_info->nsaddrinfo[i]);
cache_info->nsaddrinfo[i] = NULL;
}
+ cache_info->nsstats[i].sample_count =
+ cache_info->nsstats[i].sample_next = 0;
}
+ _res_cache_clear_stats_locked(cache_info);
+ ++cache_info->revision_id;
}
void
@@ -2103,3 +2151,65 @@
}
pthread_mutex_unlock(&_res_cache_list_lock);
}
+
+/* Resolver reachability statistics. */
+
+static void
+_res_cache_add_stats_sample_locked(struct __res_stats* stats, const struct __res_sample* sample,
+ int max_samples) {
+ // Note: This function expects max_samples > 0, otherwise a (harmless) modification of the
+ // allocated but supposedly unused memory for samples[0] will happen
+ XLOG("%s: adding sample to stats, next = %d, count = %d", __FUNCTION__,
+ stats->sample_next, stats->sample_count);
+ stats->samples[stats->sample_next] = *sample;
+ if (stats->sample_count < max_samples) {
+ ++stats->sample_count;
+ }
+ if (++stats->sample_next >= max_samples) {
+ stats->sample_next = 0;
+ }
+}
+
+static void
+_res_cache_clear_stats_locked(struct resolv_cache_info* cache_info) {
+ if (cache_info) {
+ for (int i = 0 ; i < MAXNS ; ++i) {
+ cache_info->nsstats->sample_count = cache_info->nsstats->sample_next = 0;
+ }
+ }
+}
+
+int
+_resolv_cache_get_resolver_stats( unsigned netid, struct __res_params* params,
+ struct __res_stats stats[MAXNS]) {
+
+ int revision_id = -1;
+ pthread_mutex_lock(&_res_cache_list_lock);
+
+ struct resolv_cache_info* info = _find_cache_info_locked(netid);
+ if (info) {
+ memcpy(stats, info->nsstats, sizeof(info->nsstats));
+ *params = info->params;
+ revision_id = info->revision_id;
+ }
+
+ pthread_mutex_unlock(&_res_cache_list_lock);
+ return revision_id;
+}
+
+void
+_resolv_cache_add_resolver_stats_sample( unsigned netid, int revision_id, int ns,
+ const struct __res_sample* sample, int max_samples) {
+ if (max_samples <= 0) return;
+
+ pthread_mutex_lock(&_res_cache_list_lock);
+
+ struct resolv_cache_info* info = _find_cache_info_locked(netid);
+
+ if (info && info->revision_id == revision_id) {
+ _res_cache_add_stats_sample_locked(&info->nsstats[ns], sample, max_samples);
+ }
+
+ pthread_mutex_unlock(&_res_cache_list_lock);
+}
+
diff --git a/libc/dns/resolv/res_send.c b/libc/dns/resolv/res_send.c
index 3458e48..9ceeeb7 100644
--- a/libc/dns/resolv/res_send.c
+++ b/libc/dns/resolv/res_send.c
@@ -81,9 +81,6 @@
#endif
#endif /* LIBC_SCCS and not lint */
-/* set to 1 to use our small/simple/limited DNS cache */
-#define USE_RESOLV_CACHE 1
-
/*
* Send query to name server and wait for reply.
*/
@@ -116,9 +113,7 @@
#include <isc/eventlib.h>
-#if USE_RESOLV_CACHE
-# include <resolv_cache.h>
-#endif
+#include <resolv_cache.h>
#include "private/libc_logging.h"
@@ -133,6 +128,7 @@
#endif
#include "res_debug.h"
#include "res_private.h"
+#include "resolv_stats.h"
#define EXT(res) ((res)->_u._ext)
#define DBG 0
@@ -144,10 +140,12 @@
static int get_salen __P((const struct sockaddr *));
static struct sockaddr * get_nsaddr __P((res_state, size_t));
static int send_vc(res_state, const u_char *, int,
- u_char *, int, int *, int);
+ u_char *, int, int *, int,
+ time_t *, int *, int *);
static int send_dg(res_state, const u_char *, int,
u_char *, int, int *, int,
- int *, int *);
+ int *, int *,
+ time_t *, int *, int *);
static void Aerror(const res_state, FILE *, const char *, int,
const struct sockaddr *, int);
static void Perror(const res_state, FILE *, const char *, int);
@@ -359,23 +357,13 @@
return (1);
}
-
int
res_nsend(res_state statp,
const u_char *buf, int buflen, u_char *ans, int anssiz)
{
int gotsomewhere, terrno, try, v_circuit, resplen, ns, n;
char abuf[NI_MAXHOST];
-#if USE_RESOLV_CACHE
- ResolvCacheStatus cache_status = RESOLV_CACHE_UNSUPPORTED;
-#endif
-
-#if !USE_RESOLV_CACHE
- if (statp->nscount == 0) {
- errno = ESRCH;
- return (-1);
- }
-#endif
+ ResolvCacheStatus cache_status = RESOLV_CACHE_UNSUPPORTED;
if (anssiz < HFIXEDSZ) {
errno = EINVAL;
@@ -387,7 +375,6 @@
gotsomewhere = 0;
terrno = ETIMEDOUT;
-#if USE_RESOLV_CACHE
int anslen = 0;
cache_status = _resolv_cache_lookup(
statp->netid, buf, buflen,
@@ -400,7 +387,6 @@
// data so the normal resolve path can do its thing
_resolv_populate_res_for_net(statp);
}
-
if (statp->nscount == 0) {
// We have no nameservers configured, so there's no point trying.
// Tell the cache the query failed, or any retries and anyone else asking the same
@@ -409,7 +395,6 @@
errno = ESRCH;
return (-1);
}
-#endif
/*
* If the ns_addr_list in the resolver context has changed, then
@@ -420,9 +405,9 @@
struct sockaddr_storage peer;
socklen_t peerlen;
- if (EXT(statp).nscount != statp->nscount)
+ if (EXT(statp).nscount != statp->nscount) {
needclose++;
- else
+ } else {
for (ns = 0; ns < statp->nscount; ns++) {
if (statp->nsaddr_list[ns].sin_family &&
!sock_eq((struct sockaddr *)(void *)&statp->nsaddr_list[ns],
@@ -445,6 +430,7 @@
break;
}
}
+ }
if (needclose) {
res_nclose(statp);
EXT(statp).nscount = 0;
@@ -485,7 +471,7 @@
nstime = EXT(statp).nstimes[0];
for (ns = 0; ns < lastns; ns++) {
if (EXT(statp).ext != NULL)
- EXT(statp).ext->nsaddrs[ns] =
+ EXT(statp).ext->nsaddrs[ns] =
EXT(statp).ext->nsaddrs[ns + 1];
statp->nsaddr_list[ns] = statp->nsaddr_list[ns + 1];
EXT(statp).nssocks[ns] = EXT(statp).nssocks[ns + 1];
@@ -502,13 +488,24 @@
* Send request, RETRY times, or until successful.
*/
for (try = 0; try < statp->retry; try++) {
+ struct __res_stats stats[MAXNS + 1];
+ struct __res_params params;
+ int revision_id = _resolv_cache_get_resolver_stats(statp->netid, ¶ms, stats);
+ bool usable_servers[MAXNS + 1];
+ _res_stats_get_usable_servers(¶ms, stats, statp->nscount, usable_servers);
+
for (ns = 0; ns < statp->nscount; ns++) {
+ if (!usable_servers[ns]) continue;
struct sockaddr *nsap;
int nsaplen;
+ time_t now = 0;
+ int rcode = RCODE_INTERNAL_ERROR;
+ int delay = 0;
nsap = get_nsaddr(statp, (size_t)ns);
nsaplen = get_salen(nsap);
statp->_flags &= ~RES_F_LASTMASK;
statp->_flags |= (ns << RES_F_LASTSHIFT);
+
same_ns:
if (statp->qhook) {
int done = 0, loops = 0;
@@ -552,7 +549,12 @@
try = statp->retry;
n = send_vc(statp, buf, buflen, ans, anssiz, &terrno,
- ns);
+ ns, &now, &rcode, &delay);
+
+ struct __res_sample sample;
+ _res_stats_set_sample(&sample, now, rcode, delay);
+ _resolv_cache_add_resolver_stats_sample(statp->netid, revision_id, ns,
+ &sample, params.max_samples);
if (DBG) {
__libc_format_log(ANDROID_LOG_DEBUG, "libc",
@@ -571,7 +573,13 @@
}
n = send_dg(statp, buf, buflen, ans, anssiz, &terrno,
- ns, &v_circuit, &gotsomewhere);
+ ns, &v_circuit, &gotsomewhere, &now, &rcode, &delay);
+
+ struct __res_sample sample;
+ _res_stats_set_sample(&sample, now, rcode, delay);
+ _resolv_cache_add_resolver_stats_sample(statp->netid, revision_id, ns,
+ &sample, params.max_samples);
+
if (DBG) {
__libc_format_log(ANDROID_LOG_DEBUG, "libc", "used send_dg %d\n",n);
}
@@ -582,7 +590,7 @@
goto next_ns;
if (DBG) {
__libc_format_log(ANDROID_LOG_DEBUG, "libc", "time=%ld\n",
- time(NULL));
+ time(NULL));
}
if (v_circuit)
goto same_ns;
@@ -599,12 +607,10 @@
(stdout, "%s", ""),
ans, (resplen > anssiz) ? anssiz : resplen);
-#if USE_RESOLV_CACHE
- if (cache_status == RESOLV_CACHE_NOTFOUND) {
- _resolv_cache_add(statp->netid, buf, buflen,
- ans, resplen);
- }
-#endif
+ if (cache_status == RESOLV_CACHE_NOTFOUND) {
+ _resolv_cache_add(statp->netid, buf, buflen,
+ ans, resplen);
+ }
/*
* If we have temporarily opened a virtual circuit,
* or if we haven't been asked to keep a socket open,
@@ -656,15 +662,12 @@
} else
errno = terrno;
-#if USE_RESOLV_CACHE
- _resolv_cache_query_failed(statp->netid, buf, buflen);
-#endif
+ _resolv_cache_query_failed(statp->netid, buf, buflen);
return (-1);
fail:
-#if USE_RESOLV_CACHE
+
_resolv_cache_query_failed(statp->netid, buf, buflen);
-#endif
res_nclose(statp);
return (-1);
}
@@ -735,8 +738,11 @@
static int
send_vc(res_state statp,
const u_char *buf, int buflen, u_char *ans, int anssiz,
- int *terrno, int ns)
+ int *terrno, int ns, time_t* at, int* rcode, int* delay)
{
+ *at = 0;
+ *rcode = RCODE_INTERNAL_ERROR;
+ *delay = 0;
const HEADER *hp = (const HEADER *)(const void *)buf;
HEADER *anhp = (HEADER *)(void *)ans;
struct sockaddr *nsap;
@@ -758,6 +764,8 @@
same_ns:
truncating = 0;
+ struct timespec now = evNowTime();
+
/* Are we still talking to whom we want to talk to? */
if (statp->_vcsock >= 0 && (statp->_flags & RES_F_VC) != 0) {
struct sockaddr_storage peer;
@@ -800,7 +808,7 @@
}
if (statp->_mark != MARK_UNSET) {
if (setsockopt(statp->_vcsock, SOL_SOCKET,
- SO_MARK, &statp->_mark, sizeof(statp->_mark)) < 0) {
+ SO_MARK, &statp->_mark, sizeof(statp->_mark)) < 0) {
*terrno = errno;
Perror(statp, stderr, "setsockopt", errno);
return -1;
@@ -820,6 +828,15 @@
Aerror(statp, stderr, "connect/vc", errno, nsap,
nsaplen);
res_nclose(statp);
+ /*
+ * The way connect_with_timeout() is implemented prevents us from reliably
+ * determining whether this was really a timeout or e.g. ECONNREFUSED. Since
+ * currently both cases are handled in the same way, there is no need to
+ * change this (yet). If we ever need to reliably distinguish between these
+ * cases, both connect_with_timeout() and retrying_select() need to be
+ * modified, though.
+ */
+ *rcode = RCODE_TIMEOUT;
return (0);
}
statp->_flags |= RES_F_VC;
@@ -900,6 +917,10 @@
res_nclose(statp);
return (0);
}
+
+ struct timespec done = evNowTime();
+ *at = done.tv_sec;
+
if (truncating) {
/*
* Flush rest of answer so connection stays in synch.
@@ -936,6 +957,10 @@
* All is well, or the error is fatal. Signal that the
* next nameserver ought not be tried.
*/
+ if (resplen > 0) {
+ *delay = _res_stats_calculate_rtt(&done, &now);
+ *rcode = anhp->rcode;
+ }
return (resplen);
}
@@ -952,8 +977,8 @@
res = __connect(sock, nsap, salen);
if (res < 0 && errno != EINPROGRESS) {
- res = -1;
- goto done;
+ res = -1;
+ goto done;
}
if (res != 0) {
now = evNowTime();
@@ -965,7 +990,7 @@
res = retrying_select(sock, &rset, &wset, &finish);
if (res <= 0) {
- res = -1;
+ res = -1;
}
}
done:
@@ -987,7 +1012,7 @@
retry:
if (DBG) {
- __libc_format_log(ANDROID_LOG_DEBUG, "libc", " %d retying_select\n", sock);
+ __libc_format_log(ANDROID_LOG_DEBUG, "libc", " %d retrying_select\n", sock);
}
now = evNowTime();
@@ -1042,17 +1067,20 @@
return n;
}
-
static int
send_dg(res_state statp,
const u_char *buf, int buflen, u_char *ans, int anssiz,
- int *terrno, int ns, int *v_circuit, int *gotsomewhere)
+ int *terrno, int ns, int *v_circuit, int *gotsomewhere,
+ time_t *at, int *rcode, int* delay)
{
+ *at = 0;
+ *rcode = RCODE_INTERNAL_ERROR;
+ *delay = 0;
const HEADER *hp = (const HEADER *)(const void *)buf;
HEADER *anhp = (HEADER *)(void *)ans;
const struct sockaddr *nsap;
int nsaplen;
- struct timespec now, timeout, finish;
+ struct timespec now, timeout, finish, done;
fd_set dsmask;
struct sockaddr_storage from;
socklen_t fromlen;
@@ -1145,6 +1173,7 @@
n = retrying_select(s, &dsmask, NULL, &finish);
if (n == 0) {
+ *rcode = RCODE_TIMEOUT;
Dprint(statp->options & RES_DEBUG, (stdout, ";; timeout\n"));
*gotsomewhere = 1;
return (0);
@@ -1230,6 +1259,9 @@
ans, (resplen > anssiz) ? anssiz : resplen);
goto retry;;
}
+ done = evNowTime();
+ *at = done.tv_sec;
+ *delay = _res_stats_calculate_rtt(&done, &now);
if (anhp->rcode == SERVFAIL ||
anhp->rcode == NOTIMP ||
anhp->rcode == REFUSED) {
@@ -1238,8 +1270,10 @@
ans, (resplen > anssiz) ? anssiz : resplen);
res_nclose(statp);
/* don't retry if called from dig */
- if (!statp->pfcode)
+ if (!statp->pfcode) {
+ *rcode = anhp->rcode;
return (0);
+ }
}
if (!(statp->options & RES_IGNTC) && anhp->tc) {
/*
@@ -1256,6 +1290,9 @@
* All is well, or the error is fatal. Signal that the
* next nameserver ought not be tried.
*/
+ if (resplen > 0) {
+ *rcode = anhp->rcode;
+ }
return (resplen);
}
diff --git a/libc/dns/resolv/res_stats.c b/libc/dns/resolv/res_stats.c
new file mode 100644
index 0000000..b6f5ecb
--- /dev/null
+++ b/libc/dns/resolv/res_stats.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdbool.h>
+#include <arpa/nameser.h>
+#include <string.h>
+
+#include "resolv_stats.h"
+#include "private/libc_logging.h"
+#include "isc/eventlib.h"
+
+#define DBG 1
+
+/* Calculate the round-trip-time from start time t0 and end time t1. */
+int
+_res_stats_calculate_rtt(const struct timespec* t1, const struct timespec* t0) {
+ // Divide ns by one million to get ms, multiply s by thousand to get ms (obvious)
+ long ms0 = t0->tv_sec * 1000 + t0->tv_nsec / 1000000;
+ long ms1 = t1->tv_sec * 1000 + t1->tv_nsec / 1000000;
+ return (int) (ms1 - ms0);
+}
+
+/* Create a sample for calculating server reachability statistics. */
+void
+_res_stats_set_sample(struct __res_sample* sample, time_t now, int rcode, int rtt)
+{
+ if (DBG) {
+ __libc_format_log(ANDROID_LOG_INFO, "libc", "rcode = %d, sec = %d", rcode, rtt);
+ }
+ sample->at = now;
+ sample->rcode = rcode;
+ sample->rtt = rtt;
+}
+
+/* Clears all stored samples for the given server. */
+void
+_res_stats_clear_samples(struct __res_stats* stats)
+{
+ stats->sample_count = stats->sample_next = 0;
+}
+
+/* Aggregates the reachability statistics for the given server based on on the stored samples. */
+void
+_res_stats_aggregate(struct __res_stats* stats, int* successes, int* errors, int* timeouts,
+ int* internal_errors, int* rtt_avg, time_t* last_sample_time)
+{
+ int s = 0; // successes
+ int e = 0; // errors
+ int t = 0; // timouts
+ int ie = 0; // internal errors
+ long rtt_sum = 0;
+ time_t last = 0;
+ int rtt_count = 0;
+ for (int i = 0 ; i < stats->sample_count ; ++i) {
+ // Treat everything as an error that the code in send_dg() already considers a
+ // rejection by the server, i.e. SERVFAIL, NOTIMP and REFUSED. Assume that NXDOMAIN
+ // and NOTAUTH can actually occur for user queries. NOERROR with empty answer section
+ // is not treated as an error here either. FORMERR seems to sometimes be returned by
+ // some versions of BIND in response to DNSSEC or EDNS0. Whether to treat such responses
+ // as an indication of a broken server is unclear, though. For now treat such responses,
+ // as well as unknown codes as errors.
+ switch (stats->samples[i].rcode) {
+ case NOERROR:
+ case NOTAUTH:
+ case NXDOMAIN:
+ ++s;
+ rtt_sum += stats->samples[i].rtt;
+ ++rtt_count;
+ break;
+ case RCODE_TIMEOUT:
+ ++t;
+ break;
+ case RCODE_INTERNAL_ERROR:
+ ++ie;
+ break;
+ case SERVFAIL:
+ case NOTIMP:
+ case REFUSED:
+ default:
+ ++e;
+ break;
+ }
+ }
+ *successes = s;
+ *errors = e;
+ *timeouts = t;
+ *internal_errors = ie;
+ /* If there was at least one successful sample, calculate average RTT. */
+ if (rtt_count) {
+ *rtt_avg = rtt_sum / rtt_count;
+ } else {
+ *rtt_avg = -1;
+ }
+ /* If we had at least one sample, populate last sample time. */
+ if (stats->sample_count > 0) {
+ if (stats->sample_next > 0) {
+ last = stats->samples[stats->sample_next - 1].at;
+ } else {
+ last = stats->samples[stats->sample_count - 1].at;
+ }
+ }
+ *last_sample_time = last;
+}
+
+bool
+_res_stats_usable_server(const struct __res_params* params, struct __res_stats* stats) {
+ int successes = -1;
+ int errors = -1;
+ int timeouts = -1;
+ int internal_errors = -1;
+ int rtt_avg = -1;
+ time_t last_sample_time = 0;
+ _res_stats_aggregate(stats, &successes, &errors, &timeouts, &internal_errors, &rtt_avg,
+ &last_sample_time);
+ if (successes >= 0 && errors >= 0 && timeouts >= 0) {
+ int total = successes + errors + timeouts;
+ if (DBG) {
+ __libc_format_log(ANDROID_LOG_DEBUG, "libc", "NS stats: S %d + E %d + T %d + I %d "
+ "= %d, rtt = %d, min_samples = %d\n", successes, errors, timeouts, internal_errors,
+ total, rtt_avg, params->min_samples);
+ }
+ if (total >= params->min_samples && (errors > 0 || timeouts > 0)) {
+ int success_rate = successes * 100 / total;
+ if (DBG) {
+ __libc_format_log(ANDROID_LOG_DEBUG, "libc", "success rate %d%%\n", success_rate);
+ }
+ if (success_rate < params->success_threshold) {
+ // evNowTime() is used here instead of time() to stay consistent with the rest of
+ // the code base
+ time_t now = evNowTime().tv_sec;
+ if (now - last_sample_time > params->sample_validity) {
+ // Note: It might be worth considering to expire old servers after their expiry
+ // date has been reached, however the code for returning the ring buffer to its
+ // previous non-circular state would induce additional complexity.
+ if (DBG) {
+ __libc_format_log(ANDROID_LOG_INFO, "libc",
+ "samples stale, retrying server\n");
+ }
+ _res_stats_clear_samples(stats);
+ } else {
+ if (DBG) {
+ __libc_format_log(ANDROID_LOG_INFO, "libc",
+ "too many resolution errors, ignoring server\n");
+ }
+ return 0;
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+void
+_res_stats_get_usable_servers(const struct __res_params* params, struct __res_stats stats[],
+ int nscount, bool usable_servers[]) {
+ unsigned usable_servers_found = 0;
+ for (int ns = 0; ns < nscount; ns++) {
+ bool usable = _res_stats_usable_server(params, &stats[ns]);
+ if (usable) {
+ ++usable_servers_found;
+ }
+ usable_servers[ns] = usable;
+ }
+ // If there are no usable servers, consider all of them usable.
+ // TODO: Explore other possibilities, such as enabling only the best N servers, etc.
+ if (usable_servers_found == 0) {
+ for (int ns = 0; ns < nscount; ns++) {
+ usable_servers[ns] = true;
+ }
+ }
+}
diff --git a/libc/include/signal.h b/libc/include/signal.h
index 85c46c0..763bae9 100644
--- a/libc/include/signal.h
+++ b/libc/include/signal.h
@@ -64,6 +64,11 @@
#define _NSIG (_KERNEL__NSIG + 1)
#define NSIG _NSIG
+/* The kernel headers define SIG_DFL (0) and SIG_IGN (1) but not SIG_HOLD, since
+ * SIG_HOLD is only used by the deprecated SysV signal API.
+ */
+#define SIG_HOLD ((sighandler_t)(uintptr_t)2)
+
/* We take a few real-time signals for ourselves. May as well use the same names as glibc. */
#define SIGRTMIN (__libc_current_sigrtmin())
#define SIGRTMAX (__libc_current_sigrtmax())
@@ -120,6 +125,12 @@
extern int sigsuspend(const sigset_t*) __nonnull((1));
extern int sigwait(const sigset_t*, int*) __nonnull((1, 2));
+extern int sighold(int) __attribute__((deprecated("use sigprocmask() or pthread_sigmask() instead")));
+extern int sigignore(int) __attribute__((deprecated("use sigaction() instead")));
+extern int sigpause(int) __attribute__((deprecated("use sigsuspend() instead")));
+extern int sigrelse(int) __attribute__((deprecated("use sigprocmask() or pthread_sigmask() instead")));
+extern sighandler_t sigset(int, sighandler_t) __attribute__((deprecated("use sigaction() instead")));
+
extern int raise(int);
extern int kill(pid_t, int);
extern int killpg(int, int);
diff --git a/libc/libc.arm.brillo.map b/libc/libc.arm.brillo.map
index f631ad9..c249881 100644
--- a/libc/libc.arm.brillo.map
+++ b/libc/libc.arm.brillo.map
@@ -1278,6 +1278,11 @@
hasmntopt;
pthread_getname_np;
setdomainname;
+ sighold;
+ sigignore;
+ sigpause;
+ sigrelse;
+ sigset;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index 4c2a272..9890b14 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1278,6 +1278,11 @@
hasmntopt;
pthread_getname_np;
setdomainname;
+ sighold;
+ sigignore;
+ sigpause;
+ sigrelse;
+ sigset;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index c42d1b6..a649ca2 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1200,6 +1200,11 @@
hasmntopt;
pthread_getname_np;
setdomainname;
+ sighold;
+ sigignore;
+ sigpause;
+ sigrelse;
+ sigset;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 25f647d..9f2002f 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1304,6 +1304,11 @@
hasmntopt;
pthread_getname_np;
setdomainname;
+ sighold;
+ sigignore;
+ sigpause;
+ sigrelse;
+ sigset;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/libc.mips.brillo.map b/libc/libc.mips.brillo.map
index d55f32d..a14ef42 100644
--- a/libc/libc.mips.brillo.map
+++ b/libc/libc.mips.brillo.map
@@ -1262,6 +1262,11 @@
hasmntopt;
pthread_getname_np;
setdomainname;
+ sighold;
+ sigignore;
+ sigpause;
+ sigrelse;
+ sigset;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index 44d7d81..dec8352 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1262,6 +1262,11 @@
hasmntopt;
pthread_getname_np;
setdomainname;
+ sighold;
+ sigignore;
+ sigpause;
+ sigrelse;
+ sigset;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index c42d1b6..a649ca2 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1200,6 +1200,11 @@
hasmntopt;
pthread_getname_np;
setdomainname;
+ sighold;
+ sigignore;
+ sigpause;
+ sigrelse;
+ sigset;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/libc.x86.brillo.map b/libc/libc.x86.brillo.map
index 9c0b2ec..a186fb3 100644
--- a/libc/libc.x86.brillo.map
+++ b/libc/libc.x86.brillo.map
@@ -1261,6 +1261,11 @@
hasmntopt;
pthread_getname_np;
setdomainname;
+ sighold;
+ sigignore;
+ sigpause;
+ sigrelse;
+ sigset;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index 980322a..d594a9a 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1261,6 +1261,11 @@
hasmntopt;
pthread_getname_np;
setdomainname;
+ sighold;
+ sigignore;
+ sigpause;
+ sigrelse;
+ sigset;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index c42d1b6..a649ca2 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1200,6 +1200,11 @@
hasmntopt;
pthread_getname_np;
setdomainname;
+ sighold;
+ sigignore;
+ sigpause;
+ sigrelse;
+ sigset;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/private/bionic_time_conversions.h b/libc/private/bionic_time_conversions.h
index a834843..b9eaad2 100644
--- a/libc/private/bionic_time_conversions.h
+++ b/libc/private/bionic_time_conversions.h
@@ -42,9 +42,6 @@
__LIBC_HIDDEN__ void timeval_from_timespec(timeval& tv, const timespec& ts);
-__LIBC_HIDDEN__ void absolute_timespec_from_timespec(timespec& abs_ts, const timespec& ts,
- clockid_t clock);
-
__END_DECLS
static inline int check_timespec(const timespec* ts, bool null_allowed) {
@@ -62,4 +59,16 @@
return 0;
}
+#if !defined(__LP64__)
+static inline void absolute_timespec_from_timespec(timespec& abs_ts, const timespec& ts, clockid_t clock) {
+ clock_gettime(clock, &abs_ts);
+ abs_ts.tv_sec += ts.tv_sec;
+ abs_ts.tv_nsec += ts.tv_nsec;
+ if (abs_ts.tv_nsec >= NS_PER_S) {
+ abs_ts.tv_nsec -= NS_PER_S;
+ abs_ts.tv_sec++;
+ }
+}
+#endif
+
#endif
diff --git a/tests/Android.mk b/tests/Android.mk
index aeb10ea..7fdf2f4 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -38,6 +38,10 @@
-Werror \
-fno-builtin \
+# We want to test deprecated API too.
+test_cflags += \
+ -Wno-deprecated-declarations \
+
test_cflags += -D__STDC_LIMIT_MACROS # For glibc.
test_cppflags := \
diff --git a/tests/ScopedSignalHandler.h b/tests/ScopedSignalHandler.h
index 3fb60a1..8998d0d 100644
--- a/tests/ScopedSignalHandler.h
+++ b/tests/ScopedSignalHandler.h
@@ -39,6 +39,10 @@
sigaction(signal_number_, &action_, &old_action_);
}
+ ScopedSignalHandler(int signal_number) : signal_number_(signal_number) {
+ sigaction(signal_number, nullptr, &old_action_);
+ }
+
~ScopedSignalHandler() {
sigaction(signal_number_, &old_action_, NULL);
}
@@ -49,4 +53,18 @@
const int signal_number_;
};
+class ScopedSignalMask {
+ public:
+ ScopedSignalMask() {
+ sigprocmask(SIG_SETMASK, nullptr, &old_mask_);
+ }
+
+ ~ScopedSignalMask() {
+ sigprocmask(SIG_SETMASK, &old_mask_, nullptr);
+ }
+
+ private:
+ sigset_t old_mask_;
+};
+
#endif // _BIONIC_TESTS_SCOPED_SIGNAL_HANDLER_H
diff --git a/tests/signal_test.cpp b/tests/signal_test.cpp
index c5128ea..36ac690 100644
--- a/tests/signal_test.cpp
+++ b/tests/signal_test.cpp
@@ -422,3 +422,108 @@
#endif
#endif
+
+TEST(signal, sigignore_EINVAL) {
+ errno = 0;
+ ASSERT_EQ(-1, sigignore(99999));
+ ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(signal, sigignore) {
+ errno = 0;
+ EXPECT_EQ(-1, sigignore(SIGKILL));
+ EXPECT_EQ(errno, EINVAL);
+
+ errno = 0;
+ EXPECT_EQ(-1, sigignore(SIGSTOP));
+ EXPECT_EQ(errno, EINVAL);
+
+ ScopedSignalHandler sigalrm{SIGALRM};
+ ASSERT_EQ(0, sigignore(SIGALRM));
+
+ struct sigaction sa;
+ ASSERT_EQ(0, sigaction(SIGALRM, nullptr, &sa));
+ EXPECT_EQ(SIG_IGN, sa.sa_handler);
+}
+
+TEST(signal, sighold_EINVAL) {
+ errno = 0;
+ ASSERT_EQ(-1, sighold(99999));
+ ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(signal, sigpause_EINVAL) {
+ errno = 0;
+ ASSERT_EQ(-1, sigpause(99999));
+ ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(signal, sigrelse_EINVAL) {
+ errno = 0;
+ ASSERT_EQ(-1, sigpause(99999));
+ ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(signal, sighold_sigpause_sigrelse) {
+ static int sigalrm_handler_call_count;
+ auto sigalrm_handler = [](int) { sigalrm_handler_call_count++; };
+ ScopedSignalHandler sigalrm{SIGALRM, sigalrm_handler};
+ ScopedSignalMask mask;
+ sigset_t set;
+
+ // sighold(SIGALRM) should add SIGALRM to the signal mask ...
+ ASSERT_EQ(0, sighold(SIGALRM));
+ ASSERT_EQ(0, sigprocmask(SIG_SETMASK, 0, &set));
+ EXPECT_TRUE(sigismember(&set, SIGALRM));
+
+ // ... preventing our SIGALRM handler from running ...
+ raise(SIGALRM);
+ ASSERT_EQ(0, sigalrm_handler_call_count);
+ // ... until sigpause(SIGALRM) temporarily unblocks it.
+ ASSERT_EQ(-1, sigpause(SIGALRM));
+ ASSERT_EQ(EINTR, errno);
+ ASSERT_EQ(1, sigalrm_handler_call_count);
+
+ // But sigpause(SIGALRM) shouldn't permanently unblock SIGALRM.
+ ASSERT_EQ(0, sigprocmask(SIG_SETMASK, 0, &set));
+ EXPECT_TRUE(sigismember(&set, SIGALRM));
+
+ ASSERT_EQ(0, sigrelse(SIGALRM));
+ ASSERT_EQ(0, sigprocmask(SIG_SETMASK, 0, &set));
+ EXPECT_FALSE(sigismember(&set, SIGALRM));
+}
+
+TEST(signal, sigset_EINVAL) {
+ errno = 0;
+ ASSERT_EQ(SIG_ERR, sigset(99999, SIG_DFL));
+ ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(signal, sigset) {
+ auto sigalrm_handler = [](int) { };
+ ScopedSignalHandler sigalrm{SIGALRM, sigalrm_handler};
+ ScopedSignalMask mask;
+
+ // block SIGALRM so the next sigset(SIGARLM) call will return SIG_HOLD
+ sigset_t sigalrm_set;
+ sigemptyset(&sigalrm_set);
+ sigaddset(&sigalrm_set, SIGALRM);
+ ASSERT_EQ(0, sigprocmask(SIG_BLOCK, &sigalrm_set, nullptr));
+
+ sigset_t set;
+ ASSERT_EQ(SIG_HOLD, sigset(SIGALRM, sigalrm_handler));
+ ASSERT_EQ(0, sigprocmask(SIG_BLOCK, nullptr, &set));
+ EXPECT_FALSE(sigismember(&set, SIGALRM));
+
+ ASSERT_EQ(sigalrm_handler, sigset(SIGALRM, SIG_IGN));
+ ASSERT_EQ(0, sigprocmask(SIG_BLOCK, nullptr, &set));
+ EXPECT_FALSE(sigismember(&set, SIGALRM));
+
+ ASSERT_EQ(SIG_IGN, sigset(SIGALRM, SIG_DFL));
+ ASSERT_EQ(0, sigprocmask(SIG_BLOCK, nullptr, &set));
+ EXPECT_FALSE(sigismember(&set, SIGALRM));
+
+ ASSERT_EQ(SIG_DFL, sigset(SIGALRM, SIG_HOLD));
+ ASSERT_EQ(0, sigprocmask(SIG_BLOCK, nullptr, &set));
+ EXPECT_TRUE(sigismember(&set, SIGALRM));
+}