mirror of https://github.com/mkerrisk/man-pages
624 lines
15 KiB
Groff
624 lines
15 KiB
Groff
.\" Copyright (c) 2009 Petr Baudis <pasky@suse.cz>
|
|
.\" and clean-ups and additions (C) Copyright 2010 Michael Kerrisk
|
|
.\" <mtk.manpages@gmail.com>
|
|
.\"
|
|
.\" %%%LICENSE_START(VERBATIM)
|
|
.\" Permission is granted to make and distribute verbatim copies of this
|
|
.\" manual provided the copyright notice and this permission notice are
|
|
.\" preserved on all copies.
|
|
.\"
|
|
.\" Permission is granted to copy and distribute modified versions of this
|
|
.\" manual under the conditions for verbatim copying, provided that the
|
|
.\" entire resulting derived work is distributed under the terms of a
|
|
.\" permission notice identical to this one.
|
|
.\"
|
|
.\" Since the Linux kernel and libraries are constantly changing, this
|
|
.\" manual page may be incorrect or out-of-date. The author(s) assume no
|
|
.\" responsibility for errors or omissions, or for damages resulting from
|
|
.\" the use of the information contained herein. The author(s) may not
|
|
.\" have taken the same level of care in the production of this manual,
|
|
.\" which is licensed free of charge, as they might when working
|
|
.\" professionally.
|
|
.\"
|
|
.\" Formatted or processed versions of this manual, if unaccompanied by
|
|
.\" the source, must acknowledge the copyright and authors of this work.
|
|
.\" %%%LICENSE_END
|
|
.\"
|
|
.\" References: http://people.redhat.com/drepper/asynchnl.pdf,
|
|
.\" http://www.imperialviolet.org/2005/06/01/asynchronous-dns-lookups-with-glibc.html
|
|
.\"
|
|
.TH GETADDRINFO_A 3 2015-07-23 "GNU" "Linux Programmer's Manual"
|
|
.SH NAME
|
|
getaddrinfo_a, gai_suspend, gai_error, gai_cancel \- asynchronous
|
|
network address and service translation
|
|
.SH SYNOPSIS
|
|
.nf
|
|
.BR "#define _GNU_SOURCE" " /* See feature_test_macros(7) */"
|
|
.B #include <netdb.h>
|
|
.sp
|
|
.BI "int getaddrinfo_a(int " "mode" ", struct gaicb *" "list[]" ,
|
|
.BI " int " "nitems" ", struct sigevent *" "sevp" );
|
|
.sp
|
|
.BI "int gai_suspend(const struct gaicb * const " "list[]" ", int " "nitems" ,
|
|
.BI " const struct timespec *" "timeout" );
|
|
.sp
|
|
.BI "int gai_error(struct gaicb *" "req" );
|
|
.sp
|
|
.BI "int gai_cancel(struct gaicb *" "req" );
|
|
.sp
|
|
Link with \fI\-lanl\fP.
|
|
.fi
|
|
.SH DESCRIPTION
|
|
The
|
|
.BR getaddrinfo_a ()
|
|
function performs the same task as
|
|
.BR getaddrinfo (3),
|
|
but allows multiple name look-ups to be performed asynchronously,
|
|
with optional notification on completion of look-up operations.
|
|
|
|
The
|
|
.I mode
|
|
argument has one of the following values:
|
|
.TP
|
|
.B GAI_WAIT
|
|
Perform the look-ups synchronously.
|
|
The call blocks until the look-ups have completed.
|
|
.TP
|
|
.B GAI_NOWAIT
|
|
Perform the look-ups asynchronously.
|
|
The call returns immediately,
|
|
and the requests are resolved in the background.
|
|
See the discussion of the
|
|
.I sevp
|
|
argument below.
|
|
.PP
|
|
The array
|
|
.I list
|
|
specifies the look-up requests to process.
|
|
The
|
|
.I nitems
|
|
argument specifies the number of elements in
|
|
.IR list .
|
|
The requested look-up operations are started in parallel.
|
|
NULL elements in
|
|
.I list
|
|
are ignored.
|
|
Each request is described by a
|
|
.I gaicb
|
|
structure, defined as follows:
|
|
.sp
|
|
.in +4n
|
|
.nf
|
|
struct gaicb {
|
|
const char *ar_name;
|
|
const char *ar_service;
|
|
const struct addrinfo *ar_request;
|
|
struct addrinfo *ar_result;
|
|
};
|
|
.fi
|
|
.in
|
|
|
|
The elements of this structure correspond to the arguments of
|
|
.BR getaddrinfo (3).
|
|
Thus,
|
|
.I ar_name
|
|
corresponds to the
|
|
.I node
|
|
argument and
|
|
.I ar_service
|
|
to the
|
|
.I service
|
|
argument, identifying an Internet host and a service.
|
|
The
|
|
.I ar_request
|
|
element corresponds to the
|
|
.I hints
|
|
argument, specifying the criteria for selecting
|
|
the returned socket address structures.
|
|
Finally,
|
|
.I ar_result
|
|
corresponds to the
|
|
.I res
|
|
argument; you do not need to initialize this element,
|
|
it will be automatically set when the request
|
|
is resolved.
|
|
The
|
|
.I addrinfo
|
|
structure referenced by the last two elements is described in
|
|
.BR getaddrinfo (3).
|
|
|
|
When
|
|
.I mode
|
|
is specified as
|
|
.BR GAI_NOWAIT ,
|
|
notifications about resolved requests
|
|
can be obtained by employing the
|
|
.I sigevent
|
|
structure pointed to by the
|
|
.I sevp
|
|
argument.
|
|
For the definition and general details of this structure, see
|
|
.BR sigevent (7).
|
|
The
|
|
.I sevp\->sigev_notify
|
|
field can have the following values:
|
|
.TP
|
|
.BR SIGEV_NONE
|
|
Don't provide any notification.
|
|
.TP
|
|
.BR SIGEV_SIGNAL
|
|
When a look-up completes, generate the signal
|
|
.I sigev_signo
|
|
for the process.
|
|
See
|
|
.BR sigevent (7)
|
|
for general details.
|
|
The
|
|
.I si_code
|
|
field of the
|
|
.I siginfo_t
|
|
structure will be set to
|
|
.BR SI_ASYNCNL .
|
|
.\" si_pid and si_uid are also set, to the values of the calling process,
|
|
.\" which doesn't provide useful information, so we'll skip mentioning it.
|
|
.TP
|
|
.BR SIGEV_THREAD
|
|
When a look-up completes, invoke
|
|
.I sigev_notify_function
|
|
as if it were the start function of a new thread.
|
|
See
|
|
.BR sigevent (7)
|
|
for details.
|
|
.PP
|
|
For
|
|
.BR SIGEV_SIGNAL
|
|
and
|
|
.BR SIGEV_THREAD ,
|
|
it may be useful to point
|
|
.IR sevp\->sigev_value.sival_ptr
|
|
to
|
|
.IR list .
|
|
|
|
The
|
|
.BR gai_suspend ()
|
|
function suspends execution of the calling thread,
|
|
waiting for the completion of one or more requests in the array
|
|
.IR list .
|
|
The
|
|
.I nitems
|
|
argument specifies the size of the array
|
|
.IR list .
|
|
The call blocks until one of the following occurs:
|
|
.IP * 3
|
|
One or more of the operations in
|
|
.I list
|
|
completes.
|
|
.IP *
|
|
The call is interrupted by a signal that is caught.
|
|
.IP *
|
|
The time interval specified in
|
|
.I timeout
|
|
elapses.
|
|
This argument specifies a timeout in seconds plus nanoseconds (see
|
|
.BR nanosleep (2)
|
|
for details of the
|
|
.I timespec
|
|
structure).
|
|
If
|
|
.I timeout
|
|
is NULL, then the call blocks indefinitely
|
|
(until one of the events above occurs).
|
|
.PP
|
|
No explicit indication of which request was completed is given;
|
|
you must determine which request(s) have completed by iterating with
|
|
.BR gai_error ()
|
|
over the list of requests.
|
|
|
|
The
|
|
.BR gai_error ()
|
|
function returns the status of the request
|
|
.IR req :
|
|
either
|
|
.B EAI_INPROGRESS
|
|
if the request was not completed yet,
|
|
0 if it was handled successfully,
|
|
or an error code if the request could not be resolved.
|
|
|
|
The
|
|
.BR gai_cancel ()
|
|
function cancels the request
|
|
.IR req .
|
|
If the request has been canceled successfully,
|
|
the error status of the request will be set to
|
|
.B EAI_CANCELED
|
|
and normal asynchronous notification will be performed.
|
|
The request cannot be canceled if it is currently being processed;
|
|
in that case, it will be handled as if
|
|
.BR gai_cancel ()
|
|
has never been called.
|
|
If
|
|
.I req
|
|
is NULL, an attempt is made to cancel all outstanding requests
|
|
that the process has made.
|
|
.SH RETURN VALUE
|
|
The
|
|
.BR getaddrinfo_a ()
|
|
function returns 0 if all of the requests have been enqueued successfully,
|
|
or one of the following nonzero error codes:
|
|
.TP
|
|
.B EAI_AGAIN
|
|
The resources necessary to enqueue the look-up requests were not available.
|
|
The application may check the error status of each
|
|
request to determine which ones failed.
|
|
.TP
|
|
.B EAI_MEMORY
|
|
Out of memory.
|
|
.TP
|
|
.B EAI_SYSTEM
|
|
.I mode
|
|
is invalid.
|
|
.PP
|
|
The
|
|
.BR gai_suspend ()
|
|
function returns 0 if at least one of the listed requests has been completed.
|
|
Otherwise, it returns one of the following nonzero error codes:
|
|
.TP
|
|
.B EAI_AGAIN
|
|
The given timeout expired before any of the requests could be completed.
|
|
.TP
|
|
.B EAI_ALLDONE
|
|
There were no actual requests given to the function.
|
|
.TP
|
|
.B EAI_INTR
|
|
A signal has interrupted the function.
|
|
Note that this interruption might have been
|
|
caused by signal notification of some completed look-up request.
|
|
.PP
|
|
The
|
|
.BR gai_error ()
|
|
function can return
|
|
.B EAI_INPROGRESS
|
|
for an unfinished look-up request,
|
|
0 for a successfully completed look-up
|
|
(as described above), one of the error codes that could be returned by
|
|
.BR getaddrinfo (3),
|
|
or the error code
|
|
.B EAI_CANCELED
|
|
if the request has been canceled explicitly before it could be finished.
|
|
|
|
The
|
|
.BR gai_cancel ()
|
|
function can return one of these values:
|
|
.TP
|
|
.B EAI_CANCELED
|
|
The request has been canceled successfully.
|
|
.TP
|
|
.B EAI_NOTCANCELED
|
|
The request has not been canceled.
|
|
.TP
|
|
.B EAI_ALLDONE
|
|
The request has already completed.
|
|
.PP
|
|
The
|
|
.BR gai_strerror (3)
|
|
function translates these error codes to a human readable string,
|
|
suitable for error reporting.
|
|
.SH ATTRIBUTES
|
|
For an explanation of the terms used in this section, see
|
|
.BR attributes (7).
|
|
.TS
|
|
allbox;
|
|
lbw31 lb lb
|
|
l l l.
|
|
Interface Attribute Value
|
|
T{
|
|
.BR getaddrinfo_a (),
|
|
.BR gai_suspend (),
|
|
.BR gai_error (),
|
|
.BR gai_cancel ()
|
|
T} Thread safety MT-Safe
|
|
.TE
|
|
|
|
.SH CONFORMING TO
|
|
These functions are GNU extensions;
|
|
they first appeared in glibc in version 2.2.3.
|
|
.SH NOTES
|
|
The interface of
|
|
.BR getaddrinfo_a ()
|
|
was modeled after the
|
|
.BR lio_listio (3)
|
|
interface.
|
|
.SH EXAMPLE
|
|
Two examples are provided: a simple example that resolves
|
|
several requests in parallel synchronously, and a complex example
|
|
showing some of the asynchronous capabilities.
|
|
.SS Synchronous example
|
|
The program below simply resolves several hostnames in parallel,
|
|
giving a speed-up compared to resolving the hostnames sequentially using
|
|
.BR getaddrinfo (3).
|
|
The program might be used like this:
|
|
.in +4n
|
|
.nf
|
|
|
|
$ \fB./a.out ftp.us.kernel.org enoent.linuxfoundation.org gnu.cz\fP
|
|
ftp.us.kernel.org: 128.30.2.36
|
|
enoent.linuxfoundation.org: Name or service not known
|
|
gnu.cz: 87.236.197.13
|
|
.fi
|
|
.in
|
|
.PP
|
|
Here is the program source code
|
|
.nf
|
|
|
|
#define _GNU_SOURCE
|
|
#include <netdb.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
int i, ret;
|
|
struct gaicb *reqs[argc \- 1];
|
|
char host[NI_MAXHOST];
|
|
struct addrinfo *res;
|
|
|
|
if (argc < 2) {
|
|
fprintf(stderr, "Usage: %s HOST...\\n", argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
for (i = 0; i < argc \- 1; i++) {
|
|
reqs[i] = malloc(sizeof(*reqs[0]));
|
|
if (reqs[i] == NULL) {
|
|
perror("malloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
memset(reqs[i], 0, sizeof(*reqs[0]));
|
|
reqs[i]\->ar_name = argv[i + 1];
|
|
}
|
|
|
|
ret = getaddrinfo_a(GAI_WAIT, reqs, argc \- 1, NULL);
|
|
if (ret != 0) {
|
|
fprintf(stderr, "getaddrinfo_a() failed: %s\\n",
|
|
gai_strerror(ret));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
for (i = 0; i < argc \- 1; i++) {
|
|
printf("%s: ", reqs[i]\->ar_name);
|
|
ret = gai_error(reqs[i]);
|
|
if (ret == 0) {
|
|
res = reqs[i]\->ar_result;
|
|
|
|
ret = getnameinfo(res\->ai_addr, res\->ai_addrlen,
|
|
host, sizeof(host),
|
|
NULL, 0, NI_NUMERICHOST);
|
|
if (ret != 0) {
|
|
fprintf(stderr, "getnameinfo() failed: %s\\n",
|
|
gai_strerror(ret));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
puts(host);
|
|
|
|
} else {
|
|
puts(gai_strerror(ret));
|
|
}
|
|
}
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
.fi
|
|
.SS Asynchronous example
|
|
This example shows a simple interactive
|
|
.BR getaddrinfo_a ()
|
|
front-end.
|
|
The notification facility is not demonstrated.
|
|
.PP
|
|
An example session might look like this:
|
|
.in +4n
|
|
.nf
|
|
|
|
$ \fB./a.out\fP
|
|
> a ftp.us.kernel.org enoent.linuxfoundation.org gnu.cz
|
|
> c 2
|
|
[2] gnu.cz: Request not canceled
|
|
> w 0 1
|
|
[00] ftp.us.kernel.org: Finished
|
|
> l
|
|
[00] ftp.us.kernel.org: 216.165.129.139
|
|
[01] enoent.linuxfoundation.org: Processing request in progress
|
|
[02] gnu.cz: 87.236.197.13
|
|
> l
|
|
[00] ftp.us.kernel.org: 216.165.129.139
|
|
[01] enoent.linuxfoundation.org: Name or service not known
|
|
[02] gnu.cz: 87.236.197.13
|
|
.fi
|
|
.in
|
|
.PP
|
|
The program source is as follows:
|
|
|
|
.nf
|
|
#define _GNU_SOURCE
|
|
#include <netdb.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
static struct gaicb **reqs = NULL;
|
|
static int nreqs = 0;
|
|
|
|
static char *
|
|
getcmd(void)
|
|
{
|
|
static char buf[256];
|
|
|
|
fputs("> ", stdout); fflush(stdout);
|
|
if (fgets(buf, sizeof(buf), stdin) == NULL)
|
|
return NULL;
|
|
|
|
if (buf[strlen(buf) \- 1] == \(aq\\n\(aq)
|
|
buf[strlen(buf) \- 1] = 0;
|
|
|
|
return buf;
|
|
}
|
|
|
|
/* Add requests for specified hostnames */
|
|
static void
|
|
add_requests(void)
|
|
{
|
|
int nreqs_base = nreqs;
|
|
char *host;
|
|
int ret;
|
|
|
|
while ((host = strtok(NULL, " "))) {
|
|
nreqs++;
|
|
reqs = realloc(reqs, nreqs * sizeof(reqs[0]));
|
|
|
|
reqs[nreqs \- 1] = calloc(1, sizeof(*reqs[0]));
|
|
reqs[nreqs \- 1]\->ar_name = strdup(host);
|
|
}
|
|
|
|
/* Queue nreqs_base..nreqs requests. */
|
|
|
|
ret = getaddrinfo_a(GAI_NOWAIT, &reqs[nreqs_base],
|
|
nreqs \- nreqs_base, NULL);
|
|
if (ret) {
|
|
fprintf(stderr, "getaddrinfo_a() failed: %s\\n",
|
|
gai_strerror(ret));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/* Wait until at least one of specified requests completes */
|
|
static void
|
|
wait_requests(void)
|
|
{
|
|
char *id;
|
|
int i, ret, n;
|
|
struct gaicb const **wait_reqs = calloc(nreqs, sizeof(*wait_reqs));
|
|
/* NULL elements are ignored by gai_suspend(). */
|
|
|
|
while ((id = strtok(NULL, " ")) != NULL) {
|
|
n = atoi(id);
|
|
|
|
if (n >= nreqs) {
|
|
printf("Bad request number: %s\\n", id);
|
|
return;
|
|
}
|
|
|
|
wait_reqs[n] = reqs[n];
|
|
}
|
|
|
|
ret = gai_suspend(wait_reqs, nreqs, NULL);
|
|
if (ret) {
|
|
printf("gai_suspend(): %s\\n", gai_strerror(ret));
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < nreqs; i++) {
|
|
if (wait_reqs[i] == NULL)
|
|
continue;
|
|
|
|
ret = gai_error(reqs[i]);
|
|
if (ret == EAI_INPROGRESS)
|
|
continue;
|
|
|
|
printf("[%02d] %s: %s\\n", i, reqs[i]\->ar_name,
|
|
ret == 0 ? "Finished" : gai_strerror(ret));
|
|
}
|
|
}
|
|
|
|
/* Cancel specified requests */
|
|
static void
|
|
cancel_requests(void)
|
|
{
|
|
char *id;
|
|
int ret, n;
|
|
|
|
while ((id = strtok(NULL, " ")) != NULL) {
|
|
n = atoi(id);
|
|
|
|
if (n >= nreqs) {
|
|
printf("Bad request number: %s\\n", id);
|
|
return;
|
|
}
|
|
|
|
ret = gai_cancel(reqs[n]);
|
|
printf("[%s] %s: %s\\n", id, reqs[atoi(id)]\->ar_name,
|
|
gai_strerror(ret));
|
|
}
|
|
}
|
|
|
|
/* List all requests */
|
|
static void
|
|
list_requests(void)
|
|
{
|
|
int i, ret;
|
|
char host[NI_MAXHOST];
|
|
struct addrinfo *res;
|
|
|
|
for (i = 0; i < nreqs; i++) {
|
|
printf("[%02d] %s: ", i, reqs[i]\->ar_name);
|
|
ret = gai_error(reqs[i]);
|
|
|
|
if (!ret) {
|
|
res = reqs[i]\->ar_result;
|
|
|
|
ret = getnameinfo(res\->ai_addr, res\->ai_addrlen,
|
|
host, sizeof(host),
|
|
NULL, 0, NI_NUMERICHOST);
|
|
if (ret) {
|
|
fprintf(stderr, "getnameinfo() failed: %s\\n",
|
|
gai_strerror(ret));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
puts(host);
|
|
} else {
|
|
puts(gai_strerror(ret));
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
char *cmdline;
|
|
char *cmd;
|
|
|
|
while ((cmdline = getcmd()) != NULL) {
|
|
cmd = strtok(cmdline, " ");
|
|
|
|
if (cmd == NULL) {
|
|
list_requests();
|
|
} else {
|
|
switch (cmd[0]) {
|
|
case \(aqa\(aq:
|
|
add_requests();
|
|
break;
|
|
case \(aqw\(aq:
|
|
wait_requests();
|
|
break;
|
|
case \(aqc\(aq:
|
|
cancel_requests();
|
|
break;
|
|
case \(aql\(aq:
|
|
list_requests();
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Bad command: %c\\n", cmd[0]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
.fi
|
|
.SH SEE ALSO
|
|
.BR getaddrinfo (3),
|
|
.BR inet (3),
|
|
.BR lio_listio (3),
|
|
.BR hostname (7),
|
|
.BR ip (7),
|
|
.BR sigevent (7)
|