#define _GNU_SOURCE /* feature_test_macros(7) 参照 */ #include <netdb.h> int getaddrinfo_a(int mode, struct gaicb *list[], int nitems, struct sigevent *sevp); int gai_suspend(const struct gaicb * const list[], int nitems, const struct timespec *timeout); int gai_error(struct gaicb *req); int gai_cancel(struct gaicb *req); -lanl でリンクする。
mode 引数は以下の値のいずれかを指定する。
配列 list は処理すべき検索要求を指定する。 nitems 引数は list の要素数を指定する。 要求された検索命令は並列に開始される。 list の NULL 要素は無視される。 各要求は以下のように定義された gaicb 構造体で規定される。
struct gaicb {
const char *ar_name;
const char *ar_service;
const struct addrinfo *ar_request;
struct addrinfo *ar_result;
};
この構造体の要素は getaddrinfo(3) の引数に対応している。 したがって、 ar_name はインターネットホストを示す node 引数に、 ar_service はサービスを示す service 引数に対応する。 ar_request 要素は、 返されたソケットアドレス構造体を選択する基準を示す hints 引数に対応する。 最後の ar_request は res 引数に対応する。 この要素を初期化する必要はなく、この要素は要求が解決されると自動的にセットされる。 最後の 2 つの要素が参照している addrinfo 構造体については getaddrinfo(3) に説明がある。
mode に GAI_NOWAIT が指定された場合、 解決した要求に関する通知を sevp 引数が指す sigevent 構造体を使って受け取ることができる。 この構造体の定義と一般的な説明については sigevent(7) を参照。 sevp->sigev_notify フィールドには以下の値を指定できる。
SIGEV_SIGNAL と SIGEV_THREAD では、 sevp->sigev_value.sival_ptr が list を指すようにしておくと役立つことがある。
gai_suspend() 関数は呼び出し元のスレッドの実行を中断し、 配列 list 内の一つ以上の要求が完了するのを待つ。 nitems 引数は配列 list の大きさを指定する。 呼び出しは以下のいずれかの状況になるまで停止する。
どの要求が完了したかは明示的な通知は行われない。 どの要求が完了したかを知るためには、 要求のリストに対して gai_error() を繰り返し呼び出す必要がある。
gai_error() 関数は要求 req のステータスを返す。 要求がまだ完了していない場合は EAI_INPROGRESS が、 要求が正常に処理された場合は 0 が、 要求を解決できなかった場合はエラーコードが返される。
gai_cancel() 関数は要求 req をキャンセルする。 要求が正常にキャンセルされた場合、 要求のエラーステータスに EAI_CANCELED が設定され、 通常の非同期通知が実行される。 要求が現在処理中でキャンセルできない場合もある。 この場合 gai_cancel() が呼ばれなかったかのように処理が行われる。 req が NULL の場合、 そのプロセスが行ったすべての処理中の要求をキャンセルしようとする。
gai_suspend() 関数はリストの要求の少なくともひとつが完了すると 0 を返す。 それ以外の場合、 以下の 0 でないエラーコードのいずれかを返す。
gai_error() 関数は、 完了していない検索要求に対して EAI_INPROGRESS を返し、 成功で完了した検索に対して 0 を返す。 getaddrinfo(3) が返すエラーコードのいずれかを返す場合もある。 要求の完了前に明示的に要求がキャンセルされた場合はエラーコード EAI_CANCELED を返す。
gai_cancel() 関数はこれらの値のいずれかを返すことがある。
gai_strerror(3) 関数を使うと、 これらのエラーコードを、 エラーレポートに適した人間が読みやすい文字列に翻訳してくれる。
Interface | Attribute | Value |
getaddrinfo_a(), gai_suspend(), gai_error(), gai_cancel() | Thread safety | MT-Safe |
$ ./a.out ftp.us.kernel.org enoent.linuxfoundation.org gnu.cz ftp.us.kernel.org: 128.30.2.36 enoent.linuxfoundation.org: Name or service not known gnu.cz: 87.236.197.13
プログラムのソースコードは以下のとおりである。
#define _GNU_SOURCE #include <netdb.h> #include <stdio.h> #include <stdlib.h> #include <string.h>
int
main(int argc, char *argv[])
{
int 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 (int 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 (int 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);
}
セッションの実行例は以下のようになる。
$ ./a.out > 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
プログラムのソースは以下のとおりである。
#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] == '\n')
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, sizeof(reqs[0]) * nreqs);
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 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 (int 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 ret;
char host[NI_MAXHOST];
struct addrinfo *res;
for (int 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 'a':
add_requests();
break;
case 'w':
wait_requests();
break;
case 'c':
cancel_requests();
break;
case 'l':
list_requests();
break;
default:
fprintf(stderr, "Bad command: %c\n", cmd[0]);
break;
}
}
}
exit(EXIT_SUCCESS);
}