git: f126d349810f - main - Add -F option to sysctl(8) to display sysctl format.

From: Kirk McKusick <mckusick_at_FreeBSD.org>
Date: Sun, 25 Dec 2022 06:59:21 UTC
The branch main has been updated by mckusick:

URL: https://cgit.FreeBSD.org/src/commit/?id=f126d349810fdb512c0b01e101342d430b947488

commit f126d349810fdb512c0b01e101342d430b947488
Author:     Kirk McKusick <mckusick@FreeBSD.org>
AuthorDate: 2022-12-25 06:57:13 +0000
Commit:     Kirk McKusick <mckusick@FreeBSD.org>
CommitDate: 2022-12-25 06:59:00 +0000

    Add -F option to sysctl(8) to display sysctl format.
    
    Also add a test to ensure that it is working correctly.
    
    Submitted by: ota_j.email.ne.jp
    Reviewed by:  mckusick
    Differential Revision: https://reviews.freebsd.org/D34012
---
 sbin/sysctl/sysctl.8              |  15 ++++-
 sbin/sysctl/sysctl.c              | 101 ++++++++++++++++++++++++---------
 sbin/sysctl/tests/Makefile        |   5 ++
 sbin/sysctl/tests/Makefile.depend |  11 ++++
 sbin/sysctl/tests/sysctl_test.sh  | 116 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 218 insertions(+), 30 deletions(-)

diff --git a/sbin/sysctl/sysctl.8 b/sbin/sysctl/sysctl.8
index 17c9b8c28346..c4c2e68edfca 100644
--- a/sbin/sysctl/sysctl.8
+++ b/sbin/sysctl/sysctl.8
@@ -28,7 +28,7 @@
 .\"	From: @(#)sysctl.8	8.1 (Berkeley) 6/6/93
 .\" $FreeBSD$
 .\"
-.Dd June 30, 2022
+.Dd December 24, 2022
 .Dt SYSCTL 8
 .Os
 .Sh NAME
@@ -36,13 +36,13 @@
 .Nd get or set kernel state
 .Sh SYNOPSIS
 .Nm
-.Op Fl bdehiNnoTtqWx
+.Op Fl bdeFhilNnoTtqWx
 .Op Fl B Ar bufsize
 .Op Fl f Ar filename
 .Ar name Ns Op = Ns Ar value Ns Op , Ns Ar value
 .Ar ...
 .Nm
-.Op Fl bdehNnoTtqWx
+.Op Fl bdeFhlNnoTtqWx
 .Op Fl B Ar bufsize
 .Fl a
 .Sh DESCRIPTION
@@ -100,6 +100,10 @@ Specify a file which contains a pair of name and value in each line.
 .Nm
 reads and processes the specified file first and then processes the name
 and value pairs in the command line argument.
+.It Fl F
+Print the format of the variable.
+This is additional information to describe the type of the variable and
+most useful with struct types such as clockinfo, timeval, and loadavg.
 .It Fl h
 Format output for human, rather than machine, readability.
 .It Fl i
@@ -108,6 +112,11 @@ The purpose is to make use of
 .Nm
 for collecting data from a variety of machines (not all of which
 are necessarily running exactly the same software) easier.
+.It Fl l
+Show the length of variables along with their values.
+This option cannot be combined with the
+.Fl N
+option.
 .It Fl N
 Show only variable names, not their values.
 This is particularly useful with shells that offer programmable
diff --git a/sbin/sysctl/sysctl.c b/sbin/sysctl/sysctl.c
index 5a090ca20fc4..9a1f8c5431a9 100644
--- a/sbin/sysctl/sysctl.c
+++ b/sbin/sysctl/sysctl.c
@@ -66,6 +66,7 @@ static const char *conffile;
 
 static int	aflag, bflag, Bflag, dflag, eflag, hflag, iflag;
 static int	Nflag, nflag, oflag, qflag, tflag, Tflag, Wflag, xflag;
+static bool	Fflag, lflag;
 
 static int	oidfmt(int *, int, char *, u_int *);
 static int	parsefile(const char *);
@@ -123,8 +124,8 @@ usage(void)
 {
 
 	(void)fprintf(stderr, "%s\n%s\n",
-	    "usage: sysctl [-bdehiNnoqTtWx] [ -B <bufsize> ] [-f filename] name[=value] ...",
-	    "       sysctl [-bdehNnoqTtWx] [ -B <bufsize> ] -a");
+	    "usage: sysctl [-bdeFhilNnoqTtWx] [ -B <bufsize> ] [-f filename] name[=value] ...",
+	    "       sysctl [-bdeFhlNnoqTtWx] [ -B <bufsize> ] -a");
 	exit(1);
 }
 
@@ -138,7 +139,7 @@ main(int argc, char **argv)
 	setbuf(stdout,0);
 	setbuf(stderr,0);
 
