Index: sbin/sysctl/sysctl.8 =================================================================== --- sbin/sysctl/sysctl.8 +++ sbin/sysctl/sysctl.8 @@ -28,7 +28,7 @@ .\" From: @(#)sysctl.8 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" -.Dd October 30, 2020 +.Dd February 5, 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 bdeFhiNnoTtqWx .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 bdeFhNnoTtqWx .Op Fl B Ar bufsize .Fl a .Sh DESCRIPTION @@ -97,6 +97,10 @@ .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 Index: sbin/sysctl/sysctl.c =================================================================== --- sbin/sysctl/sysctl.c +++ sbin/sysctl/sysctl.c @@ -66,6 +66,7 @@ static int aflag, bflag, Bflag, dflag, eflag, hflag, iflag; static int Nflag, nflag, oflag, qflag, tflag, Tflag, Wflag, xflag; +static bool Fflag; static int oidfmt(int *, int, char *, u_int *); static int parsefile(const char *); @@ -123,8 +124,8 @@ { (void)fprintf(stderr, "%s\n%s\n", - "usage: sysctl [-bdehiNnoqTtWx] [ -B ] [-f filename] name[=value] ...", - " sysctl [-bdehNnoqTtWx] [ -B ] -a"); + "usage: sysctl [-bdeFhiNnoqTtWx] [ -B ] [-f filename] name[=value] ...", + " sysctl [-bdeFhNnoqTtWx] [ -B ] -a"); exit(1); } @@ -138,7 +139,7 @@ setbuf(stdout,0); setbuf(stderr,0); - while ((ch = getopt(argc, argv, "AabB:def:hiNnoqtTwWxX")) != -1) { + while ((ch = getopt(argc, argv, "AabB:def:FhiNnoqtTwWxX")) != -1) { switch (ch) { case 'A': /* compatibility */ @@ -162,6 +163,9 @@ case 'f': conffile = optarg; break; + case 'F': + Fflag = true; + break; case 'h': hflag = 1; break; @@ -214,7 +218,6 @@ if (argc == 0 && conffile == NULL) usage(); - warncount = 0; if (conffile != NULL) warncount += parsefile(conffile); @@ -949,6 +952,55 @@ 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 +1012,9 @@ 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 +1029,6 @@ /* Silence GCC. */ umv = mv = intlen = 0; - bzero(buf, BUFSIZ); bzero(fmt, BUFSIZ); bzero(name, BUFSIZ); qoid[0] = CTL_SYSCTL; @@ -1008,25 +1059,8 @@ 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)) && Index: sbin/sysctl/tests/Makefile =================================================================== --- /dev/null +++ sbin/sysctl/tests/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +ATF_TESTS_SH= sysctl_test + +.include Index: sbin/sysctl/tests/Makefile.depend =================================================================== --- /dev/null +++ sbin/sysctl/tests/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif Index: sbin/sysctl/tests/sysctl_test.sh =================================================================== --- /dev/null +++ sbin/sysctl/tests/sysctl_test.sh @@ -0,0 +1,119 @@ +# Copyright (c) 2022 Yoshihiro Ota +# +# 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. +# +# $FreeBSD$ +# + +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 +}