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
This commit is contained in:
Kirk McKusick 2022-12-24 22:57:13 -08:00
parent 57cc27a332
commit f126d34981
5 changed files with 218 additions and 30 deletions

View File

@ -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

View File

@ -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++);

View File

@ -0,0 +1,5 @@
# $FreeBSD$
ATF_TESTS_SH= sysctl_test
.include <bsd.test.mk>

View File

@ -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

View File

@ -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
}