public inbox for linux-man@vger.kernel.org
 help / color / mirror / Atom feed
From: Greg Minshall <minshall@umich.edu>
To: Alejandro Colomar <alx@kernel.org>
Cc: linux-man@vger.kernel.org
Subject: Re: getaddrinfo_a man page: add notification example?
Date: Sun, 25 Aug 2024 14:29:13 +0300	[thread overview]
Message-ID: <864874.1724585353@archlinux> (raw)
In-Reply-To: <4vdd7x3bdhpomg5epf4huwbdsytvgd2qqgohyavpsjmqgwperv@tx2ytsol5ymv>

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

  reply	other threads:[~2024-08-25 11:32 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-08-21  8:50 getaddrinfo_a man page: add notification example? Greg Minshall
2024-08-21 10:36 ` Alejandro Colomar
2024-08-21 13:09   ` Greg Minshall
2024-08-25 10:17   ` Greg Minshall
2024-08-25 10:48     ` Alejandro Colomar
2024-08-25 11:29       ` Greg Minshall [this message]
2024-11-01 13:46         ` Alejandro Colomar
2024-11-01 17:57           ` Greg Minshall
2024-11-01 20:01             ` Alejandro Colomar

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=864874.1724585353@archlinux \
    --to=minshall@umich.edu \
    --cc=alx@kernel.org \
    --cc=linux-man@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox