Skip site navigation (1)Skip section navigation (2)
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>