diff --git a/sbin/sysctl/sysctl.8 b/sbin/sysctl/sysctl.8 --- 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 @@ .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 @@ .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 --- a/sbin/sysctl/sysctl.c +++ b/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, lflag; 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 [-bdeFhilNnoqTtWx] [ -B ] [-f filename] name[=value] ...", + " sysctl [-bdeFhlNnoqTtWx] [ -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:FhilNnoqtTwWxX")) != -1) { switch (ch) { case 'A': /* compatibility */ @@ -162,12 +163,18 @@ 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 @@ 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 @@ 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 @@ 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 @@ /* Silence GCC. */ umv = mv = intlen = 0; - bzero(buf, BUFSIZ); bzero(fmt, BUFSIZ); bzero(name, BUFSIZ); qoid[0] = CTL_SYSCTL; @@ -1008,25 +1064,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)) && @@ -1109,6 +1148,8 @@ 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 @@ 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 @@ 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 @@ } 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 --- /dev/null +++ b/sbin/sysctl/tests/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +ATF_TESTS_SH= sysctl_test + +.include diff --git a/sbin/sysctl/tests/Makefile.depend b/sbin/sysctl/tests/Makefile.depend new file mode 100644 --- /dev/null +++ b/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 diff --git a/sbin/sysctl/tests/sysctl_test.sh b/sbin/sysctl/tests/sysctl_test.sh new file mode 100644 --- /dev/null +++ b/sbin/sysctl/tests/sysctl_test.sh @@ -0,0 +1,116 @@ +# 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. + +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 +}