diff --git a/share/man/man3/Makefile b/share/man/man3/Makefile --- a/share/man/man3/Makefile +++ b/share/man/man3/Makefile @@ -29,6 +29,7 @@ siginfo.3 \ stats.3 \ stdarg.3 \ + sysctlinfo.3 \ sysexits.3 \ tgmath.3 \ timeradd.3 \ @@ -297,6 +298,11 @@ stdarg.3 va_end.3 \ stdarg.3 varargs.3 \ stdarg.3 va_start.3 +MLINKS+= sysctlinfo.3 SYSCTLINFO.3 \ + sysctlinfo.3 SYSCTLINFO_BYNAME.3 \ + sysctlinfo.3 SYSCTLINFO_DESOBJ.3 \ + sysctlinfo.3 SYSCTLINFO_DESOBJ_NEXTOID.3 \ + sysctlinfo.3 SYSCTLINFO_DESOBJ_NEXTNAME.3 MLINKS+= timeradd.3 timerclear.3 \ timeradd.3 timercmp.3 \ timeradd.3 timerisset.3 \ diff --git a/share/man/man3/sysctlinfo.3 b/share/man/man3/sysctlinfo.3 new file mode 100644 --- /dev/null +++ b/share/man/man3/sysctlinfo.3 @@ -0,0 +1,406 @@ +.\" +.\" Copyright (c) 2019-2021 Alfonso Sabato Siciliano +.\" +.\" 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. +.\" +.Dd February 21, 2021 +.Dt SYSCTLINFO 3 +.Os +.Sh NAME +.Nm SYSCTLINFO , +.Nm SYSCTLINFO_BYNAME , +.Nm SYSCTLINFO_DESOBJ , +.Nm SYSCTLINFO_DESOBJ_NEXTOID , +.Nm SYSCTLINFO_DESOBJ_NEXTNAME +.Nd sysctl MIB-Tree interface +.Sh SYNOPSIS +.In sys/types.h +.In sys/sysctl.h +.In sysctlinfo.h +.Fd #define OBJNAME +.Fd #define OBJID_BYNAME +.Fd #define FAKEOBJNAME +.Fd #define FAKEOBJID_BYNAME +.Fd #define OBJDESC +.Fd #define OBJDESC_BYNAME +.Fd #define OBJLABEL +.Fd #define OBJLABEL_BYNAME +.Fd #define OBJKIND +.Fd #define OBJKIND_BYNAME +.Fd #define OBJFMT +.Fd #define OBJFMT_BYNAME +.Fd #define NEXTOBJNODE +.Fd #define NEXTOBJNODE_BYNAME +.Fd #define NEXTOBJLEAF +.Fd #define NEXTOBJLEAF_BYNAME +.Fd #define SEROBJ +.Fd #define SEROBJ_BYNAME +.Fd #define SEROBJNEXTNODE +.Fd #define SEROBJNEXTNODE_BYNAME +.Fd #define SEROBJNEXTLEAF +.Fd #define SEROBJNEXTLEAF_BYNAME +.Fd #define OBJHASHANDLER +.Fd #define OBJHASHANDLER_BYNAME +.Fd #define NEXTOBJLEAFAVOIDSKIP +.Ft "int" +.Fo SYSCTLINFO +.Fa "int *id" +.Fa "size_t idlevel" +.Fa "int prop[2]" +.Fa "void *buf" +.Fa "size_t *buflen" +.Fc +.Ft "int" +.Fn SYSCTLINFO_BYNAME "char *name" "int prop[2]" "void *buf" "size_t *buflen" +.Ft "void" +.Fo SYSCTLINFO_DESOBJ +.Fa "void *buf" +.Fa "size_t idlevel" +.Fa "int *id" +.Fa "char *namep" +.Fa "char *descrp" +.Fa "unsigned int kind" +.Fa "char *fmtp" +.Fa "char *labelp" +.Fc +.Ft "void" +.Fo SYSCTLINFO_DESOBJ_NEXTOID +.Fa "void *buf" +.Fa "size_t idlevel" +.Fa "int *id" +.Fa "char *namep" +.Fa "char *descrp" +.Fa "unsigned int kind" +.Fa "char *fmtp" +.Fa "char *labelp" +.Fa "size_t idnextlevel" +.Fa "int *idnext" +.Fc +.Ft "void" +.Fo SYSCTLINFO_DESOBJ_NEXTNAME +.Fa "void *buf" +.Fa "size_t idlevel" +.Fa "int *id" +.Fa "char *namep" +.Fa "char *descrp" +.Fa "unsigned int kind" +.Fa "char *fmtp" +.Fa "char *labelp" +.Fa "char *namenextp" +.Fc +.Sh DESCRIPTION +Macros to wrap the +.Xr sysctlinfo 4 +interface for exploring the sysctl MIB-Tree and for getting the properties of an +object, as it is not designed to get and set object values, anyone wishing to do +this should see +.Xr sysctl 3 . +.Pp +An object is identified by an Object Identifier +.Pq OID , +a series of numbers, represented by a pair +.Fa "int *id" +and +.Fa "size_t idlevel" , +the level should be between 1 and +.Dv CTL_MAXNAME . +It is possible to replace a number with a string to obtain an object name, e.g., +[1.1] \(-> +.Dq kern.ostype . +.Pp +.Fn SYSCTLINFO +and +.Fn SYSCLINFO_BYNAME +seek the node with +.Fa id +/ +.Fa idlevel +or +.Fa name , +then the information specified by +.Fa prop +is copied into the buffer +.Fa buf . +Before the call +.Fa buflen +gives the size of +.Fa buf , +after a successful call +.Fa buflen +gives the amount of data copied; the size of the info can be determined with the +NULL argument for +.Fa buf , +the size will be returned in the location pointed to by +.Fa buflen . +The value of +.Fa prop[0] +should be +.Dv CTL_SYSCTL +and +.Fa prop[1] +can specify the desired info, the possible values are listed later. +.Pp +.Fn SYSCTLINFO +accepts an array +.Fa id +of +.Fa idlevel +elements and the following +.Fa prop[1] : +.Bl -tag -width indent +.It Dv OBJFAKENAME +if the object exists +.Fa buf +is set like a string with its name, otherwise +.Fn SYSCTLINFO +build a name depending on the +.Fa id , +e.g., with a non-existent OID +[1.1.100.500.1000] the name is +.Dq kern.ostype.100.500.1000 . +.It Dv OBJNAME , Dv OBJDESC , Dv OBJLABEL and Dv OBJFMT +set +.Fa buf +like a string with the name, description, label and format, respectively. +.It Dv OBJKIND +sets +.Fa buf +like an unsigned int with the kind of the object, its format is: 3 bytes for +flags and 1 byte for type, they are defined in +.In sys/sysctl.h . +.It HASHANDLER +sets +.Fa buf +like a bool value, +.Dv true +if the object has a defined handler, +.Dv false +otherwise. +.It Dv NEXTOBJLEAF and Dv NEXTOBJNODE +set +.Fa buf +like an integer array with the OID of the next leaf or also internal visited in +a +.Dq Depth-First Traversal , +the next level is +.Dq buflen / sizeof(int) +to be consistent with the acceptable level range. +.It FAKENEXTOBJLEAFNOSKIP +sets +.Fa buf +like the OID of the next leaf ignoring the objects with the +.Dv SKIP +flag; this feature exists for compatibility with the undocumented interface, see +.Sx IMPLEMENTATION NOTES . +.It Dv DESOBJ +serializes the object info in +.Fa buf , +it should be deserialized via +.Fn SYSCTLINFO_DESOBJ , +if description, format or label is NULL, descp, fmtp or labep is set to +.Dq +respectively. +.It Dv SEROBJNEXTNODE and Dv SEROBJNEXTLEAF +serialize the object info like +.Dv DESOBJ +adding the next Object OID, +.Fa buf +should be deserialized via +.Fn SYSCTLINFO_DESOBJ_NEXTOID , +if no next object exists +.Fa idnextlevel +is set to 0. +.El +.Pp +.Fn SYSCTLINFO_BYNAME +accepts a string +.Fa name +and the following +.Fa prop[1] : +.Bl -tag -width indent +.It Dv OBJFAKEID_BYNAME and Dv OBJID_BYNAME +set +.Fa buf +like an integer array with the OID of the object, the level is +.Dq buflen / sizeof(int) . +OBJFAKEID_BYNAME builds an incomplete OID if some level name is +.Dq , +e.g., name +.Dq n1.n2.n3. +\(-> [1.2.3]. +.It Dv OBJDESC_BYNAME , Dv OBJLABEL_BYNAME and Dv OBJFMT_BYNAME +set +.Fa buf +like a string with the description, label and format respectively. +.It Dv OBJKIND_BYNAME +sets +.Fa buf +like an unsigned int with the kind of the object, its format is: 3 bytes for +flags and 1 byte for type, they are defined in +.In sys/sysctl.h . +.It HASHANDLER_BYNAME +sets +.Fa buf +like a bool value, +.Dv true +if the object has a defined handler, +.Dv false +otherwise. +.It Dv NEXTOBJLEAF_BYNAME and Dv NEXTOBJNODE_BYNAME +set +.Fa buf +copy the name of the next leaf or also internal node visited in a +.Dq Depth-First Traversal . +.It Dv DESOBJ_BYNAME +serializes the object info in +.Fa buf , +it should be deserialized via +.Fn SYSCTLINFO_DESOBJ , +if description, format or label is NULL, descp, fmtp or labep is set to +.Dq +respectively. +.It Dv SEROBJNEXTLEAF_BYNAME and Dv SEROBJNEXTNODE_BYNAME +serialize the object info like +.Dv DESOBJ +adding the next object name, +.Fa buf +should be deserialized via +.Fn SYSCTLINFO_DESOBJ_NEXTNAME , +if no next object exists +.Fa nextnamep +is set to +.Dq . +.El +.Sh IMPLEMENTATION NOTES +The kernel provides an undocumented interface for the info of the sysctl tree, +.Nm sysctlinfo +and the current kernel interface can coexist. +.Pp +In +.Dq capability mode , +see +.Xr cap_enter 2 , +.Nm sysctlinfo +checks if the object has +.Dv CTLFLAG_CAPRD +or +.Dv CTLFLAG_CAPWR +before to return its info. +.Dv NEXTOBJNODE , +.Dv NEXTOBJLEAF , +.Dv NEXTOBJNODE_BYNAME +and +.Dv NEXTOBJLEAF_BYNAME +ignore capability flags to traverse the tree also in capability mode, +.Dv OBJFAKENAME +ignores capability flags for compatibility with the kernel interface. +.Sh RETURN VALUES +.Rv -std SYSCTLINFO SYSCTLINFO_BYNAME +.Sh EXAMPLES +If installed: +.Dl /usr/local/share/examples/sysctlinfo/ +.Pp +Example to explore the MIB printing only names +.Pp +.Bd -literal -offset indent -compact +char name[MAXPATHLEN], next[MAXPATHLEN]; +int prop[2] = {CTL_SYSCTL, NEXTOBJNODE_BYNAME}; +size_t nextlen = MAXPATHLEN; + +strcpy(name, "sysctl"); +while(SYSCTLINFO_BYNAME(name, prop, next, &nextlen) == 0) { + printf("%s\\n", name); + strncpy(name, next, nextlen); + nextlen = MAXPATHLEN; +} +.Ed +.Pp +Example to explore the MIB printing all of the properties: +.Pp +.Bd -literal -offset indent -compact +int i, id[CTL_MAXNAME], *idp_unused, *idnextp; +size_t idlevel, idnextlevel, buflen, lev_unused; +unsigned int kind; +char buf[BUFSIZE], *namep, *descrp, *fmtp, *labelp; +int prop[2] = {CTL_SYSCTL, SEROBJNEXTNODE}; + +id[0] = 0; +idlevel = 1; + +for (;;) { + buflen = BUFSIZE; + if(SYSCTLINFO(id, idlevel, prop, buf, &buflen) != 0) + err(1, "SEROBJNEXTNODE"); + + SYSCTLINFO_DESOBJ_NEXTOID(buf, lev_unused, idp_unused, namep, + descrp, kind, fmtp, labelp, idnextlevel, idnextp); + + for (i = 0; i < idlevel; i++) + printf("%d%c", id[i], i+1 < idlevel ? '.' : '\\n'); + printf("name: %s\\n", namep); + printf("descr: %s\\n", descrp); + printf("label: %s\\n", labelp); + printf("fmt: %s\\n", fmtp); + printf("kind: %u\\n", kind); + printf("flags: %u\\n", kind & 0xfffffff0); + printf("type: %u\\n", kind & CTLTYPE); + printf("------------------------------------\\n"); + + if (idnextlevel < 1) + break; + + memcpy(id, idnextp, idnextlevel * sizeof(int)); + idlevel = idnextlevel; +} +.Ed +.Sh ERRORS +The following errors may be reported: +.Bl -tag -width Er +.It Bq Er ECAPMODE +In capability mode the object has not the CTLFLAG_CAPRD or CTLFLAG_CAPWR flag. +.It Bq Er EINVAL +.Fa name +has more than CTL_MAXNAME levels. +.It Bq Er EINVAL +.Fa idlevel +is either greater CTL_MAXNAME, equal to zero or is not an integer. +.It Bq Er ENAMETOOLONG +.Fa name +is >= MAXPATHLEN. +.It Bq Er ENOATTR +The object exists but its info is NULL. +.It Bq Er ENOENT +The object does not exist. +.El +.Sh SEE ALSO +.Xr cap_enter 2 , +.Xr sysctl 3 , +.Xr sysctlinfo 4 +.Sh HISTORY +The +.Nm sysctlinfo +interface first appeared in +.Fx 13.0 . +.Sh AUTHORS +.Nm sysctlinfo +was written by +.An Alfonso Sabato Siciliano Aq Mt alf.siciliano@gmail.com . diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -540,6 +540,7 @@ syncache.4 \ syncer.4 \ syscons.4 \ + sysctlinfo.4 \ sysmouse.4 \ tap.4 \ targ.4 \ diff --git a/share/man/man4/sysctlinfo.4 b/share/man/man4/sysctlinfo.4 new file mode 100644 --- /dev/null +++ b/share/man/man4/sysctlinfo.4 @@ -0,0 +1,80 @@ +.\" +.\" Copyright (c) 2019-2021 Alfonso Sabato Siciliano +.\" +.\" 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. +.\" +.Dd February 21, 2021 +.Dt SYSCTLINFO 4 +.Os +.Sh NAME +.Nm sysctlinfo +.Nd sysctl MIB-Tree Interface +.Sh SYNOPSIS +.Cd sysctl.objname +.Cd sysctl.objid_byname +.Cd sysctl.objfakename +.Cd sysctl.objfakeid_byname +.Cd sysctl.objdesc +.Cd sysctl.objdesc_byname +.Cd sysctl.objlabel +.Cd sysctl.objlabel_byname +.Cd sysctl.objkind +.Cd sysctl.objkind_byname +.Cd sysctl.objfmt +.Cd sysctl.objfmt_byname +.Cd sysctl.nextobjnode +.Cd sysctl.nextobjnode_byname +.Cd sysctl.nextobjleaf +.Cd sysctl.nextobjleaf_byname +.Cd sysctl.serobj +.Cd sysctl.serobj_byname +.Cd sysctl.serobjnextnode +.Cd sysctl.serobjnextnode_byname +.Cd sysctl.serobjnextleaf +.Cd sysctl.serobjnextleaf_byname +.Cd sysctl.objhashandler +.Cd sysctl.objhashandler_byname +.Cd sysctl.fakenextobjleafnoskip +.Sh DESCRIPTION +The +.Nm +interface explores the sysctl MIB-Tree to pass the info of the objects to +userland, it should be used via +.Xr sysctlinfo 3 . +.Pp +To load the required kernel modules at boot time, place the following lines in +.Xr loader.conf 5 : +.Bd -literal -offset indent +sysctlinfo_load="YES" +.Ed +.Sh SEE ALSO +.Xr sysctl 3 , +.Xr sysctlinfo 3 +.Sh HISTORY +The +.Nm sysctlinfo +interface first appeared in +.Fx 13.0 . +.Sh AUTHORS +.Nm sysctlinfo +was written by +.An Alfonso Sabato Siciliano Aq Mt alf.siciliano@gmail.com . diff --git a/sys/kern/kern_mib.c b/sys/kern/kern_mib.c --- a/sys/kern/kern_mib.c +++ b/sys/kern/kern_mib.c @@ -59,11 +59,410 @@ #include #include #include +#include #include #include SYSCTL_ROOT_NODE(0, sysctl, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "Sysctl internal magic"); + +/* sysctlinfo interface */ +#define SYSCTLINFO_F_BYNAME 0x8000 +#define SYSCTLINFO_F_NODE 0x4000 +#define SYSCTLINFO_F_LEAF 0x2000 +#define SYSCTLINFO_F_NOCAP 0x1000 +#define SYSCTLINFO_INFOMASK 0x00FF /* CTL_AUTO_START 100, sysctl.h */ + +/* Utils, the caller has to hold a lock */ +static int +sysctlinfo_nextnode_locked(struct sysctl_oid *path[CTL_MAXNAME], + int id[CTL_MAXNAME], size_t *level, int info) +{ + struct sysctl_oid_list *children; + size_t l = *level; + bool nextleaf = (info & SYSCTLINFO_F_LEAF) ? true : false; + + info = info & SYSCTLINFO_INFOMASK; + + for (;;) { + children = SYSCTL_CHILDREN(path[l-1]); + if (children != NULL && !SLIST_EMPTY(children)) { /* children */ + path[l]=SLIST_FIRST(children); + l++; + } else { /* no child */ + for (;;) { + if (l <= 0) + break; + if (SLIST_NEXT(path[l-1], oid_link) != NULL) { + /* brother */ + path[l-1] = SLIST_NEXT(path[l-1], oid_link); + break; + } else { /* no brother */ + l--; + } + } + } + + if (l <= 0 || l > CTL_MAXNAME) + return (ENOATTR); + + id[l-1]=path[l-1]->oid_number; + if (path[l-1]->oid_kind & CTLFLAG_DORMANT) + continue; + + if (info == FAKENEXTOBJLEAFNOSKIP && + path[l-1]->oid_kind & CTLFLAG_SKIP) + continue; + + if (nextleaf) { + if ((path[l-1]->oid_kind & CTLTYPE) != CTLTYPE_NODE || + path[l-1]->oid_handler !=NULL) + break; + } else { + break; + } + } + + *level=l; + return 0; +} + +static int +sysctlinfo_outname_locked(struct sysctl_req *req, size_t idlevel, + struct sysctl_oid *path[CTL_MAXNAME]) +{ + int i, error; + + for (i=0; i < idlevel; i++) { + if (i > 0) + error = SYSCTL_OUT(req, ".", 1); + if (!error) { + error = SYSCTL_OUT(req, path[i]->oid_name, + strlen(path[i]->oid_name)); + } + if (error) + return error; + } + error = SYSCTL_OUT(req, "", 1); + + return error; +} + +static int +sysctlinfo_serobj_locked(struct sysctl_req *req, + struct sysctl_oid *path[CTL_MAXNAME], int id[CTL_MAXNAME], size_t idlevel, + struct sysctl_oid *oid) +{ + int error; + + /* idlevel */ + if ((error = SYSCTL_OUT(req, &idlevel, sizeof (size_t))) != 0) + return error; + /* id */ + if ((error = SYSCTL_OUT(req, id, idlevel * sizeof (int))) != 0) + return error; + /* name */ + if((error = sysctlinfo_outname_locked(req, idlevel, path)) !=0) + return error; + /* desc */ + if (oid->oid_descr == NULL) + error = SYSCTL_OUT(req, "", 1); + else + error = SYSCTL_OUT_STR(req, oid->oid_descr); + if (error != 0) + return error; + /* kind */ + error = SYSCTL_OUT(req, &oid->oid_kind, sizeof(oid->oid_kind)); + if (error) + return error; + /* fmt */ + if (oid->oid_fmt == NULL) + error = SYSCTL_OUT(req, "", 1); + else + error = SYSCTL_OUT_STR(req, oid->oid_fmt); + if(error != 0) + return error; + /* label */ + if (oid->oid_label == NULL) + error = SYSCTL_OUT(req, "", 1); + else + error = SYSCTL_OUT_STR(req, oid->oid_label); + + return error; +} + +/* Interface implementation */ +static int +sysctlinfo_interface(SYSCTL_HANDLER_ARGS) +{ + struct rm_priotracker tracker; + int error = 0, id[CTL_MAXNAME], indx = 0, i; + size_t idlevel, info = arg2 & SYSCTLINFO_INFOMASK; + struct sysctl_oid *oid, *path[CTL_MAXNAME]; + struct sysctl_oid_list *lsp = &sysctl__children; + char buf[SYSCTLINFO_MAXFAKENAME], name_input[MAXPATHLEN], *name_inputp; + bool byname, has_handler; + + if (req->newlen == 0) + return (EINVAL); + + /* Get OID or name from userspace */ + byname = (arg2 & SYSCTLINFO_F_BYNAME) ? true : false; + + if (byname) { + if (req->newlen >= MAXPATHLEN) + return (ENAMETOOLONG); + memset(name_input, 0, MAXPATHLEN); + if ((error = SYSCTL_IN(req, name_input, req->newlen)) !=0) + return (error); + idlevel=1; + for (i=0; inewlen; i++) { + if (name_input[i] == '.') { + name_input[i] = '\0'; + if (name_input[i+1] == '\0' && + info == OBJFAKEID_BYNAME) + break; + idlevel++; + if (idlevel > CTL_MAXNAME) + return (EINVAL); + } + } + name_inputp = &name_input[0]; + } else { + if (req->newlen > CTL_MAXNAME * sizeof(int)) + return (EINVAL); + if (req->newlen % sizeof(int) != 0) + return (EINVAL); + if ((error = SYSCTL_IN(req, id, req->newlen)) !=0) + return (error); + + idlevel = req->newlen / sizeof(int); + } + + /* + * The MIB-Tree is a critical section, sysctl() gets the lock + * but sysctl_root_handler_locked() unlock before to call this handler + */ + sysctl_rlock(&tracker); + + /* Find object */ + while (indx < idlevel) { + SLIST_FOREACH(oid, lsp, oid_link) { + if (byname) { + if (strcmp(name_inputp, oid->oid_name) == 0) + break; + } else { + if (oid->oid_number == id[indx]) + break; + } + } + if (oid == NULL) { + break; + } + path[indx] = oid; + if (byname) + id[indx] = oid->oid_number; + indx++; + + if (indx == idlevel) { + KASSERT((oid->oid_kind & CTLFLAG_DYING) == 0, + ("%s found DYING node %p", __func__, oid)); + + if ((oid->oid_kind & CTLTYPE) != CTLTYPE_NODE && + (oid->oid_kind & CTLFLAG_DORMANT) != 0) { + error = ENOENT; + goto out; + } + } else { /* indx < idlevel */ + if (byname) + name_inputp += (strlen(name_inputp) + 1); + if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE && + oid->oid_handler == NULL) { + lsp = SYSCTL_CHILDREN(oid); + } else { + break; + } + } + } + + if (indx != idlevel && info != OBJFAKENAME) { + error = ENOENT; + goto out; + } + + /* Capability check */ +#ifdef CAPABILITY_MODE + if (IN_CAPABILITY_MODE(req->td)) { + if (!(oid->oid_kind & CTLFLAG_CAPRD) && + !(oid->oid_kind & CTLFLAG_CAPWR) && + !(arg2 & SYSCTLINFO_F_NOCAP)) { + error = ECAPMODE; + goto out; + } + } +#endif + + /* Pass info to userspace */ + switch (info) { + case OBJID_BYNAME: + case OBJFAKEID_BYNAME: + error = SYSCTL_OUT(req, id, idlevel * sizeof (int)); + break; + case OBJNAME: + error = sysctlinfo_outname_locked(req, idlevel, path); + break; + case OBJFAKENAME: + for (i=0; i < idlevel; i++) { + if (i > 0) + if ((error = SYSCTL_OUT(req, ".", 1)) != 0) + goto out; + + if (i < indx) { + error = SYSCTL_OUT(req, path[i]->oid_name, + strlen(path[i]->oid_name)); + } else { + snprintf(buf, sizeof(buf), "%d", id[i]); + error = SYSCTL_OUT(req, buf, strlen(buf)); + } + if (error) + goto out; + } + error = SYSCTL_OUT(req, "", 1); + break; + case OBJDESC: + case OBJDESC_BYNAME: + if (oid->oid_descr == NULL) + error = ENOATTR; + else + error = SYSCTL_OUT_STR(req, oid->oid_descr); + break; + case OBJKIND: + case OBJKIND_BYNAME: + error = SYSCTL_OUT(req, &oid->oid_kind, sizeof(oid->oid_kind)); + break; + case OBJFMT: + case OBJFMT_BYNAME: + if (oid->oid_fmt == NULL) + error = ENOATTR; + else + error = SYSCTL_OUT_STR(req, oid->oid_fmt); + break; + case OBJLABEL: + case OBJLABEL_BYNAME: + if (oid->oid_label == NULL) + error = ENOATTR; + else + error = SYSCTL_OUT_STR(req, oid->oid_label); + break; + case NEXTOBJNODE: + case NEXTOBJLEAF: + case FAKENEXTOBJLEAFNOSKIP: + error = sysctlinfo_nextnode_locked(path, id, &idlevel, arg2); + if (error != 0) + break; + error = SYSCTL_OUT(req, id, idlevel * sizeof (int)); + break; + case NEXTOBJNODE_BYNAME: + case NEXTOBJLEAF_BYNAME: + error = sysctlinfo_nextnode_locked(path, id, &idlevel, arg2); + if (error != 0) + break; + error = sysctlinfo_outname_locked(req, idlevel, path); + break; + case SEROBJ: + case SEROBJ_BYNAME: + error = sysctlinfo_serobj_locked(req, path, id, idlevel, oid); + break; + case SEROBJNEXTNODE: + case SEROBJNEXTLEAF: + error = sysctlinfo_serobj_locked(req, path, id, idlevel, oid); + if (error != 0) + break; + error = sysctlinfo_nextnode_locked(path, id, &idlevel, arg2); + if (error != 0) + idlevel=0; + if((error = SYSCTL_OUT(req, &idlevel, sizeof (size_t))) != 0) + break; + error = SYSCTL_OUT(req, id, idlevel * sizeof (int)); + break; + case SEROBJNEXTNODE_BYNAME: + case SEROBJNEXTLEAF_BYNAME: + error = sysctlinfo_serobj_locked(req, path, id, idlevel, oid); + if (error != 0) + break; + error = sysctlinfo_nextnode_locked(path, id, &idlevel, arg2); + if (error != 0) + idlevel=0; + error = sysctlinfo_outname_locked(req, idlevel, path); + break; + case OBJHASHANDLER: + case OBJHASHANDLER_BYNAME: + has_handler = oid->oid_handler != NULL; + error = SYSCTL_OUT(req, &has_handler, sizeof (bool)); + break; + default: + error = EOPNOTSUPP; + } + +out: + sysctl_runlock(&tracker); + return (error); +} + +#define ADDSYSCTL(OID, name, flags, desc) \ + SYSCTL_PROC(_sysctl, OID, name, CTLTYPE_OPAQUE | CTLFLAG_RW | \ + CTLFLAG_ANYBODY | CTLFLAG_MPSAFE | CTLFLAG_CAPRW, NULL, \ + flags | OID, sysctlinfo_interface, "MIB", desc) + +ADDSYSCTL(OBJNAME, objname, 0, "Object name by its OID"); +ADDSYSCTL(OBJID_BYNAME, objid_byname, SYSCTLINFO_F_BYNAME, + "Object ID by its name"); +ADDSYSCTL(OBJFAKENAME, objfakename, SYSCTLINFO_F_NOCAP, + "Object (possible fake) name"); +ADDSYSCTL(OBJFAKEID_BYNAME, objfakeid_byname, SYSCTLINFO_F_BYNAME, + "Object (possible fake) ID by its name"); +ADDSYSCTL(OBJDESC, objdesc, 0, "Object description string"); +ADDSYSCTL(OBJDESC_BYNAME, objdesc_byname, SYSCTLINFO_F_BYNAME, + "Object description by its name"); +ADDSYSCTL(OBJLABEL, objlabel, 0, "Object label"); +ADDSYSCTL(OBJLABEL_BYNAME, objlabel_byname, SYSCTLINFO_F_BYNAME, + "Object label by its name"); +ADDSYSCTL(OBJKIND, objkind, 0, "Object kind (flags and type)"); +ADDSYSCTL(OBJKIND_BYNAME, objkind_byname, SYSCTLINFO_F_BYNAME, + "Object kind by its name"); +ADDSYSCTL(OBJFMT, objfmt, 0, "Object format string"); +ADDSYSCTL(OBJFMT_BYNAME, objfmt_byname, SYSCTLINFO_F_BYNAME, + "object format string by its name"); +ADDSYSCTL(NEXTOBJNODE, nextobjnode, (SYSCTLINFO_F_NODE | SYSCTLINFO_F_NOCAP), + "Next object (internal node or leaf)"); +ADDSYSCTL(NEXTOBJNODE_BYNAME, nextobjnode_byname, + (SYSCTLINFO_F_NODE | SYSCTLINFO_F_BYNAME | SYSCTLINFO_F_NOCAP), + "Next object name (internal node or leaf)"); +ADDSYSCTL(NEXTOBJLEAF, nextobjleaf, (SYSCTLINFO_F_LEAF | SYSCTLINFO_F_NOCAP), + "Next object (only leaf)"); +ADDSYSCTL(NEXTOBJLEAF_BYNAME, nextobjleaf_byname, + (SYSCTLINFO_F_LEAF | SYSCTLINFO_F_BYNAME | SYSCTLINFO_F_NOCAP), + "Next object name (only leaf)"); +ADDSYSCTL(SEROBJ, serobj, 0, "Serialize object properties"); +ADDSYSCTL(SEROBJ_BYNAME, serobj_byname, SYSCTLINFO_F_BYNAME, + "Serialize object by its name"); +ADDSYSCTL(SEROBJNEXTNODE, serobjnextnode, SYSCTLINFO_F_NODE, + "Serialize object with next-node-OID"); +ADDSYSCTL(SEROBJNEXTNODE_BYNAME, serobjnextnode_byname, + (SYSCTLINFO_F_NODE | SYSCTLINFO_F_BYNAME), + "Serialize object by its name with next-node-name"); +ADDSYSCTL(SEROBJNEXTLEAF, serobjnextleaf, SYSCTLINFO_F_LEAF, + "Serialize object with next-leaf-OID"); +ADDSYSCTL(SEROBJNEXTLEAF_BYNAME, serobjnextleaf_byname, + (SYSCTLINFO_F_LEAF | SYSCTLINFO_F_BYNAME), + "Serialize object by its name with next-leaf-name"); +ADDSYSCTL(OBJHASHANDLER, objhashandler, 0, "Object has a handler"); +ADDSYSCTL(OBJHASHANDLER_BYNAME, objhashandler_byname, + SYSCTLINFO_F_BYNAME, "Object has a handler by its name"); +ADDSYSCTL(FAKENEXTOBJLEAFNOSKIP, fakenextobjleafnoskip, + SYSCTLINFO_F_LEAF, "Next object (only leaf) avoiding SKIP flag"); +/* end sysctinfo */ + SYSCTL_ROOT_NODE(CTL_KERN, kern, CTLFLAG_RW | CTLFLAG_CAPRD | CTLFLAG_MPSAFE, 0, "High kernel, proc, limits &c"); SYSCTL_ROOT_NODE(CTL_VM, vm, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c --- a/sys/kern/kern_sysctl.c +++ b/sys/kern/kern_sysctl.c @@ -88,10 +88,10 @@ * The sysctllock protects the MIB tree. It also protects sysctl * contexts used with dynamic sysctls. The sysctl_register_oid() and * sysctl_unregister_oid() routines require the sysctllock to already - * be held, so the sysctl_wlock() and sysctl_wunlock() routines are - * provided for the few places in the kernel which need to use that - * API rather than using the dynamic API. Use of the dynamic API is - * strongly encouraged for most code. + * be held, so the sysctl_wlock(), sysctl_wunlock() sysctl_rlock() and + * sysctl_runlock() routines are provided for the few places in the kernel which + * need to use that API rather than using the dynamic API. Use of the dynamic + * API is strongly encouraged for most code. * * The sysctlmemlock is used to limit the amount of user memory wired for * sysctl requests. This is implemented by serializing any userland @@ -160,6 +160,20 @@ SYSCTL_WUNLOCK(); } +void +sysctl_rlock(struct rm_priotracker *tracker) +{ + + SYSCTL_RLOCK(tracker); +} + +void +sysctl_runlock(struct rm_priotracker *tracker) +{ + + SYSCTL_RUNLOCK(tracker); +} + static int sysctl_root_handler_locked(struct sysctl_oid *oid, void *arg1, intmax_t arg2, struct sysctl_req *req, struct rm_priotracker *tracker) diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -1100,6 +1100,8 @@ #ifdef _KERNEL +#include + #define CTL_P1003_1B_MAXID 26 /* @@ -1170,6 +1172,8 @@ int *nindx, struct sysctl_req *req); void sysctl_wlock(void); void sysctl_wunlock(void); +void sysctl_rlock(struct rm_priotracker *tracker); +void sysctl_runlock(struct rm_priotracker *tracker); int sysctl_wire_old_buffer(struct sysctl_req *req, size_t len); int kern___sysctlbyname(struct thread *td, const char *name, size_t namelen, void *old, size_t *oldlenp, void *new, diff --git a/sys/sys/sysctlinfo.h b/sys/sys/sysctlinfo.h new file mode 100644 --- /dev/null +++ b/sys/sys/sysctlinfo.h @@ -0,0 +1,90 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019-2021 Alfonso Sabato Siciliano + * + * 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. + */ + +#ifndef _SYSCTLINFO_H_ +#define _SYSCTLINFO_H_ + +/* MIB level 2: sysctlinfo OIDs */ +/* #define OBJIDEXTENDED_BYNAME 10 sysctlbyname_improved */ +#define OBJNAME 11 +#define OBJID_BYNAME 12 +#define OBJFAKENAME 13 +#define OBJFAKEID_BYNAME 14 +#define OBJDESC 15 +#define OBJDESC_BYNAME 16 +#define OBJLABEL 17 +#define OBJLABEL_BYNAME 18 +#define OBJKIND 19 +#define OBJKIND_BYNAME 20 +#define OBJFMT 21 +#define OBJFMT_BYNAME 22 +#define NEXTOBJNODE 23 +#define NEXTOBJNODE_BYNAME 24 +#define NEXTOBJLEAF 25 +#define NEXTOBJLEAF_BYNAME 26 +#define SEROBJ 27 +#define SEROBJ_BYNAME 28 +#define SEROBJNEXTNODE 29 +#define SEROBJNEXTNODE_BYNAME 30 +#define SEROBJNEXTLEAF 31 +#define SEROBJNEXTLEAF_BYNAME 32 +#define OBJHASHANDLER 33 +#define OBJHASHANDLER_BYNAME 34 +#define FAKENEXTOBJLEAFNOSKIP 35 /* partial, no fake */ + +#define SYSCTLINFO_MAXFAKENAME 64 + +#ifndef _KERNEL +#define SYSCTLINFO(id, idlevel, prop, buf, buflen) \ + sysctl(prop, 2, buf, buflen, id, idlevel * sizeof(int)) + +#define SYSCTLINFO_BYNAME(name, prop, buf, buflen) \ + sysctl(prop, 2, buf, buflen, name, strlen(name) + 1) + +#define SYSCTLINFO_DESOBJ(buf, idlevel, id, name, descr, kind, fmt, label) do { \ + idlevel = *((size_t *)buf); \ + id = (int*) (((char*)buf) + sizeof(size_t)); \ + name = ((char *)buf) + (idlevel * sizeof(int) + sizeof(size_t)); \ + descr = strchr(name, '\0') + 1; \ + kind = *((unsigned int *) (strchr(descr, '\0') + 1)); \ + fmt = strchr(descr, '\0') + 1 + sizeof(unsigned int); \ + label = strchr(fmt, '\0') + 1; \ +} while (0) + +#define SYSCTLINFO_DESOBJ_NEXTOID(buf, idlevel, id, name, descr, kind, fmt, label, idnextlevel, idnext) do { \ + SYSCTLINFO_DESOBJ(buf, idlevel, id, name, descr, kind, fmt, label); \ + idnextlevel = *((size_t *) ((strchr(label, '\0') + 1))); \ + idnext = (int*) (strchr(label, '\0') + 1 + sizeof(size_t) ); \ +} while (0) + +#define SYSCTLINFO_DESOBJ_NEXTNAME(buf, idlevel, id, name, descr, kind, fmt, label, namenext) do { \ + SYSCTLINFO_DESOBJ(buf, idlevel, id, name, descr, kind, fmt, label); \ + namenext = strchr(label, '\0') + 1; \ +} while (0) +#endif /* ifndef _KERNEL */ + +#endif /* _SYSCTLINFO_H_ */