GETADDRINFO_A(3)

名前
getaddrinfo_a, gai_suspend, gai_error, gai_cancel - 非同期のネットワークアドレスとサービスの変換

書式
#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 でリンクする。

説明
getaddrinfo_a() 関数は getaddrinfo(3) と同じ処理を実行するが、 複数の名前検索を非同期で実行でき、 検索処理の完了の通知ができる点が異なる。

mode 引数は以下の値のいずれかを指定する。

GAI_WAIT
    検索を同期で実行する。 呼び出しは検索が完了するまで停止 (block) する。

GAI_NOWAIT
    検索を非同期で実行する。 呼び出しは直ちに返り、 要求はバックグラウンドで処理される。 以下の sevp 引数の議論を参照。

配列 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_NONE
    通知は行わない。

SIGEV_SIGNAL
    検索が完了した際に、 プロセスに対してシグナル sigev_signo を生成する。 一般的な説明は sigevent(7) を参照。 siginfo_t 構造体の si_code フィールドには SI_ASYNCNL がセットされる。

SIGEV_THREAD
    検索が完了した際に、 sigev_notify_function を新しいスレッドの開始関数であるかのように起動する。 詳細は sigevent(7) を参照。

SIGEV_SIGNAL と SIGEV_THREAD では、 sevp->sigev_value.sival_ptr が list を指すようにしておくと役立つことがある。

gai_suspend() 関数は呼び出し元のスレッドの実行を中断し、 配列 list 内の一つ以上の要求が完了するのを待つ。 nitems 引数は配列 list の大きさを指定する。 呼び出しは以下のいずれかの状況になるまで停止する。

*   list 内の一つ以上の操作が完了した。

*   呼び出しが補足されたシグナルに割り込まれた。

*   timeout で指定された期間が経過した。 この引数は、秒とナノ秒でタイムアウトを指定する (timespec 構造体の詳細は nanosleep(2) を参照)。 timeout が NULL の場合、 (上記のイベントのいずれかが発生するまで) 呼び出しは無限に停止する。

どの要求が完了したかは明示的な通知は行われない。 どの要求が完了したかを知るためには、 要求のリストに対して gai_error() を繰り返し呼び出す必要がある。

gai_error() 関数は要求 req のステータスを返す。 要求がまだ完了していない場合は EAI_INPROGRESS が、 要求が正常に処理された場合は 0 が、 要求を解決できなかった場合はエラーコードが返される。

gai_cancel() 関数は要求 req をキャンセルする。 要求が正常にキャンセルされた場合、 要求のエラーステータスに EAI_CANCELED が設定され、 通常の非同期通知が実行される。 要求が現在処理中でキャンセルできない場合もある。 この場合 gai_cancel() が呼ばれなかったかのように処理が行われる。 req が NULL の場合、 そのプロセスが行ったすべての処理中の要求をキャンセルしようとする。

返り値
getaddrinfo_a() 関数はすべての要求が正常にキューに追加されると 0 を返す。 または、以下のいずれかの 0 でないエラーコードを返す。

EAI_AGAIN
    検索要求をキューに入れるために必要なリソースがなかった。 アプリケーションは書く要求のエラーステータスを確認し、 どの要求が失敗したかを判定することができる。

EAI_MEMORY
    メモリーが足りない。

EAI_SYSTEM
    mode が無効である。

gai_suspend() 関数はリストの要求の少なくともひとつが完了すると 0 を返す。 それ以外の場合、 以下の 0 でないエラーコードのいずれかを返す。

EAI_AGAIN
    いずれかの要求が完了する前に指定されたタイムアウト時間が満了した。

EAI_ALLDONE
    指定された関数には実際には要求がなかった。

EAI_INTR
    シグナルが関数に割り込んだ。 この割り込みは検索要求が完了したことを示すシグナル通知により起こる場合もある。

gai_error() 関数は、 完了していない検索要求に対して EAI_INPROGRESS を返し、 成功で完了した検索に対して 0 を返す。 getaddrinfo(3) が返すエラーコードのいずれかを返す場合もある。 要求の完了前に明示的に要求がキャンセルされた場合はエラーコード EAI_CANCELED を返す。

gai_cancel() 関数はこれらの値のいずれかを返すことがある。

EAI_CANCELED
    要求は正常にキャンセルされた。

EAI_NOTCANCELED
    要求はキャンセルされていない。

EAI_ALLDONE
    要求はすでに完了している。

gai_strerror(3) 関数を使うと、 これらのエラーコードを、 エラーレポートに適した人間が読みやすい文字列に翻訳してくれる。

属性
この節で使用されている用語の説明は attributes(7) を参照のこと。
Interface | Attribute | Value
getaddrinfo_a(), gai_suspend(), gai_error(), gai_cancel() | Thread safety | MT-Safe

準拠
これらの関数は GNU 拡張である。 バージョン 2.2.3 で初めて glibc に登場した。

注意
getaddrinfo_a() インターフェースは lio_listio(3) インターフェースの後にモデル化された。

例
ここでは二つの例を示す。 一つは複数の要求を同期処理で並行して解決する例で、 もう一つは非同期機能を使った複雑な例である。

  同期型の例
以下のプログラムは単に複数のホスト名の解決を並行で行う。 getaddrinfo(3) を使って順番にホスト名の解決を行うのに比べて速度が向上する。 このプログラムは以下のように使う。

    $ ./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);
    }

  非同期型の例
この例は getaddrinfo_a() の簡単な対話式のフロントエンドである。 通知機能は使っていない。

セッションの実行例は以下のようになる。

    $ ./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);
    }

関連項目
getaddrinfo(3), inet(3), lio_listio(3), hostname(7), ip(7), sigevent(7)

この文書について
この man ページは Linux man-pages プロジェクトのリリース 5.10 の一部である。プロジェクトの説明とバグ報告に関する情報は https://www.kernel.org/doc/man-pages/ に書かれている。