aboutsummaryrefslogtreecommitdiff
path: root/resolv
diff options
context:
space:
mode:
Diffstat (limited to 'resolv')
-rw-r--r--resolv/Makefile20
-rw-r--r--resolv/Versions9
-rw-r--r--resolv/ga_test.c99
-rw-r--r--resolv/gai_cancel.c48
-rw-r--r--resolv/gai_error.c28
-rw-r--r--resolv/gai_misc.c423
-rw-r--r--resolv/gai_misc.h100
-rw-r--r--resolv/gai_notify.c100
-rw-r--r--resolv/gai_suspend.c147
-rw-r--r--resolv/getaddrinfo_a.c168
-rw-r--r--resolv/netdb.h72
11 files changed, 1199 insertions, 15 deletions
diff --git a/resolv/Makefile b/resolv/Makefile
index 64afbcb..fd056d0 100644
--- a/resolv/Makefile
+++ b/resolv/Makefile
@@ -1,4 +1,4 @@
-# Copyright (C) 1994,95,96,97,98,99,2000 Free Software Foundation, Inc.
+# Copyright (C) 1994,95,96,97,98,99,2000,2001 Free Software Foundation, Inc.
# This file is part of the GNU C Library.
# The GNU C Library is free software; you can redistribute it and/or
@@ -26,16 +26,16 @@ headers := resolv.h \
arpa/nameser.h arpa/nameser_compat.h \
sys/bitypes.h
distribute := ../conf/portability.h mapv4v6addr.h mapv4v6hostent.h \
- Banner res_hconf.h res_debug.h README
+ Banner res_hconf.h res_debug.h README gai_misc.h ga_test.c
routines := herror inet_addr inet_ntop inet_pton nsap_addr res_init \
- res_hconf res_libc
+ res_hconf res_libc gai_sigqueue
tests = tst-aton
include ../Makeconfig
-extra-libs := libresolv libnss_dns
+extra-libs := libresolv libanl libnss_dns
extra-libs-others = $(extra-libs)
libresolv-routines := gethnamaddr res_comp res_debug \
res_data res_mkquery res_query res_send \
@@ -43,6 +43,9 @@ libresolv-routines := gethnamaddr res_comp res_debug \
ns_parse ns_name ns_netint ns_ttl ns_print \
ns_samedomain
+libanl-routines := gai_cancel gai_error gai_misc gai_notify gai_suspend \
+ getaddrinfo_a
+
subdir-dirs = nss_dns
vpath %.c nss_dns
@@ -51,6 +54,10 @@ ifneq ($(build-static-nss),yes)
libnss_dns-inhibit-o = $(filter-out .os,$(object-suffixes))
endif
+ifeq (yes,$(build-shared))
+tests: $(objpfx)ga_test
+endif
+
include ../Rules
CPPFLAGS += -Dgethostbyname=res_gethostbyname \
@@ -69,3 +76,8 @@ $(objpfx)libresolv.so: $(common-objpfx)libc.so
# The DNS NSS modules needs the resolver.
$(objpfx)libnss_dns.so: $(objpfx)libresolv.so $(common-objpfx)libc.so
+
+# The asynchronous name lookup code needs the thread library.
+$(objpfx)libanl.so: $(common-objpfx)libc.so $(shared-thread-library)
+
+$(objpfx)ga_test: $(objpfx)libanl.so $(shared-thread-library)
diff --git a/resolv/Versions b/resolv/Versions
index 8131f1d..b76b112 100644
--- a/resolv/Versions
+++ b/resolv/Versions
@@ -22,6 +22,9 @@ libc {
# r*
__res_state; __res_init; __res_nclose; __res_ninit; _res_hconf;
}
+ GLIBC_2.2.3 {
+ __gai_sigqueue;
+ }
}
libresolv {
@@ -66,3 +69,9 @@ libnss_dns {
_nss_dns_getnetbyname_r;
}
}
+
+libanl {
+ GLIBC_2.2.3 {
+ getaddrinfo_a; gai_cancel; gai_error; gai_suspend;
+ }
+}
diff --git a/resolv/ga_test.c b/resolv/ga_test.c
new file mode 100644
index 0000000..673162f
--- /dev/null
+++ b/resolv/ga_test.c
@@ -0,0 +1,99 @@
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+int
+main (void)
+{
+#define N 10
+ struct gaicb reqmem[N];
+ struct gaicb *req[N];
+ int n;
+
+ for (n = 0; n < N; ++n)
+ {
+ asprintf (&reqmem[n].ar_name, "test%d.test.redhat.com", 140 + n);
+ reqmem[n].ar_service = NULL;
+ reqmem[n].ar_request = NULL;
+ reqmem[n].ar_result = NULL;
+ req[n] = &reqmem[n];
+ }
+
+ if (getaddrinfo_a (GAI_NOWAIT, req, N, NULL) != 0)
+ {
+ puts ("queue call failed");
+ exit (1);
+ }
+ else
+ puts ("queue call successful");
+
+ while (1)
+ {
+ int any = 0;
+
+ for (n = 0; n < N; ++n)
+ if (req[n] != NULL && gai_error (req[n]) != EAI_INPROGRESS)
+ {
+ if (gai_error (req[n]) == 0)
+ {
+ struct addrinfo *runp = req[n]->ar_result;
+
+ while (runp != NULL)
+ {
+ switch (runp->ai_family)
+ {
+ case PF_INET:
+ {
+ struct sockaddr_in *sinp;
+
+ sinp = (struct sockaddr_in *) runp->ai_addr;
+ printf ("%2d: %s = %s\n", n,
+ req[n]->ar_name, inet_ntoa (sinp->sin_addr));
+ }
+ break;
+ default:
+ printf ("%2d: family %d\n", n, runp->ai_family);
+ break;
+ }
+ runp = runp->ai_next;
+ }
+ }
+ else
+ printf ("error for %d: %s\n", n,
+ gai_strerror (gai_error (req[n])));
+ req[n] = NULL;
+ break;
+ }
+ else if (req[n] != NULL)
+ any = 1;
+
+ if (n == N)
+ {
+ if (any)
+ gai_suspend (req, N, NULL);
+ else
+ break;
+ }
+ }
+
+ __libc_write(1,"got all\n", 8);
+
+ for (n = 0; n < N; ++n)
+ if (gai_error (&reqmem[n]) == 0)
+ {
+ struct addrinfo *runp = reqmem[n].ar_result;
+
+ while (runp != NULL)
+ {
+ struct addrinfo *oldp = runp;
+ runp = runp->ai_next;
+ freeaddrinfo (oldp);
+ }
+ }
+
+ return 0;
+}
diff --git a/resolv/gai_cancel.c b/resolv/gai_cancel.c
new file mode 100644
index 0000000..13c6488
--- /dev/null
+++ b/resolv/gai_cancel.c
@@ -0,0 +1,48 @@
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <netdb.h>
+#include <pthread.h>
+
+#include "gai_misc.h"
+
+
+int
+gai_cancel (struct gaicb *gaicbp)
+{
+ int result = 0;
+ int status;
+
+ /* Request the mutex. */
+ pthread_mutex_lock (&__gai_requests_mutex);
+
+ /* Find the request among those queued but not yet running. */
+ status = __gai_remove_request (gaicbp);
+ if (status == 0)
+ result = EAI_CANCELED;
+ else if (status > 0)
+ result = EAI_NOTCANCELED;
+ else
+ result = EAI_ALLDONE;
+
+ /* Release the mutex. */
+ pthread_mutex_unlock (&__gai_requests_mutex);
+
+ return result;
+}
diff --git a/resolv/gai_error.c b/resolv/gai_error.c
new file mode 100644
index 0000000..f3d36c5
--- /dev/null
+++ b/resolv/gai_error.c
@@ -0,0 +1,28 @@
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <netdb.h>
+
+#include "gai_misc.h"
+
+int
+gai_error (struct gaicb *req)
+{
+ return req->__return;
+}
diff --git a/resolv/gai_misc.c b/resolv/gai_misc.c
new file mode 100644
index 0000000..b69a8f6
--- /dev/null
+++ b/resolv/gai_misc.c
@@ -0,0 +1,423 @@
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+#include "gai_misc.h"
+
+
+
+/* Pool of request list entries. */
+static struct requestlist **pool;
+
+/* Number of total and allocated pool entries. */
+static size_t pool_max_size;
+static size_t pool_size;
+
+/* We implement a two dimensional array but allocate each row separately.
+ The macro below determines how many entries should be used per row.
+ It should better be a power of two. */
+#define ENTRIES_PER_ROW 32
+
+/* How many rows we allocate at once. */
+#define ROWS_STEP 8
+
+/* List of available entries. */
+static struct requestlist *freelist;
+
+/* Structure list of all currently processed requests. */
+static struct requestlist *requests;
+static struct requestlist *requests_tail;
+
+/* Number of threads currently running. */
+static int nthreads;
+
+/* Number of threads waiting for work to arrive. */
+static int idle_thread_count;
+
+
+/* These are the values used for optimization. We will probably
+ create a funcion to set these values. */
+static struct gaiinit optim =
+{
+ 20, /* int gai_threads; Maximal number of threads. */
+ 64, /* int gai_num; Number of expected simultanious requests. */
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0
+};
+
+
+/* Since the list is global we need a mutex protecting it. */
+pthread_mutex_t __gai_requests_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+
+/* When you add a request to the list and there are idle threads present,
+ you signal this condition variable. When a thread finishes work, it waits
+ on this condition variable for a time before it actually exits. */
+pthread_cond_t __gai_new_request_notification = PTHREAD_COND_INITIALIZER;
+
+
+/* Functions to handle request list pool. */
+static struct requestlist *
+get_elem (void)
+{
+ struct requestlist *result;
+
+ if (freelist == NULL)
+ {
+ struct requestlist *new_row;
+ int cnt;
+
+ if (pool_size + 1 >= pool_max_size)
+ {
+ size_t new_max_size = pool_max_size + ROWS_STEP;
+ struct requestlist **new_tab;
+
+ new_tab = (struct requestlist **)
+ realloc (pool, new_max_size * sizeof (struct requestlist *));
+
+ if (new_tab == NULL)
+ return NULL;
+
+ pool_max_size = new_max_size;
+ pool = new_tab;
+ }
+
+ /* Allocate the new row. */
+ cnt = pool_size == 0 ? optim.gai_num : ENTRIES_PER_ROW;
+ new_row = (struct requestlist *) calloc (cnt,
+ sizeof (struct requestlist));
+ if (new_row == NULL)
+ return NULL;
+
+ pool[pool_size++] = new_row;
+
+ /* Put all the new entries in the freelist. */
+ do
+ {
+ new_row->next = freelist;
+ freelist = new_row++;
+ }
+ while (--cnt > 0);
+ }
+
+ result = freelist;
+ freelist = freelist->next;
+
+ return result;
+}
+
+
+struct requestlist *
+internal_function
+__gai_find_request (const struct gaicb *gaicbp)
+{
+ struct requestlist *runp;
+
+ runp = requests;
+ while (runp != NULL)
+ if (runp->gaicbp == gaicbp)
+ return runp;
+ else
+ runp = runp->next;
+
+ return NULL;
+}
+
+
+int
+internal_function
+__gai_remove_request (struct gaicb *gaicbp)
+{
+ struct requestlist *runp;
+ struct requestlist *lastp;
+
+ runp = requests;
+ lastp = NULL;
+ while (runp != NULL)
+ if (runp->gaicbp == gaicbp)
+ break;
+ else
+ {
+ lastp = runp;
+ runp = runp->next;
+ }
+
+ if (runp == NULL)
+ /* Not known. */
+ return -1;
+ if (runp->running != 0)
+ /* Currently handled. */
+ return 1;
+
+ /* Dequeue the request. */
+ if (lastp == NULL)
+ requests = runp->next;
+ else
+ lastp->next = runp->next;
+ if (runp == requests_tail)
+ requests_tail = lastp;
+
+ return 0;
+}
+
+
+/* The thread handler. */
+static void *handle_requests (void *arg);
+
+
+/* The main function of the async I/O handling. It enqueues requests
+ and if necessary starts and handles threads. */
+struct requestlist *
+internal_function
+__gai_enqueue_request (struct gaicb *gaicbp)
+{
+ struct requestlist *newp;
+ struct requestlist *lastp;
+
+ /* Get the mutex. */
+ pthread_mutex_lock (&__gai_requests_mutex);
+
+ /* Get a new element for the waiting list. */
+ newp = get_elem ();
+ if (newp == NULL)
+ {
+ pthread_mutex_unlock (&__gai_requests_mutex);
+ __set_errno (EAGAIN);
+ return NULL;
+ }
+ newp->running = 0;
+ newp->gaicbp = gaicbp;
+ newp->waiting = NULL;
+ newp->next = NULL;
+
+ lastp = requests_tail;
+ if (requests_tail == NULL)
+ requests = requests_tail = newp;
+ else
+ {
+ requests_tail->next = newp;
+ requests_tail = newp;
+ }
+
+ gaicbp->__return = EAI_INPROGRESS;
+
+ /* See if we need to and are able to create a thread. */
+ if (nthreads < optim.gai_threads && idle_thread_count == 0)
+ {
+ pthread_t thid;
+ pthread_attr_t attr;
+
+ newp->running = 1;
+
+ /* Make sure the thread is created detached. */
+ pthread_attr_init (&attr);
+ pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+
+ /* Now try to start a thread. */
+ if (pthread_create (&thid, &attr, handle_requests, newp) == 0)
+ /* We managed to enqueue the request. All errors which can
+ happen now can be recognized by calls to `gai_error'. */
+ ++nthreads;
+ else
+ {
+ if (nthreads == 0)
+ {
+ /* We cannot create a thread in the moment and there is
+ also no thread running. This is a problem. `errno' is
+ set to EAGAIN if this is only a temporary problem. */
+ assert (lastp->next == newp);
+ lastp->next = NULL;
+ requests_tail = lastp;
+
+ newp->next = freelist;
+ freelist = newp;
+
+ newp = NULL;
+ }
+ else
+ /* We are not handling the request after all. */
+ newp->running = 0;
+ }
+ }
+
+ /* Enqueue the request in the request queue. */
+ if (newp != NULL)
+ {
+ /* If there is a thread waiting for work, then let it know that we
+ have just given it something to do. */
+ if (idle_thread_count > 0)
+ pthread_cond_signal (&__gai_new_request_notification);
+ }
+
+ /* Release the mutex. */
+ pthread_mutex_unlock (&__gai_requests_mutex);
+
+ return newp;
+}
+
+
+static void *
+handle_requests (void *arg)
+{
+ struct requestlist *runp = (struct requestlist *) arg;
+
+ do
+ {
+ /* If runp is NULL, then we were created to service the work queue
+ in general, not to handle any particular request. In that case we
+ skip the "do work" stuff on the first pass, and go directly to the
+ "get work off the work queue" part of this loop, which is near the
+ end. */
+ if (runp == NULL)
+ pthread_mutex_lock (&__gai_requests_mutex);
+ else
+ {
+ /* Make the request. */
+ struct gaicb *req = runp->gaicbp;
+ struct requestlist *srchp;
+ struct requestlist *lastp;
+
+ req->__return = getaddrinfo (req->ar_name, req->ar_service,
+ req->ar_request, &req->ar_result);
+
+ /* Get the mutex. */
+ pthread_mutex_lock (&__gai_requests_mutex);
+
+ /* Send the signal to notify about finished processing of the
+ request. */
+ __gai_notify (runp);
+
+ /* Now dequeue the current request. */
+ lastp = NULL;
+ srchp = requests;
+ while (srchp != runp)
+ {
+ lastp = srchp;
+ srchp = srchp->next;
+ }
+ assert (runp->running == 1);
+
+ if (requests_tail == runp)
+ requests_tail = lastp;
+ if (lastp == NULL)
+ requests = requests->next;
+ else
+ lastp->next = runp->next;
+
+ /* Free the old element. */
+ runp->next = freelist;
+ freelist = runp;
+ }
+
+ runp = requests;
+ while (runp != NULL && runp->running != 0)
+ runp = runp->next;
+
+ /* If the runlist is empty, then we sleep for a while, waiting for
+ something to arrive in it. */
+ if (runp == NULL && optim.gai_idle_time >= 0)
+ {
+ struct timeval now;
+ struct timespec wakeup_time;
+
+ ++idle_thread_count;
+ gettimeofday (&now, NULL);
+ wakeup_time.tv_sec = now.tv_sec + optim.gai_idle_time;
+ wakeup_time.tv_nsec = now.tv_usec * 1000;
+ if (wakeup_time.tv_nsec > 1000000000)
+ {
+ wakeup_time.tv_nsec -= 1000000000;
+ ++wakeup_time.tv_sec;
+ }
+ pthread_cond_timedwait (&__gai_new_request_notification,
+ &__gai_requests_mutex, &wakeup_time);
+ --idle_thread_count;
+ runp = requests;
+ while (runp != NULL && runp->running != 0)
+ runp = runp->next;
+ }
+
+ if (runp == NULL)
+ --nthreads;
+ else
+ {
+ /* Mark the request as being worked on. */
+ assert (runp->running == 0);
+ runp->running = 1;
+
+ /* If we have a request to process, and there's still another in
+ the run list, then we need to either wake up or create a new
+ thread to service the request that is still in the run list. */
+ if (requests != NULL)
+ {
+ /* There are at least two items in the work queue to work on.
+ If there are other idle threads, then we should wake them
+ up for these other work elements; otherwise, we should try
+ to create a new thread. */
+ if (idle_thread_count > 0)
+ pthread_cond_signal (&__gai_new_request_notification);
+ else if (nthreads < optim.gai_threads)
+ {
+ pthread_t thid;
+ pthread_attr_t attr;
+
+ /* Make sure the thread is created detached. */
+ pthread_attr_init (&attr);
+ pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+
+ /* Now try to start a thread. If we fail, no big deal,
+ because we know that there is at least one thread (us)
+ that is working on lookup operations. */
+ if (pthread_create (&thid, &attr, handle_requests, NULL)
+ == 0)
+ ++nthreads;
+ }
+ }
+ }
+
+ /* Release the mutex. */
+ pthread_mutex_unlock (&__gai_requests_mutex);
+ }
+ while (runp != NULL);
+
+ pthread_exit (NULL);
+}
+
+
+/* Free allocated resources. */
+static void
+__attribute__ ((unused))
+free_res (void)
+{
+ size_t row;
+
+ for (row = 0; row < pool_max_size; ++row)
+ free (pool[row]);
+
+ free (pool);
+}
+text_set_element (__libc_subfreeres, free_res);
diff --git a/resolv/gai_misc.h b/resolv/gai_misc.h
new file mode 100644
index 0000000..a37264e
--- /dev/null
+++ b/resolv/gai_misc.h
@@ -0,0 +1,100 @@
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifndef _GAI_MISC_H
+#define _GAI_MISC_H 1
+
+#include <netdb.h>
+#include <signal.h>
+
+
+/* Used to synchronize. */
+struct waitlist
+ {
+ struct waitlist *next;
+
+ pthread_cond_t *cond;
+ volatile int *counterp;
+ /* The next field is used in asynchronous `lio_listio' operations. */
+ struct sigevent *sigevp;
+ /* XXX See requestlist, it's used to work around the broken signal
+ handling in Linux. */
+ pid_t caller_pid;
+ };
+
+
+/* Used to queue requests.. */
+struct requestlist
+ {
+ int running;
+
+ struct requestlist *next;
+
+ /* Pointer to the actual data. */
+ struct gaicb *gaicbp;
+
+ /* List of waiting processes. */
+ struct waitlist *waiting;
+ };
+
+/* To customize the implementation one can use the following struct.
+ This implementation follows the one in Irix. */
+struct gaiinit
+ {
+ int gai_threads; /* Maximal number of threads. */
+ int gai_num; /* Number of expected simultanious requests. */
+ int gai_locks; /* Not used. */
+ int gai_usedba; /* Not used. */
+ int gai_debug; /* Not used. */
+ int gai_numusers; /* Not used. */
+ int gai_idle_time; /* Number of seconds before idle thread
+ terminates. */
+ int gai_reserved;
+ };
+
+
+/* Lock for global I/O list of requests. */
+extern pthread_mutex_t __gai_requests_mutex;
+
+
+/* Enqueue request. */
+extern struct requestlist *__gai_enqueue_request (struct gaicb *gaicbp)
+ internal_function;
+
+/* Find request on wait list. */
+extern struct requestlist *__gai_find_request (const struct gaicb *gaicbp)
+ internal_function;
+
+/* Remove request from waitlist. */
+extern int __gai_remove_request (struct gaicb *gaicbp)
+ internal_function;
+
+/* Notify initiator of request and tell this everybody listening. */
+extern void __gai_notify (struct requestlist *req)
+ internal_function;
+
+/* Notify initiator of request. */
+extern int __gai_notify_only (struct sigevent *sigev, pid_t caller_pid)
+ internal_function;
+
+/* Send the signal. */
+extern int __gai_sigqueue (int sig, const union sigval val, pid_t caller_pid)
+ internal_function;
+
+#endif /* gai_misc.h */
diff --git a/resolv/gai_notify.c b/resolv/gai_notify.c
new file mode 100644
index 0000000..b3e623a
--- /dev/null
+++ b/resolv/gai_notify.c
@@ -0,0 +1,100 @@
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <netdb.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#include "gai_misc.h"
+
+
+static void *
+notify_func_wrapper (void *arg)
+{
+ struct sigevent *sigev = arg;
+ sigev->sigev_notify_function (sigev->sigev_value);
+ return NULL;
+}
+
+
+int
+internal_function
+__gai_notify_only (struct sigevent *sigev, pid_t caller_pid)
+{
+ int result = 0;
+
+ /* Send the signal to notify about finished processing of the request. */
+ if (sigev->sigev_notify == SIGEV_THREAD)
+ {
+ /* We have to start a thread. */
+ pthread_t tid;
+ pthread_attr_t attr, *pattr;
+
+ pattr = (pthread_attr_t *) sigev->sigev_notify_attributes;
+ if (pattr == NULL)
+ {
+ pthread_attr_init (&attr);
+ pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+ pattr = &attr;
+ }
+
+ if (pthread_create (&tid, pattr, notify_func_wrapper, sigev) < 0)
+ result = -1;
+ }
+ else if (sigev->sigev_notify == SIGEV_SIGNAL)
+ /* We have to send a signal. */
+ if (__gai_sigqueue (sigev->sigev_signo, sigev->sigev_value, caller_pid)
+ < 0)
+ result = -1;
+
+ return result;
+}
+
+
+void
+internal_function
+__gai_notify (struct requestlist *req)
+{
+ struct waitlist *waitlist;
+
+ /* Now also notify possibly waiting threads. */
+ waitlist = req->waiting;
+ while (waitlist != NULL)
+ {
+ struct waitlist *next = waitlist->next;
+
+ /* Decrement the counter. This is used in both cases. */
+ --*waitlist->counterp;
+
+ if (waitlist->sigevp == NULL)
+ pthread_cond_signal (waitlist->cond);
+ else
+ /* This is part of a asynchronous `getaddrinfo_a' operation. If
+ this request is the last one, send the signal. */
+ if (*waitlist->counterp == 0)
+ {
+ __gai_notify_only (waitlist->sigevp, waitlist->caller_pid);
+ /* This is tricky. See getaddrinfo_a.c for the reason why
+ this works. */
+ free ((void *) waitlist->counterp);
+ }
+
+ waitlist = next;
+ }
+}
diff --git a/resolv/gai_suspend.c b/resolv/gai_suspend.c
new file mode 100644
index 0000000..b82acb4
--- /dev/null
+++ b/resolv/gai_suspend.c
@@ -0,0 +1,147 @@
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <errno.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+#include "gai_misc.h"
+
+
+int
+gai_suspend (const struct gaicb *const list[], int ent,
+ const struct timespec *timeout)
+{
+ struct waitlist waitlist[ent];
+ struct requestlist *requestlist[ent];
+ pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+ int cnt;
+ int dummy;
+ int none = 1;
+ int result;
+
+ /* Request the mutex. */
+ pthread_mutex_lock (&__gai_requests_mutex);
+
+ /* There is not yet a finished request. Signal the request that
+ we are working for it. */
+ for (cnt = 0; cnt < ent; ++cnt)
+ if (list[cnt] != NULL && list[cnt]->__return == EAI_INPROGRESS)
+ {
+ requestlist[cnt] = __gai_find_request (list[cnt]);
+
+ if (requestlist[cnt] != NULL)
+ {
+ waitlist[cnt].cond = &cond;
+ waitlist[cnt].next = requestlist[cnt]->waiting;
+ waitlist[cnt].counterp = &dummy;
+ waitlist[cnt].sigevp = NULL;
+ waitlist[cnt].caller_pid = 0; /* Not needed. */
+ requestlist[cnt]->waiting = &waitlist[cnt];
+ none = 0;
+ }
+ }
+
+ if (none)
+ {
+ if (cnt < ent)
+ /* There is an entry which is finished. */
+ result = 0;
+ else
+ result = EAI_ALLDONE;
+ }
+ else
+ {
+ /* There is no request done but some are still being worked on. */
+ int oldstate;
+
+ /* Since `pthread_cond_wait'/`pthread_cond_timedwait' are cancelation
+ points we must be careful. We added entries to the waiting lists
+ which we must remove. So defer cancelation for now. */
+ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &oldstate);
+
+ if (timeout == NULL)
+ result = pthread_cond_wait (&cond, &__gai_requests_mutex);
+ else
+ {
+ /* We have to convert the relative timeout value into an
+ absolute time value with pthread_cond_timedwait expects. */
+ struct timeval now;
+ struct timespec abstime;
+
+ __gettimeofday (&now, NULL);
+ abstime.tv_nsec = timeout->tv_nsec + now.tv_usec * 1000;
+ abstime.tv_sec = timeout->tv_sec + now.tv_sec;
+ if (abstime.tv_nsec >= 1000000000)
+ {
+ abstime.tv_nsec -= 1000000000;
+ abstime.tv_sec += 1;
+ }
+
+ result = pthread_cond_timedwait (&cond, &__gai_requests_mutex,
+ &abstime);
+ }
+
+ /* Now remove the entry in the waiting list for all requests
+ which didn't terminate. */
+ for (cnt = 0; cnt < ent; ++cnt)
+ if (list[cnt] != NULL && list[cnt]->__return == EAI_INPROGRESS
+ && requestlist[cnt] != NULL)
+ {
+ struct waitlist **listp = &requestlist[cnt]->waiting;
+
+ /* There is the chance that we cannot find our entry anymore.
+ This could happen if the request terminated and restarted
+ again. */
+ while (*listp != NULL && *listp != &waitlist[cnt])
+ listp = &(*listp)->next;
+
+ if (*listp != NULL)
+ *listp = (*listp)->next;
+ }
+
+ /* Now it's time to restore the cancelation state. */
+ pthread_setcancelstate (oldstate, NULL);
+
+ /* Release the conditional variable. */
+ if (pthread_cond_destroy (&cond) != 0)
+ /* This must never happen. */
+ abort ();
+
+ if (result != 0)
+ {
+ /* An error occurred. Possibly it's EINTR. We have to translate
+ the timeout error report of `pthread_cond_timedwait' to the
+ form expected from `gai_suspend'. */
+ if (__builtin_expect (result, ETIMEDOUT) == ETIMEDOUT)
+ result = EAI_AGAIN;
+ else if (result == EINTR)
+ result = EAI_INTR;
+ else
+ result = EAI_SYSTEM;
+ }
+ }
+
+ /* Release the mutex. */
+ pthread_mutex_unlock (&__gai_requests_mutex);
+
+ return result;
+}
diff --git a/resolv/getaddrinfo_a.c b/resolv/getaddrinfo_a.c
new file mode 100644
index 0000000..1a077d1
--- /dev/null
+++ b/resolv/getaddrinfo_a.c
@@ -0,0 +1,168 @@
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <errno.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "gai_misc.h"
+
+
+/* We need this special structure to handle asynchronous I/O. */
+struct async_waitlist
+ {
+ int counter;
+ struct sigevent sigev;
+ struct waitlist list[0];
+ };
+
+
+int
+getaddrinfo_a (int mode, struct gaicb *list[], int ent, struct sigevent *sig)
+{
+ struct sigevent defsigev;
+ struct requestlist *requests[ent];
+ int cnt;
+ volatile int total = 0;
+ int result = 0;
+
+ /* Check arguments. */
+ if (mode != GAI_WAIT && mode != GAI_NOWAIT)
+ {
+ __set_errno (EINVAL);
+ return EAI_SYSTEM;
+ }
+
+ if (sig == NULL)
+ {
+ defsigev.sigev_notify = SIGEV_NONE;
+ sig = &defsigev;
+ }
+
+ /* Request the mutex. */
+ pthread_mutex_lock (&__gai_requests_mutex);
+
+ /* Now we can enqueue all requests. Since we already acquired the
+ mutex the enqueue function need not do this. */
+ for (cnt = 0; cnt < ent; ++cnt)
+ if (list[cnt] != NULL)
+ {
+ requests[cnt] = __gai_enqueue_request (list[cnt]);
+
+ if (requests[cnt] != NULL)
+ /* Successfully enqueued. */
+ ++total;
+ else
+ /* Signal that we've seen an error. `errno' and the error code
+ of the gaicb will tell more. */
+ result = EAI_SYSTEM;
+ }
+ else
+ requests[cnt] = NULL;
+
+ if (total == 0)
+ {
+ /* We don't have anything to do except signalling if we work
+ asynchronously. */
+
+ /* Release the mutex. We do this before raising a signal since the
+ signal handler might do a `siglongjmp' and then the mutex is
+ locked forever. */
+ pthread_mutex_unlock (&__gai_requests_mutex);
+
+ if (mode == GAI_NOWAIT)
+ __gai_notify_only (sig,
+ sig->sigev_notify == SIGEV_SIGNAL ? getpid () : 0);
+
+ return result;
+ }
+ else if (mode == GAI_WAIT)
+ {
+ pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+ struct waitlist waitlist[ent];
+ int oldstate;
+
+ total = 0;
+ for (cnt = 0; cnt < ent; ++cnt)
+ if (requests[cnt] != NULL)
+ {
+ waitlist[cnt].cond = &cond;
+ waitlist[cnt].next = requests[cnt]->waiting;
+ waitlist[cnt].counterp = &total;
+ waitlist[cnt].sigevp = NULL;
+ waitlist[cnt].caller_pid = 0; /* Not needed. */
+ requests[cnt]->waiting = &waitlist[cnt];
+ ++total;
+ }
+
+ /* Since `pthread_cond_wait'/`pthread_cond_timedwait' are cancelation
+ points we must be careful. We added entries to the waiting lists
+ which we must remove. So defer cancelation for now. */
+ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &oldstate);
+
+ while (total > 0)
+ pthread_cond_wait (&cond, &__gai_requests_mutex);
+
+ /* Now it's time to restore the cancelation state. */
+ pthread_setcancelstate (oldstate, NULL);
+
+ /* Release the conditional variable. */
+ if (pthread_cond_destroy (&cond) != 0)
+ /* This must never happen. */
+ abort ();
+ }
+ else
+ {
+ struct async_waitlist *waitlist;
+
+ waitlist = (struct async_waitlist *)
+ malloc (sizeof (struct async_waitlist)
+ + (ent * sizeof (struct waitlist)));
+
+ if (waitlist == NULL)
+ result = EAI_AGAIN;
+ else
+ {
+ pid_t caller_pid = sig->sigev_notify == SIGEV_SIGNAL ? getpid () : 0;
+ total = 0;
+
+ for (cnt = 0; cnt < ent; ++cnt)
+ if (requests[cnt] != NULL)
+ {
+ waitlist->list[cnt].cond = NULL;
+ waitlist->list[cnt].next = requests[cnt]->waiting;
+ waitlist->list[cnt].counterp = &waitlist->counter;
+ waitlist->list[cnt].sigevp = &waitlist->sigev;
+ waitlist->list[cnt].caller_pid = caller_pid;
+ requests[cnt]->waiting = &waitlist->list[cnt];
+ ++total;
+ }
+
+ waitlist->counter = total;
+ waitlist->sigev = *sig;
+ }
+ }
+
+ /* Release the mutex. */
+ pthread_mutex_unlock (&__gai_requests_mutex);
+
+ return result;
+}
diff --git a/resolv/netdb.h b/resolv/netdb.h
index ae3f95a..79c147e 100644
--- a/resolv/netdb.h
+++ b/resolv/netdb.h
@@ -33,6 +33,11 @@
# include <rpc/netdb.h>
#endif
+#ifdef __USE_GNU
+# define __need_sigevent_t
+# include <bits/siginfo.h>
+#endif
+
#include <bits/netdb.h>
/* Absolute file name for network data base files. */
@@ -412,23 +417,48 @@ struct addrinfo
struct addrinfo *ai_next; /* Pointer to next in list. */
};
+# ifdef __USE_GNU
+/* Structure used as control block for asynchronous lookup. */
+struct gaicb
+{
+ const char *ar_name; /* Name to look up. */
+ const char *ar_service; /* Service name. */
+ const struct addrinfo *ar_request; /* Additional request specification. */
+ struct addrinfo *ar_result; /* Pointer to result. */
+ /* The following are internal elements. */
+ int __return;
+ int __unused[5];
+};
+
+/* Lookup mode. */
+# define GAI_WAIT 0
+# define GAI_NOWAIT 1
+# endif
+
/* Possible values for `ai_flags' field in `addrinfo' structure. */
# define AI_PASSIVE 0x0001 /* Socket address is intended for `bind'. */
# define AI_CANONNAME 0x0002 /* Request for canonical name. */
# define AI_NUMERICHOST 0x0004 /* Don't use name resolution. */
/* Error values for `getaddrinfo' function. */
-# define EAI_BADFLAGS -1 /* Invalid value for `ai_flags' field. */
-# define EAI_NONAME -2 /* NAME or SERVICE is unknown. */
-# define EAI_AGAIN -3 /* Temporary failure in name resolution. */
-# define EAI_FAIL -4 /* Non-recoverable failure in name res. */
-# define EAI_NODATA -5 /* No address associated with NAME. */
-# define EAI_FAMILY -6 /* `ai_family' not supported. */
-# define EAI_SOCKTYPE -7 /* `ai_socktype' not supported. */
-# define EAI_SERVICE -8 /* SERVICE not supported for `ai_socktype'. */
-# define EAI_ADDRFAMILY -9 /* Address family for NAME not supported. */
-# define EAI_MEMORY -10 /* Memory allocation failure. */
-# define EAI_SYSTEM -11 /* System error returned in `errno'. */
+# define EAI_BADFLAGS -1 /* Invalid value for `ai_flags' field. */
+# define EAI_NONAME -2 /* NAME or SERVICE is unknown. */
+# define EAI_AGAIN -3 /* Temporary failure in name resolution. */
+# define EAI_FAIL -4 /* Non-recoverable failure in name res. */
+# define EAI_NODATA -5 /* No address associated with NAME. */
+# define EAI_FAMILY -6 /* `ai_family' not supported. */
+# define EAI_SOCKTYPE -7 /* `ai_socktype' not supported. */
+# define EAI_SERVICE -8 /* SERVICE not supported for `ai_socktype'. */
+# define EAI_ADDRFAMILY -9 /* Address family for NAME not supported. */
+# define EAI_MEMORY -10 /* Memory allocation failure. */
+# define EAI_SYSTEM -11 /* System error returned in `errno'. */
+# ifdef __USE_GNU
+# define EAI_INPROGRESS -100 /* Processing request in progress. */
+# define EAI_CANCELED -101 /* Request canceled. */
+# define EAI_NOTCANCELED -102 /* Request not canceled. */
+# define EAI_ALLDONE -103 /* All requests done. */
+# define EAI_INTR -104 /* Interrupted by a signal. */
+# endif
# define NI_MAXHOST 1025
# define NI_MAXSERV 32
@@ -458,6 +488,26 @@ extern int getnameinfo (__const struct sockaddr *__restrict __sa,
socklen_t __hostlen, char *__restrict __serv,
socklen_t __servlen, unsigned int __flags) __THROW;
+# ifdef __USE_GNU
+/* Enqueue ENT requests from the LIST. If MODE is GAI_WAIT wait until all
+ requests are handled. If WAIT is GAI_NOWAIT return immediately after
+ queueing the requests and signal completion according to SIG. */
+extern int getaddrinfo_a (int __mode, struct gaicb *__list[__restrict_arr],
+ int __ent, struct sigevent *__restrict __sig)
+ __THROW;
+
+/* Suspend execution of the thread until at least one of the ENT requests
+ in LIST is handled. If TIMEOUT is not a null pointer it specifies the
+ longest time the function keeps waiting before returning with an error. */
+extern int gai_suspend (__const struct gaicb *__const __list[], int __ent,
+ const struct timespec *__timeout) __THROW;
+
+/* Get the error status of the request REQ. */
+extern int gai_error (struct gaicb *__req) __THROW;
+
+/* Cancel the requests associated with GAICBP. */
+extern int gai_cancel (struct gaicb *__gaicbp) __THROW;
+# endif /* GNU */
#endif /* POSIX */
__END_DECLS