-	while ((ch = getopt(argc, argv, "AabB:def:hiNnoqtTwWxX")) != -1) {
+	while ((ch = getopt(argc, argv, "AabB:def:FhilNnoqtTwWxX")) != -1) {
 		switch (ch) {
 		case 'A':
 			/* compatibility */
@@ -162,12 +163,18 @@ main(int argc, char **argv)
 		case 'f':
 			conffile = optarg;
 			break;
+		case 'F':
+			Fflag = true;
+			break;
 		case 'h':
 			hflag = 1;
 			break;
 		case 'i':
 			iflag = 1;
 			break;
+		case 'l':
+			lflag = true;
+			break;
 		case 'N':
 			Nflag = 1;
 			break;
@@ -207,14 +214,15 @@ main(int argc, char **argv)
 	argc -= optind;
 	argv += optind;
 
-	if (Nflag && nflag)
+	/* Nflag is name only and doesn't make sense to combind with these */
+	/* TODO: few other combinations do not make sense but come back later */
+	if (Nflag && (lflag || nflag))
 		usage();
 	if (aflag && argc == 0)
 		exit(sysctl_all(NULL, 0));
 	if (argc == 0 && conffile == NULL)
 		usage();
 
-	warncount = 0;
 	if (conffile != NULL)
 		warncount += parsefile(conffile);
 
@@ -949,6 +957,55 @@ oidfmt(int *oid, int len, char *fmt, u_int *kind)
 	return (0);
 }
 
+/*
+ * This displays a combination of name, type, format, and/or description.
+ *
+ * Returns zero if anything was actually output.
+ * Returns one if there is an error.
+ */
+static int
+show_info(char *name, const char *sep, int ctltype, char *fmt, int *qoid, int nlen)
+{
+	u_char buf[BUFSIZ];
+	const char *prntype;
+	int error = 0, i;
+	size_t j;
+
+	if (!nflag)
+		printf("%s%s", name, sep);
+	if (tflag) {
+		if (ctl_typename[ctltype] != NULL)
+			prntype = ctl_typename[ctltype];
+		else {
+			prntype = "unknown";
+			error++;
+		}
+		if (Fflag || dflag)
+			printf("%s%s", prntype, sep);
+		else
+			fputs(prntype, stdout);
+	}
+	if (Fflag) {
+		if (!isprint(fmt[0])) /* Few codes doesn't have formats */
+			fmt = "";
+		if (dflag)
+			printf("%s%s", fmt, sep);
+		else
+			fputs(fmt, stdout);
+	}
+	if (!dflag)
+		return (error);
+
+	qoid[1] = CTL_SYSCTL_OIDDESCR;
+	bzero(buf, BUFSIZ);
+	j = sizeof(buf);
+	i = sysctl(qoid, nlen + 2, buf, &j, 0, 0);
+	if (i < 0)
+		return (1);
+	fputs(buf, stdout);
+	return (error);
+}
+
 /*
  * This formats and outputs the value of one variable
  *
@@ -960,9 +1017,9 @@ static int
 show_var(int *oid, int nlen, bool honor_skip)
 {
 	static int skip_len = 0, skip_oid[CTL_MAXNAME];
-	u_char buf[BUFSIZ], *val, *oval, *p;
+	u_char *val, *oval, *p;
 	char name[BUFSIZ], fmt[BUFSIZ];
-	const char *sep, *sep1, *prntype;
+	const char *sep, *sep1;
 	int qoid[CTL_MAXNAME+2];
 	uintmax_t umv;
 	intmax_t mv;
@@ -977,7 +1034,6 @@ show_var(int *oid, int nlen, bool honor_skip)
 	/* Silence GCC. */
 	umv = mv = intlen = 0;
 
-	bzero(buf, BUFSIZ);
 	bzero(fmt, BUFSIZ);
 	bzero(name, BUFSIZ);
 	qoid[0] = CTL_SYSCTL;
@@ -1008,25 +1064,8 @@ show_var(int *oid, int nlen, bool honor_skip)
 		sep = ": ";
 
 	ctltype = (kind & CTLTYPE);
-	if (tflag || dflag) {
-		if (!nflag)
-			printf("%s%s", name, sep);
-        	if (ctl_typename[ctltype] != NULL)
-            		prntype = ctl_typename[ctltype];
-        	else
-            		prntype = "unknown";
-		if (tflag && dflag)
-			printf("%s%s", prntype, sep);
-		else if (tflag) {
-			printf("%s", prntype);
-			return (0);
-		}
-		qoid[1] = CTL_SYSCTL_OIDDESCR;
-		j = sizeof(buf);
-		i = sysctl(qoid, nlen + 2, buf, &j, 0, 0);
-		printf("%s", buf);
-		return (0);
-	}
+	if (tflag || Fflag || dflag)
+		return show_info(name, sep, ctltype, fmt, qoid, nlen);
 
 	/* keep track of encountered skip nodes, ignoring descendants */
 	if ((skip_len == 0 || skip_len >= nlen * (int)sizeof(int)) &&
@@ -1109,6 +1148,8 @@ show_var(int *oid, int nlen, bool honor_skip)
 	case CTLTYPE_STRING:
 		if (!nflag)
 			printf("%s%s", name, sep);
+		if (lflag)
+			printf("%zd%s", len, sep);
 		printf("%.*s", (int)len, p);
 		free(oval);
 		return (0);
@@ -1127,6 +1168,8 @@ show_var(int *oid, int nlen, bool honor_skip)
 	case CTLTYPE_U64:
 		if (!nflag)
 			printf("%s%s", name, sep);
+		if (lflag)
+			printf("%zd%s", len, sep);
 		hexlen = 2 + (intlen * CHAR_BIT + 3) / 4;
 		sep1 = "";
 		while (len >= intlen) {
@@ -1197,6 +1240,8 @@ show_var(int *oid, int nlen, bool honor_skip)
 		if (func) {
 			if (!nflag)
 				printf("%s%s", name, sep);
+			if (lflag)
+				printf("%zd%s", len, sep);
 			i = (*func)(len, p);
 			free(oval);
 			return (i);
@@ -1209,6 +1254,8 @@ show_var(int *oid, int nlen, bool honor_skip)
 		}
 		if (!nflag)
 			printf("%s%s", name, sep);
+		if (lflag)
+			printf("%zd%s", len, sep);
 		printf("Format:%s Length:%zu Dump:0x", fmt, len);
 		while (len-- && (xflag || p < val + 16))
 			printf("%02x", *p++);
diff --git a/sbin/sysctl/tests/Makefile b/sbin/sysctl/tests/Makefile
new file mode 100644
index 000000000000..4a8dd4af6d72
--- /dev/null
+++ b/sbin/sysctl/tests/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+ATF_TESTS_SH=	sysctl_test
+
+.include <bsd.test.mk>
diff --git a/sbin/sysctl/tests/Makefile.depend b/sbin/sysctl/tests/Makefile.depend
new file mode 100644
index 000000000000..f80275d86ab1
--- /dev/null
+++ b/sbin/sysctl/tests/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/sbin/sysctl/tests/sysctl_test.sh b/sbin/sysctl/tests/sysctl_test.sh
new file mode 100644
index 000000000000..a26c2c77b998
--- /dev/null
+++ b/sbin/sysctl/tests/sysctl_test.sh
@@ -0,0 +1,116 @@
+# Copyright (c) 2022 Yoshihiro Ota <ota@j.email.ne.jp>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+sysctl_name="kern.ostype"
+sysctl_value="FreeBSD"
+sysctl_type="string"
+sysctl_description="Operating system type"
+
+atf_test_case sysctl_by_name
+sysctl_by_name_head()
+{
+	atf_set "descr" "Verify name without any arguments"
+}
+sysctl_by_name_body()
+{
+	atf_check -o "inline:${sysctl_name}: ${sysctl_value}\n" sysctl ${sysctl_name}
+}
+
+
+atf_test_case sysctl_nflag
+sysctl_nflag()
+{
+	atf_set "descr" "Verify -n argument"
+}
+sysctl_nflag_body()
+{
+	atf_check -o "inline:${sysctl_value}\n" sysctl -n ${sysctl_name}
+}
+
+
+atf_test_case sysctl_eflag
+sysctl_eflag()
+{
+	atf_set "descr" "Verify -e argument"
+}
+sysctl_eflag_body()
+{
+	atf_check -o "inline:${sysctl_name}=${sysctl_value}\n" sysctl -e ${sysctl_name}
+}
+
+
+atf_test_case sysctl_tflag
+sysctl_tflag()
+{
+	atf_set "descr" "Verify -t argument"
+}
+sysctl_tflag_body()
+{
+	atf_check -o "inline:${sysctl_name}: ${sysctl_type}\n" sysctl -t ${sysctl_name}
+}
+
+
+atf_test_case sysctl_dflag
+sysctl_dflag()
+{
+	atf_set "descr" "Verify -d argument"
+}
+sysctl_dflag_body()
+{
+	atf_check -o "inline:${sysctl_name}: ${sysctl_description}\n" sysctl -d ${sysctl_name}
+}
+
+
+atf_test_case sysctl_tflag_dflag
+sysctl_tflag_dflag()
+{
+	atf_set "descr" "Verify -t -d arguments"
+}
+sysctl_tflag_dflag_body()
+{
+	atf_check -o "inline:${sysctl_name}: ${sysctl_type}: ${sysctl_description}\n" sysctl -t -d ${sysctl_name}
+	atf_check -o "inline:${sysctl_name}: ${sysctl_type}: ${sysctl_description}\n" sysctl -d -t ${sysctl_name}
+}
+
+
+atf_test_case sysctl_nflag_tflag_dflag
+sysctl_nflag_tflag_dflag()
+{
+	atf_set "descr" "Verify -n -t -d arguments"
+}
+sysctl_nflag_tflag_dflag_body()
+{
+	atf_check -o "inline:${sysctl_type}: ${sysctl_description}\n" sysctl -n -t -d ${sysctl_name}
+}
+
+
+atf_init_test_cases()
+{
+	atf_add_test_case sysctl_by_name
+	atf_add_test_case sysctl_nflag
+	atf_add_test_case sysctl_eflag
+	atf_add_test_case sysctl_tflag
+	atf_add_test_case sysctl_dflag
+	atf_add_test_case sysctl_tflag_dflag
+	atf_add_test_case sysctl_nflag_tflag_dflag
+}