On Fri, Dec 09, 2022 at 12:10:59PM +0100, Alexandre Ratchov wrote:
> This diff adds an option to display variables periodically. Basically
> it replaces this usage:
>
> while sleep 1; do audioctl play.errors; done
>
> by
>
> audioctl -w 1 play.errors
>
> The purpose of above audioctl commands is to debug underruns, so we
> don't want to fork a new process and reopen the device. This would
> trigger longer kernel code-paths and may cause additional underruns
> than the ones being investigated.
>
> OK?
I like the idea, but I would like to tweak some things.
- Add [-w wait] to the first synoptic form in the manpage. It's legal
to do e.g.
# audioctl -w 1
- Call the variable "wait" in audioctl.c to match the manpage.
- Update usagestr to mention [-w wait].
- When polling variables periodically, it's better to use setitimer(2)
and sigsuspend(2) instead of sleep(3). setitimer(2) keeps the period
from drifting.
- Let the user SIGINT (^C) out of the program without returning an
error to the shell.
I'm unsure about this one, but it seems logical to give the user a
way to gracefully terminate the program. You say in the manpage that
the program will continue printing until it is interrupted.
Index: audioctl.8
===================================================================
RCS file: /cvs/src/usr.bin/audioctl/audioctl.8,v
retrieving revision 1.4
diff -u -p -r1.4 audioctl.8
--- audioctl.8 23 Apr 2020 00:16:59 -0000 1.4
+++ audioctl.8 9 Dec 2022 18:41:45 -0000
@@ -35,8 +35,10 @@
.Sh SYNOPSIS
.Nm audioctl
.Op Fl f Ar file
+.Op Fl w wait
.Nm audioctl
.Op Fl n
+.Op Fl w wait
.Op Fl f Ar file
.Ar name ...
.Nm audioctl
@@ -59,6 +61,12 @@ The default is
Suppress printing of the variable name.
.It Fl q
Suppress all output when setting a variable.
+.It Fl w Ar wait
+Pause
+.Ar wait
+seconds between each display.
+.Nm
+will display variables until it is interrupted.
.It Ar name Ns = Ns Ar value
Attempt to set the specified variable
.Ar name
@@ -130,10 +138,10 @@ audio control devices
audio devices
.El
.Sh EXAMPLES
-Display the number of bytes of silence inserted during play buffer
-underruns since device started:
+Once per second, display the number of bytes of silence inserted
+during play buffer underruns since device started:
.Bd -literal -offset indent
-# audioctl play.errors
+# audioctl -w 1 play.errors
.Ed
.Pp
Use signed 24-bit samples and 44100Hz sample rate:
Index: audioctl.c
===================================================================
RCS file: /cvs/src/usr.bin/audioctl/audioctl.c,v
retrieving revision 1.43
diff -u -p -r1.43 audioctl.c
--- audioctl.c 12 Jul 2021 15:09:19 -0000 1.43
+++ audioctl.c 9 Dec 2022 18:41:45 -0000
@@ -16,9 +16,11 @@
*/
#include <sys/types.h>
#include <sys/ioctl.h>
+#include <sys/time.h>
#include <sys/audioio.h>
#include <fcntl.h>
#include <limits.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -43,6 +45,7 @@ struct field {
#define STR 2
#define ENC 3
int type;
+ int show;
int set;
} fields[] = {
{"name", &rname.name, NULL, STR},
@@ -63,11 +66,24 @@ struct field {
};
const char usagestr[] =
- "usage: audioctl [-f file]\n"
- " audioctl [-n] [-f file] name ...\n"
+ "usage: audioctl [-f file] [-w wait]\n"
+ " audioctl [-n] [-f file] [-w wait] name ...\n"
" audioctl [-nq] [-f file] name=value ...\n";
int fd, show_names = 1, quiet = 0;
+unsigned int wait;
+volatile sig_atomic_t uninterrupted = 1;
+
+void
+handle_alrm(int signo)
+{
+}
+
+void
+handle_int(int signo)
+{
+ uninterrupted = 0;
+}
/*
* parse encoding string (examples: s8, u8, s16, s16le, s24be ...)
@@ -194,24 +210,15 @@ parse_field(struct field *f, void *addr,
void
audio_main(int argc, char **argv)
{
+ struct itimerval itv;
struct field *f;
char *lhs, *rhs;
+ sigset_t empty;
int set = 0;
- if (ioctl(fd, AUDIO_GETSTATUS, &rstatus) == -1)
- err(1, "AUDIO_GETSTATUS");
- if (ioctl(fd, AUDIO_GETDEV, &rname) == -1)
- err(1, "AUDIO_GETDEV");
- if (ioctl(fd, AUDIO_GETPAR, &rpar) == -1)
- err(1, "AUDIO_GETPAR");
- if (ioctl(fd, AUDIO_GETPOS, &rpos) == -1)
- err(1, "AUDIO_GETPOS");
if (argc == 0) {
- for (f = fields; f->name != NULL; f++) {
- printf("%s=", f->name);
- print_field(f, f->raddr);
- printf("\n");
- }
+ for (f = fields; f->name != NULL; f++)
+ f->show = 1;
}
AUDIO_INITPAR(&wpar);
for (; argc > 0; argc--, argv++) {
@@ -231,15 +238,51 @@ audio_main(int argc, char **argv)
parse_field(f, f->waddr, rhs);
f->set = 1;
set = 1;
- } else {
+ } else
+ f->show = 1;
+ }
+
+ if (set && wait)
+ errx(1, "the -w flag is incompatible with variable assignment");
+
+ if (wait != 0) {
+ if (signal(SIGALRM, handle_alrm) == SIG_ERR)
+ err(1, "signal");
+ if (signal(SIGINT, handle_int) == SIG_ERR)
+ err(1, "signal");
+ sigemptyset(&empty);
+ itv.it_value.tv_sec = wait;
+ itv.it_value.tv_usec = 0;
+ itv.it_interval = itv.it_value;
+ if (setitimer(ITIMER_REAL, &itv, NULL) == -1)
+ err(1, "setitimer");
+ }
+
+ while (uninterrupted) {
+ if (ioctl(fd, AUDIO_GETSTATUS, &rstatus) == -1)
+ err(1, "AUDIO_GETSTATUS");
+ if (ioctl(fd, AUDIO_GETDEV, &rname) == -1)
+ err(1, "AUDIO_GETDEV");
+ if (ioctl(fd, AUDIO_GETPAR, &rpar) == -1)
+ err(1, "AUDIO_GETPAR");
+ if (ioctl(fd, AUDIO_GETPOS, &rpos) == -1)
+ err(1, "AUDIO_GETPOS");
+ for (f = fields; f->name != NULL; f++) {
+ if (!f->show)
+ continue;
if (show_names)
printf("%s=", f->name);
print_field(f, f->raddr);
printf("\n");
}
+ if (wait == 0)
+ break;
+ sigsuspend(&empty);
}
+
if (!set)
return;
+
if (ioctl(fd, AUDIO_SETPAR, &wpar) == -1)
err(1, "AUDIO_SETPAR");
if (ioctl(fd, AUDIO_GETPAR, &wpar) == -1)
@@ -261,9 +304,10 @@ int
main(int argc, char **argv)
{
char *path = "/dev/audioctl0";
+ const char *errstr;
int c;
- while ((c = getopt(argc, argv, "anf:q")) != -1) {
+ while ((c = getopt(argc, argv, "anf:qw:")) != -1) {
switch (c) {
case 'a': /* ignored, compat */
break;
@@ -275,6 +319,11 @@ main(int argc, char **argv)
break;
case 'q':
quiet = 1;
+ break;
+ case 'w':
+ wait = strtonum(optarg, 1, UINT_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "wait is %s: %s", errstr, optarg);
break;
default:
fputs(usagestr, stderr);