Re: getaddrinfo_a man page: add notification example?

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

 



Hi Greg,

On Sun, Aug 25, 2024 at 02:29:13PM +0300, Greg Minshall wrote:
> 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';

Would it be better to use an enum instead of comments?

	enum {
		NOTIFICATION_NONE = 'n',
		NOTIFICATION_SIGNAL = 's',
		NOTIFICATION_CALLBACK = 'c'
	};

> 
> /* 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;

If the string does not contain a newline, it probably means something is
wrong.  Returning as if all were good is probably not a good idea.

I suggest

	static inline char *
	stpsep(char *s, const char *delim)
	{
		strsep(&s, delim);
		return s;
	}

and then:

	if (stpsep(buf, "\n") == NULL)
		return NULL;



> 
>     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 */

These comments are redundant.  Please remove them.  Maybe add a blank
line between static variables and automatic ones to make it more
evident.

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

If you invert the conditional, you can add a continue after this, and
unindent the non-error code.

Thanks for the emanple program!

Have a lovely day!
Alex

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

-- 
<https://www.alejandro-colomar.es/>

Attachment: signature.asc
Description: PGP signature


[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