Re: getaddrinfo_a man page: add notification example?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Alex,

> Would you mind sending the extracted files?  That way I can comment on
> those directly.

here you go.

cheers, Greg


----

#define _GNU_SOURCE
#include <assert.h>
#include <err.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define CALLOC(n, type)  ((type *) calloc(n, sizeof(type)))

#define REALLOCF(ptr, n, type)                                          \
({                                                                      \
    static_assert(__builtin_types_compatible_p(typeof(ptr), type *));   \
                                                                        \
    (type *) reallocarrayf(ptr, n, sizeof(type));                       \
})

static struct gaicb **reqs = NULL;
static size_t nreqs = 0;
/* default is "no notification" ('n') */
static char notification = 'n';

/* forward declaration for callback routines */
static void list_requests(void);


static inline void *
reallocarrayf(void *p, size_t nmemb, size_t size)
{
    void  *q;

    q = reallocarray(p, nmemb, size);
    if (q == NULL && nmemb != 0 && size != 0)
        free(p);
    return q;
}

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;
}

/* Set notification type: none (default), signal, callback */
static void
notification_type(void)
{
    char *type = strtok(NULL, " ");

    switch (type[0]) {
    case 'c':
    case 'n':
    case 's':
        notification = type[0];
        break;
    default:
        fprintf(stderr, "Bad type: '%c' (expecting 'c', 's', or 'n')\n", type[0]);
        break;
    }
}

/* callback routine for signal notifications */

static void
signal_handler(int signo) {
    fprintf(stdout, "notified by signal\n");
    list_requests();
    fprintf(stdout, "> ");
    fflush(stdout);
}

/* callback routine for thread/callback notifications */

static void
callback_handler(union sigval sev_value) {
    fprintf(stdout, "notified by callback\n");
    list_requests();
    fprintf(stdout, "> ");
    fflush(stdout);
}

/* Add requests for specified hostnames. */
static void
add_requests(void)
{
    static struct sigevent senull; /* static, so initialized to zero */
    static struct sigaction sanull; /* static, so intitialized to zero */
    struct sigevent se = senull;
    struct sigaction sa = sanull;
    size_t nreqs_base = nreqs;
    char *host;
    int ret;

    while ((host = strtok(NULL, " "))) {
        nreqs++;
        reqs = REALLOCF(reqs, nreqs, struct gaicb *);
        if (reqs == NULL)
            err(EXIT_FAILURE, "reallocf");

        reqs[nreqs - 1] = CALLOC(1, struct gaicb);
        if (reqs[nreqs - 1] == NULL)
            err(EXIT_FAILURE, "calloc");

        reqs[nreqs - 1]->ar_name = strdup(host);
    }

    switch (notification) {
    case 'c':
        /* notify via a callback */
        se.sigev_notify = SIGEV_THREAD;
        se.sigev_notify_function = callback_handler;
        break;
    case 'n':
        /* nothing to do */
        break;
    case 's':
        se.sigev_notify = SIGEV_SIGNAL;
        se.sigev_signo = SIGUSR1;
        sa.sa_handler = signal_handler;
        /* set SA_RESTART so read(2) in main doesn't get an EINTR */
        sa.sa_flags = SA_RESTART;
        ret = sigaction(SIGUSR1, &sa, NULL);
        if (ret) {
            err(EXIT_FAILURE, "sigaction");
        }
        break;
    default:
        fprintf(stderr, "program error: `notification` not one of [cns]: %c\n",
                notification);
        exit(4);
    }

    /* Queue nreqs_base..nreqs requests. */

    ret = getaddrinfo_a(GAI_NOWAIT, &reqs[nreqs_base],
                        nreqs - nreqs_base, notification == 'n' ? NULL : &se);
    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;
    size_t n;
    struct gaicb const **wait_reqs;

    wait_reqs = CALLOC(nreqs, const struct gaicb *);
    if (wait_reqs == NULL)
        err(EXIT_FAILURE, "calloc");

                /* 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 (size_t i = 0; i < nreqs; i++) {
        if (wait_reqs[i] == NULL)
            continue;

        ret = gai_error(reqs[i]);
        if (ret == EAI_INPROGRESS)
            continue;

        printf("[%02zu] %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;
    size_t 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 (size_t i = 0; i < nreqs; i++) {
        printf("[%02zu] %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(void)
{
    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;
            case 'n':
                notification_type();
                break;
            default:
                fprintf(stderr, "Bad command: %c\n", cmd[0]);
                break;
            }
        }
    }
    exit(EXIT_SUCCESS);
}





[Index of Archives]     [Kernel Documentation]     [Netdev]     [Linux Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux