| .\" 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 2017-09-15 "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> |
| .PP |
| .BI "int getaddrinfo_a(int " "mode" ", struct gaicb *" "list[]" , |
| .BI " int " "nitems" ", struct sigevent *" "sevp" ); |
| .PP |
| .BI "int gai_suspend(const struct gaicb * const " "list[]" ", int " "nitems" , |
| .BI " const struct timespec *" "timeout" ); |
| .PP |
| .BI "int gai_error(struct gaicb *" "req" ); |
| .PP |
| .BI "int gai_cancel(struct gaicb *" "req" ); |
| .PP |
| 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. |
| .PP |
| 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: |
| .PP |
| .in +4n |
| .EX |
| struct gaicb { |
| const char *ar_name; |
| const char *ar_service; |
| const struct addrinfo *ar_request; |
| struct addrinfo *ar_result; |
| }; |
| .EE |
| .in |
| .PP |
| 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). |
| .PP |
| 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 . |
| .PP |
| 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. |
| .PP |
| 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. |
| .PP |
| 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. |
| .PP |
| 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 |
| .sp 1 |
| .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: |
| .PP |
| .in +4n |
| .EX |
| $ \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 |
| .EE |
| .in |
| .PP |
| Here is the program source code |
| .PP |
| .EX |
| #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); |
| } |
| .EE |
| .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: |
| .PP |
| .in +4n |
| .EX |
| $ \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 |
| .EE |
| .in |
| .PP |
| The program source is as follows: |
| .PP |
| .EX |
| #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); |
| } |
| .EE |
| .SH SEE ALSO |
| .BR getaddrinfo (3), |
| .BR inet (3), |
| .BR lio_listio (3), |
| .BR hostname (7), |
| .BR ip (7), |
| .BR sigevent (7) |