Date: Mon, 12 Jan 2026 13:32:38 +0000 From: Christos Margiolis <christos@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: aa58af04dc88 - main - sndctl(8): Add libxo support Message-ID: <6964f7f6.8dba.115b4ad1@gitrepo.freebsd.org>
index | next in thread | raw e-mail
The branch main has been updated by christos: URL: https://cgit.FreeBSD.org/src/commit/?id=aa58af04dc88aabf9d2fade2c7d126031cb66f42 commit aa58af04dc88aabf9d2fade2c7d126031cb66f42 Author: Christos Margiolis <christos@FreeBSD.org> AuthorDate: 2026-01-12 13:32:06 +0000 Commit: Christos Margiolis <christos@FreeBSD.org> CommitDate: 2026-01-12 13:32:32 +0000 sndctl(8): Add libxo support Sponsored by: The FreeBSD Foundation MFC after: 1 week Reviewed by: ziaee, mckusick Differential Revision: https://reviews.freebsd.org/D54032 --- usr.sbin/sndctl/Makefile | 2 +- usr.sbin/sndctl/sndctl.8 | 11 +++- usr.sbin/sndctl/sndctl.c | 151 +++++++++++++++++++++++++++++++---------------- 3 files changed, 111 insertions(+), 53 deletions(-) diff --git a/usr.sbin/sndctl/Makefile b/usr.sbin/sndctl/Makefile index d6697bb88fd5..cb06b84f5766 100644 --- a/usr.sbin/sndctl/Makefile +++ b/usr.sbin/sndctl/Makefile @@ -5,6 +5,6 @@ PACKAGE= sound PROG= sndctl SRCS= ${PROG}.c MAN= ${PROG}.8 -LDFLAGS+= -lnv -lmixer +LDFLAGS+= -lnv -lmixer -lxo -lsbuf .include <bsd.prog.mk> diff --git a/usr.sbin/sndctl/sndctl.8 b/usr.sbin/sndctl/sndctl.8 index 4c3810f3c16b..73414bd95325 100644 --- a/usr.sbin/sndctl/sndctl.8 +++ b/usr.sbin/sndctl/sndctl.8 @@ -27,7 +27,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 5, 2025 +.Dd December 2, 2025 .Dt SNDCTL 8 .Os .Sh NAME @@ -35,6 +35,7 @@ .Nd list and modify soundcard properties .Sh SYNOPSIS .Nm +.Op Fl -libxo .Op Fl f Ar device .Op Fl hov .Op Ar control Ns Oo = Ns Ar value Oc Ar ... @@ -46,6 +47,13 @@ control-driven interface, in order to filter and/or set specific properties. .Pp The options are as follows: .Bl -tag -width "-f device" +.It Fl -libxo +Generate output via +.Xr libxo 3 +in a selection of different human and machine readable formats. +See +.Xr xo_options 7 +for details on command line arguments. .It Fl f Ar device Choose a specific audio device .Pq see Sx FILES . @@ -175,6 +183,7 @@ $ sndctl -f /dev/dsp0 `cat info` .Sh SEE ALSO .Xr sndstat 4 , .Xr sound 4 , +.Xr xo_options 7 , .Xr mixer 8 , .Xr sysctl 8 .Sh AUTHORS diff --git a/usr.sbin/sndctl/sndctl.c b/usr.sbin/sndctl/sndctl.c index e6dac67e2ea7..bbc2da6a4ab9 100644 --- a/usr.sbin/sndctl/sndctl.c +++ b/usr.sbin/sndctl/sndctl.c @@ -30,6 +30,7 @@ #include <sys/nv.h> #include <sys/queue.h> +#include <sys/sbuf.h> #include <sys/sndstat.h> #include <sys/soundcard.h> #include <sys/sysctl.h> @@ -45,6 +46,9 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <libxo/xo.h> + +#define SNDCTL_XO_VERSION "1" /* Taken from sys/dev/sound/pcm/ */ #define STATUS_LEN 64 @@ -171,7 +175,7 @@ static struct snd_ctl dev_ctls[] = { static struct snd_ctl chan_ctls[] = { #define F(member) offsetof(struct snd_chan, member) - /*{ "name", F(name), STR, NULL },*/ + { "name", F(name), STR, NULL }, { "parentchan", F(parentchan), STR, NULL }, { "unit", F(unit), NUM, NULL }, { "caps", F(caps), STR, NULL }, @@ -342,7 +346,7 @@ sysctl_int(const char *buf, const char *arg, int *var) size = sizeof(int); /* Read current value. */ if (sysctlbyname(buf, &prev, &size, NULL, 0) < 0) { - warn("sysctlbyname(%s)", buf); + xo_warn("sysctlbyname(%s)", buf); return (-1); } @@ -351,24 +355,24 @@ sysctl_int(const char *buf, const char *arg, int *var) errno = 0; n = strtol(arg, NULL, 10); if (errno == EINVAL || errno == ERANGE) { - warn("strtol(%s)", arg); + xo_warn("strtol(%s)", arg); return (-1); } /* Apply new value. */ if (sysctlbyname(buf, NULL, 0, &n, size) < 0) { - warn("sysctlbyname(%s, %d)", buf, n); + xo_warn("sysctlbyname(%s, %d)", buf, n); return (-1); } } /* Read back applied value for good measure. */ if (sysctlbyname(buf, &n, &size, NULL, 0) < 0) { - warn("sysctlbyname(%s)", buf); + xo_warn("sysctlbyname(%s)", buf); return (-1); } - if (arg != NULL) + if (arg != NULL && xo_get_style(NULL) == XO_STYLE_TEXT) printf("%s: %d -> %d\n", buf, prev, n); if (var != NULL) *var = n; @@ -386,7 +390,7 @@ sysctl_str(const char *buf, const char *arg, char *var, size_t varsz) /* Read current value. */ size = sizeof(prev); if (sysctlbyname(buf, prev, &size, NULL, 0) < 0) { - warn("sysctlbyname(%s)", buf); + xo_warn("sysctlbyname(%s)", buf); return (-1); } @@ -395,26 +399,26 @@ sysctl_str(const char *buf, const char *arg, char *var, size_t varsz) size = strlen(arg); /* Apply new value. */ if (sysctlbyname(buf, NULL, 0, arg, size) < 0) { - warn("sysctlbyname(%s, %s)", buf, arg); + xo_warn("sysctlbyname(%s, %s)", buf, arg); return (-1); } /* Get size of new string. */ if (sysctlbyname(buf, NULL, &size, NULL, 0) < 0) { - warn("sysctlbyname(%s)", buf); + xo_warn("sysctlbyname(%s)", buf); return (-1); } } if ((tmp = calloc(1, size)) == NULL) - err(1, "calloc"); + xo_err(1, "calloc"); /* Read back applied value for good measure. */ if (sysctlbyname(buf, tmp, &size, NULL, 0) < 0) { - warn("sysctlbyname(%s)", buf); + xo_warn("sysctlbyname(%s)", buf); free(tmp); return (-1); } - if (arg != NULL) + if (arg != NULL && xo_get_style(NULL) == XO_STYLE_TEXT) printf("%s: %s -> %s\n", buf, prev, tmp); if (var != NULL) strlcpy(var, tmp, varsz); @@ -436,27 +440,27 @@ read_dev(char *path) int fd, caps, unit, t1, t2, t3; if ((fd = open("/dev/sndstat", O_RDONLY)) < 0) - err(1, "open(/dev/sndstat)"); + xo_err(1, "open(/dev/sndstat)"); if (ioctl(fd, SNDSTIOC_REFRESH_DEVS, NULL) < 0) - err(1, "ioctl(SNDSTIOC_REFRESH_DEVS)"); + xo_err(1, "ioctl(SNDSTIOC_REFRESH_DEVS)"); arg.nbytes = 0; arg.buf = NULL; if (ioctl(fd, SNDSTIOC_GET_DEVS, &arg) < 0) - err(1, "ioctl(SNDSTIOC_GET_DEVS#1)"); + xo_err(1, "ioctl(SNDSTIOC_GET_DEVS#1)"); if ((arg.buf = malloc(arg.nbytes)) == NULL) - err(1, "malloc"); + xo_err(1, "malloc"); if (ioctl(fd, SNDSTIOC_GET_DEVS, &arg) < 0) - err(1, "ioctl(SNDSTIOC_GET_DEVS#2)"); + xo_err(1, "ioctl(SNDSTIOC_GET_DEVS#2)"); if ((nvl = nvlist_unpack(arg.buf, arg.nbytes, 0)) == NULL) - err(1, "nvlist_unpack"); + xo_err(1, "nvlist_unpack"); if (nvlist_empty(nvl) || !nvlist_exists(nvl, SNDST_DSPS)) - errx(1, "no soundcards attached"); + xo_errx(1, "no soundcards attached"); if (path == NULL || (path != NULL && strcmp(basename(path), "dsp") == 0)) unit = mixer_get_dunit(); @@ -475,12 +479,12 @@ read_dev(char *path) break;; } if (i == nitems) - errx(1, "device not found"); + xo_errx(1, "device not found"); #define NV(type, item) \ nvlist_get_ ## type (di[i], SNDST_DSPS_ ## item) if ((dp = calloc(1, sizeof(struct snd_dev))) == NULL) - err(1, "calloc"); + xo_err(1, "calloc"); dp->unit = -1; strlcpy(dp->name, NV(string, NAMEUNIT), sizeof(dp->name)); @@ -492,9 +496,9 @@ read_dev(char *path) #undef NV if (dp->play.pchans && !nvlist_exists(di[i], SNDST_DSPS_INFO_PLAY)) - errx(1, "%s: playback channel list empty", dp->name); + xo_errx(1, "%s: playback channel list empty", dp->name); if (dp->rec.pchans && !nvlist_exists(di[i], SNDST_DSPS_INFO_REC)) - errx(1, "%s: recording channel list empty", dp->name); + xo_errx(1, "%s: recording channel list empty", dp->name); #define NV(type, mode, item) \ nvlist_get_ ## type (nvlist_get_nvlist(di[i], \ @@ -526,7 +530,7 @@ read_dev(char *path) goto done; if (!nvlist_exists(di[i], SNDST_DSPS_PROVIDER_INFO)) - errx(1, "%s: provider_info list empty", dp->name); + xo_errx(1, "%s: provider_info list empty", dp->name); #define NV(type, item) \ nvlist_get_ ## type (nvlist_get_nvlist(di[i], \ @@ -549,13 +553,13 @@ read_dev(char *path) if (sysctl_int("hw.snd.latency", NULL, &t1) || sysctl_int("hw.snd.latency_profile", NULL, &t2) || sysctl_int("kern.timecounter.alloweddeviation", NULL, &t3)) - err(1, "%s: sysctl", dp->name); + xo_err(1, "%s: sysctl", dp->name); if (t1 == 0 && t2 == 0 && t3 == 0) dp->realtime = 1; if (!nvlist_exists(nvlist_get_nvlist(di[i], SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_CHAN_INFO)) - errx(1, "%s: channel info list empty", dp->name); + xo_errx(1, "%s: channel info list empty", dp->name); cdi = nvlist_get_nvlist_array( nvlist_get_nvlist(di[i], SNDST_DSPS_PROVIDER_INFO), @@ -567,7 +571,7 @@ read_dev(char *path) #define NV(type, item) \ nvlist_get_ ## type (cdi[j], SNDST_DSPS_SOUND4_CHAN_ ## item) if ((ch = calloc(1, sizeof(struct snd_chan))) == NULL) - err(1, "calloc"); + xo_err(1, "calloc"); strlcpy(ch->name, NV(string, NAME), sizeof(ch->name)); strlcpy(ch->parentchan, NV(string, PARENTCHAN), @@ -654,7 +658,7 @@ print_dev_ctl(struct snd_dev *dp, struct snd_ctl *ctl, bool simple, struct snd_ctl *cp; size_t len; - if (ctl->type != GRP) { + if (ctl->type != GRP && xo_get_style(NULL) == XO_STYLE_TEXT) { if (simple) printf("%s=", ctl->name); else @@ -663,10 +667,10 @@ print_dev_ctl(struct snd_dev *dp, struct snd_ctl *ctl, bool simple, switch (ctl->type) { case STR: - printf("%s\n", (char *)dp + ctl->off); + xo_emit("{a:%s/%s}\n", ctl->name, (char *)dp + ctl->off); break; case NUM: - printf("%d\n", *(int *)((intptr_t)dp + ctl->off)); + xo_emit("{a:%s/%d}\n", ctl->name, *(int *)((intptr_t)dp + ctl->off)); break; case VOL: break; @@ -691,7 +695,7 @@ print_chan_ctl(struct snd_chan *ch, struct snd_ctl *ctl, bool simple, size_t len; int v; - if (ctl->type != GRP) { + if (ctl->type != GRP && xo_get_style(NULL) == XO_STYLE_TEXT) { if (simple) printf("%s.%s=", ch->name, ctl->name); else @@ -700,14 +704,14 @@ print_chan_ctl(struct snd_chan *ch, struct snd_ctl *ctl, bool simple, switch (ctl->type) { case STR: - printf("%s\n", (char *)ch + ctl->off); + xo_emit("{a:%s/%s}\n", ctl->name, (char *)ch + ctl->off); break; case NUM: - printf("%d\n", *(int *)((intptr_t)ch + ctl->off)); + xo_emit("{a:%s/%d}\n", ctl->name, *(int *)((intptr_t)ch + ctl->off)); break; case VOL: v = *(int *)((intptr_t)ch + ctl->off); - printf("%.2f:%.2f\n", + xo_emit("{a:%s/%.2f:%.2f}\n", ctl->name, MIX_VOLNORM(v & 0x00ff), MIX_VOLNORM((v >> 8) & 0x00ff)); break; case GRP: @@ -728,31 +732,46 @@ print_dev(struct snd_dev *dp) { struct snd_chan *ch; struct snd_ctl *ctl; + struct sbuf sb; + char buf[16]; - if (!oflag) { - printf("%s: <%s> %s", dp->name, dp->desc, dp->status); + xo_open_instance("devices"); - printf(" ("); + if (!oflag || xo_get_style(NULL) != XO_STYLE_TEXT) { + sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN); + + sbuf_printf(&sb, "("); if (dp->play.pchans) - printf("play"); + sbuf_printf(&sb, "play"); if (dp->play.pchans && dp->rec.pchans) - printf("/"); + sbuf_printf(&sb, "/"); if (dp->rec.pchans) - printf("rec"); - printf(")\n"); + sbuf_printf(&sb, "rec"); + sbuf_printf(&sb, ")"); + + xo_emit("{:header/%s: <%s> %s %s}\n", + dp->name, dp->desc, dp->status, sbuf_data(&sb)); + + sbuf_delete(&sb); } for (ctl = dev_ctls; ctl->name != NULL; ctl++) print_dev_ctl(dp, ctl, oflag, false); if (vflag) { + xo_open_list("channels"); TAILQ_FOREACH(ch, &dp->chans, next) { - if (!oflag) + xo_open_instance("channels"); + if (!oflag && xo_get_style(NULL) == XO_STYLE_TEXT) printf(" %s\n", ch->name); for (ctl = chan_ctls; ctl->name != NULL; ctl++) print_chan_ctl(ch, ctl, oflag, false); + xo_close_instance("channels"); } + xo_close_list("channels"); } + + xo_close_instance("devices"); } static int @@ -916,8 +935,9 @@ mod_rec_format(struct snd_dev *dp, void *arg) static void __dead2 usage(void) { - fprintf(stderr, "usage: %s [-f device] [-hov] [control[=value] ...]\n", + xo_error("usage: %s [--libxo] [-f device] [-hov] [control[=value] ...]\n", getprogname()); + xo_finish(); exit(1); } @@ -932,6 +952,10 @@ main(int argc, char *argv[]) bool show = true, found; int c; + argc = xo_parse_args(argc, argv); + if (argc < 0) + exit(1); + while ((c = getopt(argc, argv, "f:hov")) != -1) { switch (c) { case 'f': @@ -952,11 +976,20 @@ main(int argc, char *argv[]) argc -= optind; argv += optind; + xo_set_version(SNDCTL_XO_VERSION); + xo_open_container("sndctl"); + dp = read_dev(path); + xo_open_container("executed_controls"); while (argc > 0) { - if ((s = strdup(*argv)) == NULL) - err(1, "strdup(%s)", *argv); + if ((s = strdup(*argv)) == NULL) { + xo_close_container("executed_controls"); + xo_close_container("sndctl"); + if (xo_finish() < 0) + xo_err(1, "xo_finish"); + xo_err(1, "strdup(%s)", *argv); + } propstr = strsep(&s, "="); if (propstr == NULL) @@ -966,11 +999,19 @@ main(int argc, char *argv[]) for (ctl = dev_ctls; ctl->name != NULL; ctl++) { if (strcmp(ctl->name, propstr) != 0) continue; - if (s == NULL) { - print_dev_ctl(dp, ctl, true, true); + if (s == NULL) show = false; - } else if (ctl->mod != NULL && ctl->mod(dp, s) < 0) - warnx("%s(%s) failed", ctl->name, s); + else if (ctl->mod != NULL && ctl->mod(dp, s) < 0) + xo_warnx("%s(%s) failed", ctl->name, s); + if (s == NULL || xo_get_style(NULL) != XO_STYLE_TEXT) { + /* + * Print the control in libxo mode in all + * cases, otherwise we'll not be printing any + * controls that were modified or whose + * ctl->mod() failed. + */ + print_dev_ctl(dp, ctl, true, true); + } found = true; break; } @@ -985,17 +1026,25 @@ main(int argc, char *argv[]) } } if (!found) - warnx("%s: no such property", propstr); + xo_warnx("%s: no such property", propstr); next: free(s); argc--; argv++; } + xo_close_container("executed_controls"); - if (show) { + if (show || xo_get_style(NULL) != XO_STYLE_TEXT) { + xo_open_list("devices"); print_dev(dp); + xo_close_list("devices"); } free_dev(dp); + + xo_close_container("sndctl"); + if (xo_finish() < 0) + xo_err(1, "xo_finish"); + return (0); }home | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6964f7f6.8dba.115b4ad1>
