getaddrinfo_a man page: add notification example?
hi. first, thanks for the man pages. always a great resource (in spite of the --help's of the world).
i recently tried to figure out how to use getaddrinfo_a with its notification facilities.
----
https://git.sr.ht/~minshall/gai-a
----
the examples in the man page was very helpful to me.
----
https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man/man3/getaddrinfo_a.3
----
that there is any example is great, though i can imagine controversial (because of length). but ...
Quoted 2 lines
This example shows a simple interactive
getaddrinfo_a()front-end. The notification facility is not demonstrated.
if there was a desire to expand the asynchronous example to demonstrate signal and callback notifications, i could take a stab at it. maybe adding commands to change the facility (for future requests that session), or some such.
cheers, and again thanks, Greg
Alejandro Colomar
maintainerHi Greg,
Quoted 1 line · Greg Minshall
hi. first, thanks for the man pages. always a great resource
:-)
Quoted 17 lines
(in spite
of the --help's of the world).i recently tried to figure out how to use
getaddrinfo_awith its notification facilities.
----
https://git.sr.ht/~minshall/gai-a
----the examples in the man page was very helpful to me.
----
https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man/man3/getaddrinfo_a.3
----that there is any example is great, though i can imagine controversial (because of length). but ...
Quoted 7 lines
Quoted 2 lines
This example shows a simple interactive
getaddrinfo_a()front-end. The notification facility is not demonstrated.if there was a desire to expand the asynchronous example to demonstrate signal and callback notifications, i could take a stab at it. maybe adding commands to change the facility (for future requests that session), or some such.
I think examples are great in the manual pages (I wish we had at least one example in every page).
If you can provide an example for those features, it could be good. Do you think it would fit in the existing examples, or maybe it's better to add a separate new example?
Have a lovely day!
Alex
Quoted 2 lines
cheers, and again thanks, Greg
Signature · 1 line
<https://www.alejandro-colomar.es/>
Alex,
thanks for the response.
Quoted 6 lines
I think examples are great in the manual pages (I wish we had at least one example in every page).
If you can provide an example for those features, it could be good. Do you think it would fit in the existing examples, or maybe it's better to add a separate new example?
unless you or someone thinks otherwise, i would first try to see if i can make the notifications sit naturally inside the current asynchronous example. i think they will, but maybe they won't.
if that doesn't seem to work, i can try to slim down my current code, convert to getopt(3), etc., and see if you think the results would work as an additional example.
cheers, Greg
Alejandro,
here is a shar of an extension of the current example to one that includes the ability to receive notifications. (note, this is just the source, *not* embedded in the man page.) let me know if you'd like it packaged differently.
one design choice i made was to have the callback handlers print out a message announcing themselves, and then call list_requests to show the status. they could not announce themselves, or not call list_requests, but presumably should do at least one (otherwise, how's the user to know anything happened?). i've chosen to do both.
anyway, any comments or requests you'd like me to change, please let me know. (feel free to change whatever you or anyone else would like to change -- you own it!)
if there are any licensing issues and you need me to sign a release, let me know.
again, thanks for all the man pages.
cheers, Greg
#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.15.2). # To extract the files from this archive, save it to some FILE, remove
# everything before the '#!/bin/sh' line above, then type 'sh FILE'.
#
lock_dir=_sh862790
# Made on 2024-08-25 13:04 +03 by <minshall@archlinux>.
# Source directory was '/home/minshall/work/2024/misc/getaddrinfo_a'.
#
# Existing files will *not* be overwritten, unless '-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 6889 -rw-rw-r-- manpage-like-gai.c
#
MD5SUM=${MD5SUM-md5sum}
f=`${MD5SUM} --version | egrep '^md5sum .*(core|text)utils'`
test -n "${f}" && md5check=true || md5check=false
${md5check} || \
echo 'Note: not verifying md5sums. Consider installing GNU coreutils.'
if test "X$1" = "X-c"
then keep_file=''
else keep_file=true
fi
echo=echo
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=
locale_dir=
set_echo=falsefor dir in $PATH
do
if test -f $dir/gettext \
&& ($dir/gettext --version >/dev/null 2>&1)
then
case `$dir/gettext --version 2>&1 | sed 1q` in
*GNU*) gettext_dir=$dir
set_echo=true
break ;;
esac fi
done
if ${set_echo}
then
set_echo=false
for dir in $PATH
do
if test -f $dir/shar \
&& ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
then
locale_dir=`$dir/shar --print-text-domain-dir`
set_echo=true
break
fidone
if ${set_echo}
then
TEXTDOMAINDIR=$locale_dir
export TEXTDOMAINDIR
TEXTDOMAIN=sharutils
export TEXTDOMAIN echo="$gettext_dir/gettext -s"
fi
fi
IFS="$save_IFS"
if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null then if (echo -n test; echo 1,2,3) | grep n >/dev/null
then shar_n= shar_c='
'
else shar_n=-n shar_c= ; fi
else shar_n= shar_c='\c' ; fi
f=shar-touch.$$
st1=200112312359.59
st2=123123592001.59
st2tr=123123592001.5 # old SysV 14-char limit st3=1231235901
if touch -am -t ${st1} ${f} >/dev/null 2>&1 && \
test ! -f ${st1} && test -f ${f}; thenshar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'
elif touch -am ${st2} ${f} >/dev/null 2>&1 && \
test ! -f ${st2} && test ! -f ${st2tr} && test -f ${f}; thenshar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'
elif touch -am ${st3} ${f} >/dev/null 2>&1 && \
test ! -f ${st3} && test -f ${f}; thenshar_touch='touch -am $3$4$5$6$2 "$8"'
else
shar_touch=:
echo
${echo} 'WARNING: not restoring timestamps. Consider getting and
installing GNU '\''touch'\'', distributed in GNU coreutils...'
echo
fi
rm -f ${st1} ${st2} ${st2tr} ${st3} ${f}
#
if test ! -d ${lock_dir} ; then :
else ${echo} "lock directory ${lock_dir} exists"
exit 1
fi
if mkdir ${lock_dir}
then ${echo} "x - created lock directory ${lock_dir}." else ${echo} "x - failed to create lock directory ${lock_dir}."
exit 1
fi
# ============= manpage-like-gai.c ==============
if test -n "${keep_file}" && test -f 'manpage-like-gai.c' then
${echo} "x - SKIPPING manpage-like-gai.c (file already exists)"
else
${echo} "x - extracting manpage-like-gai.c (text)"
sed 's/^X//' << 'SHAR_EOF' > 'manpage-like-gai.c' &&
#define _GNU_SOURCE
#include <assert.h>
#include <err.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
X
#define CALLOC(n, type) ((type *) calloc(n, sizeof(type))) X
#define REALLOCF(ptr, n, type) \
({ \
X static_assert(__builtin_types_compatible_p(typeof(ptr), type *)); \ X \ X (type *) reallocarrayf(ptr, n, sizeof(type)); \
})
X
static struct gaicb **reqs = NULL;
static size_t nreqs = 0;
/* default is "no notification" ('n') */
static char notification = 'n';
X
/* forward declaration for callback routines */ static void list_requests(void);
X
X
static inline void *reallocarrayf(void *p, size_t nmemb, size_t size)
{
X void *q;
X
X q = reallocarray(p, nmemb, size);
X if (q == NULL && nmemb != 0 && size != 0)
X free(p);
X return q;
}
X
static char *
getcmd(void)
{
X static char buf[256];
X
X fputs("> ", stdout); fflush(stdout);
X if (fgets(buf, sizeof(buf), stdin) == NULL)
X return NULL;
X
X if (buf[strlen(buf) - 1] == '\n')
X buf[strlen(buf) - 1] = 0;
X
X return buf;
}
X
/* Set notification type: none (default), signal, callback */ static voidnotification_type(void)
{
X char *type = strtok(NULL, " ");
X
X switch (type[0]) {
X case 'c':
X case 'n':
X case 's':X notification = type[0];
X break;
X default:
X fprintf(stderr, "Bad type: '%c' (expecting 'c', 's', or 'n')\n", type[0]);
X break;
X }
}
X
/* callback routine for signal notifications */
X
static voidsignal_handler(int signo) {
X fprintf(stdout, "notified by signal\n"); X list_requests(); X fprintf(stdout, "> "); X fflush(stdout);
}
X
/* callback routine for thread/callback notifications */ X
static voidcallback_handler(union sigval sev_value) {
X fprintf(stdout, "notified by callback\n"); X list_requests(); X fprintf(stdout, "> "); X fflush(stdout);
}
X
/* Add requests for specified hostnames. */
static voidadd_requests(void)
{
X static struct sigevent senull; /* static, so initialized to zero */
X static struct sigaction sanull; /* static, so intitialized to zero */
X struct sigevent se = senull;
X struct sigaction sa = sanull;
X size_t nreqs_base = nreqs;
X char *host;
X int ret;
X
X while ((host = strtok(NULL, " "))) {
X nreqs++;
X reqs = REALLOCF(reqs, nreqs, struct gaicb *);
X if (reqs == NULL)X err(EXIT_FAILURE, "reallocf");
X
X reqs[nreqs - 1] = CALLOC(1, struct gaicb);
X if (reqs[nreqs - 1] == NULL)
X err(EXIT_FAILURE, "calloc");
X
X reqs[nreqs - 1]->ar_name = strdup(host);
X }
X
X switch (notification) {
X case 'c':
X /* notify via a callback */ X se.sigev_notify = SIGEV_THREAD; X se.sigev_notify_function = callback_handler; X break;
X case 'n':
X /* nothing to do */
X break;
X case 's':
X se.sigev_notify = SIGEV_SIGNAL;
X se.sigev_signo = SIGUSR1;
X sa.sa_handler = signal_handler;
X /* set SA_RESTART so read(2) in main doesn't get an EINTR */
X sa.sa_flags = SA_RESTART;
X ret = sigaction(SIGUSR1, &sa, NULL);
X if (ret) {X err(EXIT_FAILURE, "sigaction");
X }
X break;
X default:
X fprintf(stderr, "program error: notification not one of [cns]: %c\n",
X notification);
X exit(4);
X }
X
X /* Queue nreqs_base..nreqs requests. */
X
X ret = getaddrinfo_a(GAI_NOWAIT, &reqs[nreqs_base],
X nreqs - nreqs_base, notification == 'n' ? NULL : &se);
X if (ret) {
X fprintf(stderr, "getaddrinfo_a() failed: %s\n",
X gai_strerror(ret));
X exit(EXIT_FAILURE);
X }
}
X
/* Wait until at least one of specified requests completes. */ static voidwait_requests(void)
{
X char *id; X int ret; X size_t n; X struct gaicb const **wait_reqs;
X
X wait_reqs = CALLOC(nreqs, const struct gaicb *);
X if (wait_reqs == NULL)
X err(EXIT_FAILURE, "calloc");
X
X /* NULL elements are ignored by gai_suspend(). */
X
X while ((id = strtok(NULL, " ")) != NULL) {
X n = atoi(id);
X
X if (n >= nreqs) {
X printf("Bad request number: %s\n", id);
X return;
X }
X
X wait_reqs[n] = reqs[n];
X }
X
X ret = gai_suspend(wait_reqs, nreqs, NULL);
X if (ret) {
X printf("gai_suspend(): %s\n", gai_strerror(ret));
X return;
X }
X
X for (size_t i = 0; i < nreqs; i++) {
X if (wait_reqs[i] == NULL)
X continue;
X
X ret = gai_error(reqs[i]);
X if (ret == EAI_INPROGRESS)
X continue;
X
X printf("[%02zu] %s: %s\n", i, reqs[i]->ar_name,
X ret == 0 ? "Finished" : gai_strerror(ret));
X }
}
X
/* Cancel specified requests. */
static voidcancel_requests(void)
{
X char *id; X int ret; X size_t n;
X
X while ((id = strtok(NULL, " ")) != NULL) {
X n = atoi(id);
X
X if (n >= nreqs) {
X printf("Bad request number: %s\n", id);
X return;
X }
X
X ret = gai_cancel(reqs[n]);
X printf("[%s] %s: %s\n", id, reqs[atoi(id)]->ar_name,
X gai_strerror(ret));
X }
}
X
/* List all requests. */
static voidlist_requests(void)
{
X int ret; X char host[NI_MAXHOST]; X struct addrinfo *res;
X
X for (size_t i = 0; i < nreqs; i++) {
X printf("[%02zu] %s: ", i, reqs[i]->ar_name);
X ret = gai_error(reqs[i]);
X
X if (!ret) {
X res = reqs[i]->ar_result;
X
X ret = getnameinfo(res->ai_addr, res->ai_addrlen,
X host, sizeof(host),
X NULL, 0, NI_NUMERICHOST);
X if (ret) {
X fprintf(stderr, "getnameinfo() failed: %s\n",
X gai_strerror(ret));
X exit(EXIT_FAILURE);
X }
X puts(host);
X } else {
X puts(gai_strerror(ret));
X }
X }
}
X
int
main(void)
{
X char *cmdline;
X char *cmd;
X
X while ((cmdline = getcmd()) != NULL) {
X cmd = strtok(cmdline, " ");
X
X if (cmd == NULL) {
X list_requests();
X } else {
X switch (cmd[0]) {
X case 'a':
X add_requests();
X break;
X case 'w':
X wait_requests();
X break;
X case 'c':
X cancel_requests();
X break;
X case 'l':
X list_requests();
X break;
X case 'n':
X notification_type();
X break;
X default:
X fprintf(stderr, "Bad command: %c\n", cmd[0]);
X break;
X }
X }
X }
X exit(EXIT_SUCCESS);
}SHAR_EOF
(set 20 24 08 25 12 59 39 'manpage-like-gai.c'
eval "${shar_touch}") && \
chmod 0664 'manpage-like-gai.c'
if test $? -ne 0
then ${echo} "restore of manpage-like-gai.c failed" fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'manpage-like-gai.c': 'MD5 check failed'
) << \SHAR_EOF1bfd1ccd4300c23092705380177c85ea manpage-like-gai.c SHAR_EOF
else
test LC_ALL=C wc -c < 'manpage-like-gai.c' -ne 6889 && \
${echo} "restoration warning: size of 'manpage-like-gai.c' is not 6889" fi
fi
if rm -fr ${lock_dir}
then ${echo} "x - removed lock directory ${lock_dir}."
else ${echo} "x - failed to remove lock directory ${lock_dir}."
exit 1
fi
exit 0
Alejandro Colomar
maintainerQuoted 1 line · Greg Minshall
Alejandro,
Hi Greg,
Quoted 4 lines
here is a shar of an extension of the current example to one that includes the ability to receive notifications. (note, this is just the source, *not* embedded in the man page.) let me know if you'd like it packaged differently.
Would you mind sending the extracted files? That way I can comment on those directly.
Thanks,
Alex
Quoted 18 lines
one design choice i made was to have the callback handlers print out a message announcing themselves, and then call
list_requeststo show the status. they could not announce themselves, or not calllist_requests, but presumably should do at least one (otherwise, how's the user to know anything happened?). i've chosen to do both.anyway, any comments or requests you'd like me to change, please let me know. (feel free to change whatever you or anyone else would like to change -- you own it!)
if there are any licensing issues and you need me to sign a release, let me know.
again, thanks for all the man pages.
cheers, Greg
Signature · 1 line
<https://www.alejandro-colomar.es/>
Alex,
Quoted 2 lines
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 voidnotification_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 voidadd_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 voidwait_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 voidcancel_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 voidlist_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);
}Alejandro Colomar
maintainerHi Greg,
Quoted 30 lines · Greg Minshall
Alex,
Quoted 2 lines
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)); \ })
Quoted 4 lines
static struct gaicb **reqs = NULL;
staticsize_tnreqs = 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'
};
Quoted 18 lines
/* forward declaration for callback routines */
static voidlist_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)
Quoted 9 lines
{
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;
Quoted 42 lines
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); }
Quoted 6 lines
/* Add requests for specified hostnames. */
static voidadd_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.
Quoted 141 lines
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 voidcancel_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 voidlist_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);
Quoted 8 lines
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
Quoted 41 lines
} } }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); }
Signature · 1 line
<https://www.alejandro-colomar.es/>
hi, Alejandro,
thanks for the e-mail and code inspection.
Quoted 9 lines
Quoted 1 line
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'
};
that works. i like that, by initializing the tags with, e.g., " = 'n'", i can still use the user's input to set values, without needing some sort of a lookup.
: echo -ne 'n signal\na example.com\nw 0' | ./manpage-like-gai
Quoted 5 lines
Quoted 2 lines
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.
here i'm thinking of the case where the program gets its input via a pipe, which may present an EOF without a trailing newline. i'll be to follow your guidance here.
Quoted 6 lines
Quoted 2 lines
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.
sure, thanks.
Quoted 27 lines
Quoted 18 lines
/* List all requests. */
static voidlist_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);Quoted 8 lines
if (ret) { fprintf(stderr, "getnameinfo() failed: %s\n", gai_strerror(ret)); exit(EXIT_FAILURE); } puts(host); } else { puts(gai_strerror(ret));
Quoted 2 lines
If you invert the conditional, you can add a continue after this, and unindent the non-error code.
that seems nice. i think i didn't touch this code, but let me know if you'd like me to add this to my submission.
again, thanks.
cheers, Greg
Alejandro Colomar
maintainerHi Greg,
Quoted 24 lines · Greg Minshall
hi, Alejandro,
thanks for the e-mail and code inspection.
Quoted 9 lines
Quoted 1 line
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'
};that works. i like that, by initializing the tags with, e.g., " = 'n'", i can still use the user's input to set values, without needing some sort of a lookup.
: echo -ne 'n signal\na example.com\nw 0' | ./manpage-like-gai
Quoted 3 lines
Quoted 2 lines
if (buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = 0;
Quoted 6 lines
Quoted 2 lines
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.
here i'm thinking of the case where the program gets its input via a pipe, which may present an EOF without a trailing newline. i'll be to follow your guidance here.
For serious programs, I prefer being POSIXly pedantic and reporting an error for such files.
For an example program, for simplicity, we can do this:
stpcpy(strchrnul(buf, '\n'), "");
If removes the conditional, has less moving parts (no '-1'; and thus less error-prone), and is protected by _FORTIFY_SOURCE.
Quoted 39 lines
Quoted 8 lines
Quoted 4 lines
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.
sure, thanks.
Quoted 27 lines
Quoted 26 lines
/* List all requests. */
static voidlist_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));
Quoted 5 lines
Quoted 2 lines
If you invert the conditional, you can add a continue after this, and unindent the non-error code.
that seems nice. i think i didn't touch this code, but let me know if you'd like me to add this to my submission.
Hmmm, if you've copied this from existing code, we can keep it like that and change it in a different commit.
Have a lovely night!
Alex
Quoted 7 lines
again, thanks.
cheers, Greg
Signature · 1 line
<https://www.alejandro-colomar.es/>