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