From nobody Mon Jan 12 13:32:38 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4dqYGN1QzWz6Nbdc for ; Mon, 12 Jan 2026 13:32:44 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R13" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4dqYGG74s9z3PkF for ; Mon, 12 Jan 2026 13:32:38 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1768224759; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=qed/N4iZ82L7JhNCEex7AoNiun3gLeTeT+5Z5NVJ7co=; b=Xd1aCk2y9oha8FndMiiEQvJQeajjqxuNzluOS4TbXsTc75+gKNW7un7c6hsZe+AWLH3JOb 7jlypBR8PPjkxPV05jLX93gEQlki7q12tDZMZdLNOITIziy+xuplTNds9yXW0Nz01kTj6y oQ69eX0W1mm84LnsDWxnuVcF2tJ8vi1grFkO4qJ886hC6gyUt8vhQNb0Xs+AlwjsFZUMJQ M3pJBwVhX7tWRSIAvZxwJ701Imk4PromZI2xlMZ3Q/DbXepF+AgE+anxSk4H1pkO1p0/gc fUyGun7m2qXxjx2prUJEdJbFfMklDtI7xWlctGX/HSXQPiuJ4sinRaiXbWpvgg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1768224759; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=qed/N4iZ82L7JhNCEex7AoNiun3gLeTeT+5Z5NVJ7co=; b=XpHVLrZe5pHDIlGlo5ZrSw2xCEJEZ+3/iwe3TsNQ2gnJwzkQeORIvkEVnbkF2Y8vLC2Fnt 0sIgOFCj80TkjgbhxTpDNx1kwm0HWcZquFBLHho546trwzuO+hu0Htr23dLW9IX+3P1R5P VVJe5QjZpnjZyVHv15gOJT+iN/zpdd7Tx8KK+lbosW81oziAOMau97hcYYGFvYKrKuxzII 8U28Xo+RCKFdSdhFpvRg+DJa5D6ANsYgpYFIpDEgXcwoLooYNyUlDgTttZ4MZtR5xaFvhY rYd18b0XETkq7Yv46/hA9zHV4k6kA5xXFAUjxO6YcZJbPIn+FsGvvkWwbccThw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1768224759; a=rsa-sha256; cv=none; b=hp2rsWf480M+wQ2ADRPtjhb9KPvUGYh+us2EeeoNbZv4SCirQktdfKA70++qdfPdjBZIz7 YmgOVkdYZkt+5FUCy3Z1mpUcx87wIXQriYL8MAtpSVSJPJpoyoAc7mDRLCdi9uu5troLz8 H4tFn/BjTImQ5xuOkf+2cCieZ7hCN2C6oYsdmbzUzcThi3NaMb40ljkT0ZL/4B+rW8yzFg nE+dVKqhew7vZn9OywtawTdZh0iAE67GczlF7aJnvje3PgIa63r5SadbP6M1SHtt7V0zPO 21VtXrXdqeCd8IvNCsiJxitffo9hhc1cgSZl7wwwU2pWCFt/05QFtFEkU1XHZw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4dqYGG6MBBzCL9 for ; Mon, 12 Jan 2026 13:32:38 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 8dba by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Mon, 12 Jan 2026 13:32:38 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Christos Margiolis Subject: git: aa58af04dc88 - main - sndctl(8): Add libxo support List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: christos X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: aa58af04dc88aabf9d2fade2c7d126031cb66f42 Auto-Submitted: auto-generated Date: Mon, 12 Jan 2026 13:32:38 +0000 Message-Id: <6964f7f6.8dba.115b4ad1@gitrepo.freebsd.org> The branch main has been updated by christos: URL: https://cgit.FreeBSD.org/src/commit/?id=aa58af04dc88aabf9d2fade2c7d126031cb66f42 commit aa58af04dc88aabf9d2fade2c7d126031cb66f42 Author: Christos Margiolis AuthorDate: 2026-01-12 13:32:06 +0000 Commit: Christos Margiolis 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 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 #include +#include #include #include #include @@ -45,6 +46,9 @@ #include #include #include +#include + +#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); }