Index: lib/libutil/Makefile =================================================================== --- lib/libutil/Makefile +++ lib/libutil/Makefile @@ -18,8 +18,8 @@ login_auth.c login_cap.c \ login_class.c login_crypt.c login_ok.c login_times.c login_tty.c \ pidfile.c property.c pty.c pw_scan.c pw_util.c quotafile.c \ - realhostname.c stub.c trimdomain.c uucplock.c -INCS= libutil.h login_cap.h + realhostname.c stub.c sysctlmibinfo.c trimdomain.c uucplock.c +INCS= libutil.h login_cap.h sysctlmibinfo.h CFLAGS+= -DNO__SCCSID @@ -35,7 +35,7 @@ login_auth.3 login_cap.3 \ login_class.3 login_ok.3 login_times.3 login_tty.3 pidfile.3 \ property.3 pty.3 quotafile.3 realhostname.3 realhostname_sa.3 \ - _secure_path.3 trimdomain.3 uucplock.3 pw_util.3 + _secure_path.3 sysctlmibinfo.3 trimdomain.3 uucplock.3 pw_util.3 MAN+= login.conf.5 MLINKS+=flopen.3 flopenat.3 MLINKS+=kld.3 kld_isloaded.3 kld.3 kld_load.3 @@ -86,6 +86,7 @@ pw_util.3 pw_scan.3 \ pw_util.3 pw_tempname.3 \ pw_util.3 pw_tmp.3 +MLINKS+=sysctlmibinfo.3 sysctlmif.3 HAS_TESTS= SUBDIR.${MK_TESTS}+= tests Index: lib/libutil/sysctlmibinfo.h =================================================================== --- lib/libutil/sysctlmibinfo.h +++ lib/libutil/sysctlmibinfo.h @@ -0,0 +1,146 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018-2019 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. + * + * $FreeBSD$ + */ + +#ifndef _SYSCTLMIBINFO_H_ +#define _SYSCTLMIBINFO_H_ + +#include +#include +#include + +#define SYSCTLMIF_VERSION 1 +#define SYSCTLMIF_MAXIDLEVEL CTL_MAXNAME +#define SYSCTLMIF_MAXDEPTH (CTL_MAXNAME - 1) + +/* + * functions to wrap 'undocumented kern_sysctl.c API', + * return: 0 for success, negative value for failure. + */ + +int +sysctlmif_nametoid(const char *name, size_t namelen, int *id, size_t *idlevel); + +int sysctlmif_name(int *id, size_t idlevel, char *name, size_t *namelen); +int sysctlmif_desc(int *id, size_t idlevel, char *desc, size_t *desclen); +int sysctlmif_label(int *id, size_t idlevel, char *label, size_t *labellen); + +#define SYSCTLMIF_NAMELEN(id, idlevel, size) \ + sysctlmif_name(id, idlevel, NULL, size) +#define SYSCTLMIF_DESCLEN(id, idlevel, size) \ + sysctlmif_desc(id, idlevel, NULL, size) +#define SYSCTLMIF_LABELLEN(id, idlevel, size) \ + sysctlmif_label(id, idlevel, NULL, size) + +int sysctlmif_info(int *id, size_t idlevel, void *info, size_t *infolen); + +#define SYSCTLMIF_INFOKIND(info) (*((unsigned int *)info)) +#define SYSCTLMIF_INFOTYPE(info) (*((unsigned int *)info) & CTLTYPE) +#define SYSCTLMIF_INFOFLAGS(info) (*((unsigned int *)info) & 0xfffffff0) +#define SYSCTLMIF_INFOFMT(info) ((char *)info + sizeof(unsigned int)) + +/* kernel returns only next leaf, next node requires extra computation */ +int +sysctlmif_nextnode(int *id, size_t idlevel, int *idnext, size_t *idnextlevel); +int +sysctlmif_nextleaf(int *id, size_t idlevel, int *idnext, size_t *idnextlevel); + +/* + * functions related to "struct sysctlmif_object" + * params: id and idlevel to identify a mib entry + * return: NULL for failure, pointer to allocated memory for success. + */ + +/* 'struct sysctlmif_object': userspace mib entry definition */ + +SLIST_HEAD(sysctlmif_object_list, sysctlmif_object); + +struct sysctlmif_object { + SLIST_ENTRY(sysctlmif_object) object_link; + int *id; + size_t idlevel; /* between 1 and SYSCTLMIF_MAXIDLEVEL */ + char *name; + char *desc; + char *label; /* aggregation label */ + uint8_t type; /* defined in */ + uint32_t flags; /* defined in */ + char *fmt; /* format string */ + /* children is set by sysctlmif_tree() */ + struct sysctlmif_object_list *children; +}; + +/* + * OR_FLAGS: object fields to set, + * .id and .idlevel are always set + * .children is default for sysctlmif_tree() + */ +#define SYSCTLMIF_FNAME 0x01 /* .name */ +#define SYSCTLMIF_FDESC 0x02 /* .desc */ +#define SYSCTLMIF_FLABEL 0x04 /* .label */ +#define SYSCTLMIF_FTYPE 0x08 /* .type */ +#define SYSCTLMIF_FFLAGS 0x10 /* .flags */ +#define SYSCTLMIF_FFMT 0x20 /* .fmt */ +#define SYSCTLMIF_FALL /* all */ \ + (SYSCTLMIF_FNAME | SYSCTLMIF_FDESC \ + | SYSCTLMIF_FLABEL | SYSCTLMIF_FTYPE \ + | SYSCTLMIF_FFLAGS | SYSCTLMIF_FFMT) + +/* object functions */ + +struct sysctlmif_object * +sysctlmif_object(int *id, size_t idlevel, unsigned int flags); + +void +sysctlmif_freeobject(struct sysctlmif_object *object); + +/* list functions */ + +typedef int sysctlmif_filterfunc_t (struct sysctlmif_object *object); + +struct sysctlmif_object_list * +sysctlmif_filterlist(sysctlmif_filterfunc_t *filterfunc, unsigned int flags); + +#define SYSCTLMIF_LIST(flags) sysctlmif_filterlist(NULL, flags) + +struct sysctlmif_object_list * +sysctlmif_grouplist(int *id, size_t idlevel, unsigned int flags, + unsigned int max_depth); + +void +sysctlmif_freelist(struct sysctlmif_object_list *list); + +/* tree fuctions */ + +struct sysctlmif_object * +sysctlmif_tree(int *id, size_t idlevel, unsigned int flags, + unsigned int max_depth); + +void +sysctlmif_freetree(struct sysctlmif_object *object_root); + +#endif /* _SYSCTLMIBINFO_H_ */ Index: lib/libutil/sysctlmibinfo.3 =================================================================== --- lib/libutil/sysctlmibinfo.3 +++ lib/libutil/sysctlmibinfo.3 @@ -0,0 +1,424 @@ +.\" +.\" Copyright (c) 2018-2019 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. +.\" +.\" $FreeBSD$ +.\" +.Dd January 29, 2019 +.Dt SYSCTLMIBINFO 3 +.Os +.Sh NAME +.Nm SYSCTLMIF_MAXIDLEVEL , +.Nm sysctlmif_nametoid , +.Nm sysctlmif_name , +.Nm SYSCTLMIF_NAMELEN , +.Nm sysctlmif_desc , +.Nm SYSCTLMIF_DESCLEN , +.Nm sysctlmif_label , +.Nm SYSCTLMIF_LABELLEN , +.Nm sysctlmif_info , +.Nm SYSCTLMIF_INFOKIND , +.Nm SYSCTLMIF_INFOTYPE , +.Nm SYSCTLMIF_INFOFLAGS , +.Nm SYSCTLMIF_INFOFMT , +.Nm sysctlmif_nextnode , +.Nm sysctlmif_nextleaf , +.Nm sysctlmif_object , +.Nm sysctlmif_freeobject , +.Nm sysctlmif_filterlist , +.Nm SYSCTLMIF_LIST , +.Nm SYSCTLMIF_MAXDEPTH , +.Nm sysctlmif_grouplist , +.Nm sysctlmif_freelist , +.Nm sysctlmif_tree , +.Nm sysctlmif_freetree +.Nd get sysctl mib information +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In sys/types.h +.In sys/queue.h +.In sysctlmibinfo.h +.Fd #define SYSCTLMIF_MAXIDLEVEL; +.Ft "int" +.Fo sysctlmif_nametoid +.Fa "const char *name" +.Fa "size_t namelen" +.Fa "int *id" +.Fa "size_t *idlevel" +.Fc +.Ft "int" +.Fn sysctlmif_name "int *id" "size_t idlevel" "char *name" "size_t *namelen" +.Ft "int" +.Fn SYSCTLMIF_NAMELEN "int *id" "size_t idlevel" "size_t *namelen" +.Ft "int" +.Fn sysctlmif_desc "int *id" "size_t idlevel" "char *desc" "size_t *desclen" +.Ft "int" +.Fn SYSCTLMIF_DESCLEN "int *id" "size_t idlevel" "size_t *desclen" +.Ft "int" +.Fn sysctlmif_label "int *id" "size_t idlevel" "char *label" "size_t *labellen" +.Ft "int" +.Fn SYSCTLMIF_LABELLEN "int *id" "size_t idlevel" "size_t *labellen" +.Ft "int" +.Fn sysctlmif_info "int *id" "size_t idlevel" "void *info" "size_t *infolen" +.Ft "uint32_t" +.Fn SYSCTLMIF_INFOKIND "info" +.Ft "uint8_t" +.Fn SYSCTLMIF_INFOTYPE "info" +.Ft "uint32_t" +.Fn SYSCTLMIF_INFOFLAGS "info" +.Ft "char *" +.Fn SYSCTLMIF_INFOFMT "info" +.Ft "int" +.Fo sysctlmif_nextleaf +.Fa "int *id" +.Fa "size_t idlevel" +.Fa "int *nextid" +.Fa "size_t *nextidlevel" +.Fc +.Ft "int" +.Fo sysctlmif_nextnode +.Fa "int *id" +.Fa "size_t idlevel" +.Fa "int *nextid" +.Fa "size_t *nextidlevel" +.Fc +.Ft "struct sysctlmif_object *" +.Fn sysctlmif_object "int *id" "size_t idlevel" "unsigned int flags" +.Ft "void" +.Fn sysctlmif_freeobject "struct sysctlmif_object *object" +.Fd #define SYSCTLMIF_MAXDEPTH +.Ft "struct sysctlmif_object_list *" +.Fo sysctlmif_filterlist +.Fa "sysctlmif_filterfunc_t *filterfunc" +.Fa "unsigned int flags" +.Fc +.Ft "struct sysctlmif_object_list *" +.Fn SYSCTLMIF_LIST "unsigned int flags" +.Ft "struct sysctlmif_object_list *" +.Fo sysctlmif_grouplist +.Fa "int *idstart" +.Fa "size_t idstartlen" +.Fa "unsigned int flags" +.Fa "unsigned int max_depth" +.Fc +.Ft "void" +.Fn sysctlmif_freelist "struct sysctlmif_object_list *list" +.Ft "struct sysctlmif_object *" +.Fo sysctlmif_tree +.Fa "int *id" +.Fa "size_t idlevel" +.Fa "unsigned int flags" +.Fa "unsigned int max_depth" +.Fc +.Ft "void" +.Fn sysctlmif_freetree "struct sysctlmif_object *node" +.Sh DESCRIPTION +The +.Nm sysctlmibinfo +library is an interface to the kernel sysctl-mib-tree. +It implements wrappers around undocumented +.Dq sysctl.* +kernel states to get mib information and +provides a convenient API to build a mib-entry, +entries-list and mib-tree in userspace; +as it is not designed to get and set entry values, +anyone wishing to do this should see +.Xr sysctl 3 . +.Pp +A mib-entry is identified by a pair +.Fa "int *id" +and +.Fa "size_t idlevel" , +the level should be between 1 and +.Dv SYSCTLMIF_MAXIDLEVEL , +see +.Fn sysctlmif_next . +.Pp +.Fn sysctlmif_nametoid +sets +.Em id +and +.Em idlevel +like the entry with +.Em name +and +.Em namelen . +.Pp +.Fn SYSCTLMIF_NAMELEN , +.Fn SYSCTLMIF_DESCLEN +and +.Fn SYSCTLMIF_LABELLEN +set +.Em namelen , +.Em desclen , +and +.Em labellen +like the entry with +.Em id +and +.Em idlevel . +.Pp +.Fn sysctlmif_name , +.Fn sysctlmif_desc +and +.Fn sysctlmif_label +set +.Em name +and +.Em namelen , +.Em desc +and +.Em desclen , +.Em label +and +.Em labellen +like the entry with +.Em id +and +.Em idlevel . +.Pp +.Fn sysctlmif_info +sets +.Em info +and +.Em infolen +like the entry with +.Em id +and +.Em idlevel , +.Em info +has the format: +3 bytes for flags, 1 byte for type and a string for the +.Dq format string ; +flags and type are defined in +.In sys/sysctl.h . +Macros to deal with +.Em info : +.Bd -ragged -offset indent -compact +.Fn SYSCTLMIF_INFOFLAGS info +returns flags; +.Ed +.Bd -ragged -offset indent -compact +.Fn SYSCTLMIF_INFOTYPE info +returns entry type; +.Ed +.Bd -ragged -offset indent -compact +.Fn SYSCTLMIF_INFOKIND info +returns flags following by type; +.Ed +.Bd -ragged -offset indent -compact +.Fn SYSCTLMIF_INFOFMT info +returns a pointer to the +.Dq format string . +.Ed +.Pp +.Fn sysctlmif_nextleaf +sets +.Em nextid +and +.Em nextidlevel +like the next-leaf-entry of the entry with +.Em id +and +.Em idlevel . +.Fn sysctlmif_nextnode +sets +.Em nextid +and +.Em nextidlevel +like the next-[node|leaf]-entry of the entry with +.Em id +and +.Em idlevel . +Notes: +.Em nextid +should have size +.Dv SYSCTLMIF_MAXIDLEVEL +and +.Em nextidlevel +should be set to +.Dv SYSCTLMIF_MAXIDLEVEL +before to call +.Fn sysctlmif_nextleaf +or +.Fn sysctlmif_nextnode . +.Pp +.Fn sysctlmif_object +returns a pointer to allocated memory for a +.Em struct sysctlmif_object +(setting +.Em flags +members) of the entry with +.Em id +and +.Em idlevel . +A mib userspace entry is defined: +.Pp +.Bd -literal -offset indent -compact +/* 'struct sysctlmif_object': userspace mib entry definition */ + +SLIST_HEAD(sysctlmif_object_list, sysctlmif_object); + +struct sysctlmif_object { + SLIST_ENTRY(sysctlmif_object) object_link; + int *id; + size_t idlevel; /* between 1 and SYSCTLMIF_MAXIDLEVEL */ + char *name; + char *desc; + char *label; /* aggregation label */ + uint8_t type; /* defined in */ + uint32_t flags; /* defined in */ + char *fmt; /* format string */ + /* children is set by sysctlmif_tree() */ + struct sysctlmif_object_list *children; +}; + +/* + * OR_FLAGS: object fields to set, + * .id and .idlevel are always set + * .children is default for sysctlmif_tree() + */ +#define SYSCTLMIF_FNAME 0x01 /* .name */ +#define SYSCTLMIF_FDESC 0x02 /* .desc */ +#define SYSCTLMIF_FLABEL 0x04 /* .label */ +#define SYSCTLMIF_FTYPE 0x08 /* .type */ +#define SYSCTLMIF_FFLAGS 0x10 /* .flags */ +#define SYSCTLMIF_FFMT 0x20 /* .fmt */ +#define SYSCTLMIF_FALL /* all */ +.Ed +.Pp +.Fn SYSCTLMIF_LIST +allocates memory and returns a SLIST of sysctlmif_object (setting +.Em flags +members), it is an alias for +.Fn sysctlmif_filterlist NULL flags . +.Pp +.Fn sysctlmif_filterlist +allocates memory for a SLIST of sysctlmif_object (setting +.Em flags +members), an object +.Dq o +is added if +.Bd -ragged -offset indent -compact +int sysctlmif_filterfunc_t (struct sysctlmif_object *o) +.Ed +returns 0 or +.Em filterfunc +is NULL; notes: +.Fn sysctlmif_filterlist +uses +.Fn sysctlmif_nextnode +and object.children is not set. +.Pp +.Fn sysctlmif_grouplist +allocates memory and returns a SLIST of sysctlmif_object (setting +.Em flags +members) visited in a +.Dq Depth First Traversal +until +.Em max_depth , +.Em id +and +.Em idlevel +denote the root. +Notes: +.Fn sysctlmif_grouplist +uses +.Fn sysctlmif_nextnode , +object.children is not set and +.Em max_depth +can be set to +.Dv SYSCTLMIF_MAXDEPTH . +.Pp +.Fn sysctlmif_tree +allocates memory for a tree of sysctlmif_object nodes (setting +.Em flags +members) until +.Em max_depth +and returns a pointer to the root: the entry with +.Em id +and +.Em idlevel . +Notes: +.Em max_depth +can be set to +.Dv SYSCTLMIF_MAXDEPTH , +object.children is set and iterable by SLIST macros. +.Pp +.Fn sysctlmif_freeobject , +.Fn sysctlmif_freelist +and +.Fn sysctlmif_freetree +free allocated memory. +.Sh IMPLEMENTATION NOTES +.Nm sysctlmibinfo +uses +.Fn sysctl +syscall for wrapping 0.[1-6] entries defined in kern_sysctl.c. +The kernel returns only next leaf, +.Fn sysctlmif_nextnode +requires extra computation. +.Sh RETURN VALUES +.Rv -std sysctlmif_nametoid SYSCTLMIF_NAMELEN sysctlmif_name SYSCTLMIF_DESCLEN sysctlmif_desc SYSCTLMIF_LABELLEN sysctlmif_label sysctlmif_info sysctlmif_nextnode sysctlmif_nextleaf +.Pp +The +.Fn sysctlmif_object , +.Fn sysctlmif_filterlist , +.Fn SYSCTLMIF_LIST , +.Fn sysctlmif_grouplist , +.Fn sysctlmif_tree +functions return +.Dv NULL +upon error or a pointer to allocated memory for success. +.Sh EXAMPLES +Complete set of examples: +.Dl https://gitlab.com/alfix/sysctlmibinfo/tree/master/examples +.Sh SEE ALSO +.Xr queue 3 , +.Xr sysctl 3 +.Sh HISTORY +The +.Nm sysctlmibinfo +library first appeared in +.Fx 13.0 . +.Sh AUTHORS +.Nm sysctlmibinfo +and this manual page were written by +.An Alfonso S. Siciliano Aq Mt alf.siciliano@gmail.com +.Sh BUGS +Problems from the kernel space: +.Bd -ragged -offset indent -compact +.Fn sysctlmif_name +false positive. +.Ed +.Bd -ragged -offset indent -compact +.Fn sysctlmif_desc +could set +.Em desc +to: +.Dq +or +.Dv NULL +for entries without description. +.Ed Index: lib/libutil/sysctlmibinfo.c =================================================================== --- lib/libutil/sysctlmibinfo.c +++ lib/libutil/sysctlmibinfo.c @@ -0,0 +1,516 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018-2019 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include + +/* lacks these identifiers */ + +/* Top-level identifiers */ +#define CTL_SYSCTLMIB 0 + +/* CTL_SYSCTLMIB identifiers */ +#define MIB_OBJECTNAME 1 +#define MIB_NEXTOID 2 +#define MIB_NAME2OID 3 +#define MIB_OBJECTFMT 4 +#define MIB_OBJECTDESCR 5 +#define MIB_OBJECTLABEL 6 + +/* Internal use */ + +static int +sysctlmif_internal_subtree(struct sysctlmif_object *obj, unsigned int flags, + unsigned int depth) +{ + int error = 0; + struct sysctlmif_object *child; + + obj->children = sysctlmif_grouplist(obj->id, obj->idlevel, flags, 1); + if (obj->children == NULL) { + return (-1); + } + SLIST_REMOVE_HEAD(obj->children, object_link); + + if (depth < 1) { + return (0); + } + + SLIST_FOREACH(child, obj->children, object_link) { + error = sysctlmif_internal_subtree(child, flags, depth - 1); + if (error != 0) { + return (error); + } + } + + return (error); +} + +/* API implementation */ + +int +sysctlmif_nametoid(const char *name, size_t namelen, int *id, size_t *idlevel) +{ + int mib[2]; + int error = 0; + + mib[0] = CTL_SYSCTLMIB; + mib[1] = MIB_NAME2OID; + + *idlevel *= sizeof(int); + error = sysctl(mib, 2, id, idlevel, name, namelen); + *idlevel /= sizeof(int); + + return (error); +} + +int +sysctlmif_desc(int *id, size_t idlevel, char *desc, size_t *desclen) +{ + int mib[CTL_MAXNAME + 2]; + int error = 0; + + mib[0] = CTL_SYSCTLMIB; + mib[1] = MIB_OBJECTDESCR; + memcpy(mib + 2, id, idlevel * sizeof(int)); + + error = sysctl(mib, idlevel + 2, (void *)desc, desclen, NULL, 0); + + return (error); +} + +int +sysctlmif_name(int *id, size_t idlevel, char *name, size_t *namelen) +{ + int mib[CTL_MAXNAME + 2]; + int error = 0; + + mib[0] = CTL_SYSCTLMIB; + mib[1] = MIB_OBJECTNAME; + memcpy(mib + 2, id, idlevel * sizeof(int)); + + error = sysctl(mib, idlevel + 2, (void *)name, namelen, NULL, 0); + + return (error); +} + +int +sysctlmif_label(int *id, size_t idlevel, char *label, size_t *labellen) +{ + int mib[CTL_MAXNAME + 2]; + int error = 0; + + mib[0] = CTL_SYSCTLMIB; + mib[1] = MIB_OBJECTLABEL; + memcpy(mib + 2, id, idlevel * sizeof(int)); + + error = sysctl(mib, idlevel + 2, (void *)label, labellen, NULL, 0); + + return (error); +} + +int +sysctlmif_info(int *id, size_t idlevel, void *info, size_t *infosize) +{ + int mib[CTL_MAXNAME + 2]; + int error = 0; + + mib[0] = CTL_SYSCTLMIB; + mib[1] = MIB_OBJECTFMT; + memcpy(mib + 2, id, idlevel*sizeof(int)); + + error = sysctl(mib, idlevel + 2, info, infosize, NULL, 0); + + return (error); +} + +int +sysctlmif_nextleaf(int *id, size_t idlevel, int *idnext, size_t *idnextlevel) +{ + int mib[CTL_MAXNAME + 2]; + int error = 0; + size_t tmp_nextlevel; + + mib[0] = CTL_SYSCTLMIB; + mib[1] = MIB_NEXTOID; + memcpy(mib + 2, id, idlevel*sizeof(int)); + + tmp_nextlevel = *idnextlevel * sizeof(int); + + error = sysctl(mib, idlevel + 2, idnext, &tmp_nextlevel, NULL, 0); + if (error == 0) { + *idnextlevel = tmp_nextlevel / sizeof(int); + } + + return (error); +} + +int +sysctlmif_nextnode(int *id, size_t idlevel, int *idnext, size_t *idnextlevel) +{ + int error = 0; + size_t i, minlevel; + size_t tmp_nextlevel; + + /* it could be "id = idnext", then: */ + int previd[CTL_MAXNAME]; + size_t prevlevel = idlevel; + + memcpy(previd, id, idlevel * sizeof(int)); + + tmp_nextlevel = *idnextlevel * sizeof(int); + error = sysctlmif_nextleaf(id, idlevel, idnext, &tmp_nextlevel); + if (error != 0) { + return (error); + } + *idnextlevel = tmp_nextlevel; + + /* + * avoid: id 5.6 -> next 5.6.4.8.2 + * we want: id 5.6 -> next 5.6.4 (just 1 level) + */ + if (*idnextlevel > prevlevel) { + *idnextlevel = prevlevel + 1; + } + + minlevel = *idnextlevel < prevlevel ? *idnextlevel : prevlevel; + + for (i = 0; i < minlevel; i++) { + if (previd[i] != idnext[i]) { + *idnextlevel = i+1; + break; + } + } + + return (error); +} + +struct sysctlmif_object * +sysctlmif_object(int *id, size_t idlevel, unsigned int flags) +{ + struct sysctlmif_object *obj = NULL; + size_t size = 0; + void *tmpinfo = NULL; + + obj = malloc(sizeof(struct sysctlmif_object)); + if (obj == NULL) { + return (NULL); + } + memset(obj, 0, sizeof(struct sysctlmif_object)); + + /* id and idlevel are always set */ + obj->id = malloc(idlevel * sizeof(int)); + if (obj->id == NULL) { + sysctlmif_freeobject(obj); + return (NULL); + } + memcpy(obj->id, id, idlevel * sizeof(int)); + obj->idlevel = idlevel; + + if (flags & SYSCTLMIF_FNAME) { + /* kernel returns false positive */ + if (SYSCTLMIF_NAMELEN(id, idlevel, &size) == 0) { + if ((obj->name = malloc(size)) == NULL) { + sysctlmif_freeobject(obj); + return (NULL); + } + memset(obj->name, 0, size); + if (sysctlmif_name(id, idlevel, obj->name, + &size) != 0) { + obj->name = NULL; + } + } + } + + if (flags & SYSCTLMIF_FDESC) { + size = 0; + /* entry without descr could return "\0" or NULL */ + if (SYSCTLMIF_DESCLEN(id, idlevel, &size) == 0) { + if ((obj->desc = malloc(size)) == NULL) { + sysctlmif_freeobject(obj); + return (NULL); + } + memset(obj->desc, 0, size); + if (sysctlmif_desc(id, idlevel, obj->desc, + &size) != 0) { + obj->desc = NULL; + } + } + } + + if (flags & SYSCTLMIF_FLABEL) { + size = 0; + if (SYSCTLMIF_LABELLEN(id, idlevel, &size) == 0) { + if ((obj->label = malloc(size)) == NULL) { + sysctlmif_freeobject(obj); + return (NULL); + } + memset(obj->label, 0, size); + if (sysctlmif_label(id, idlevel, obj->label, + &size) != 0) { + obj->label = NULL; + } + } + } + + if ((flags & SYSCTLMIF_FFLAGS) || (flags & SYSCTLMIF_FFMT) || + (flags & SYSCTLMIF_FTYPE)) { + size = 0; + /* get info size because fmt is variable */ + if (sysctlmif_info(id, idlevel, NULL, &size) == 0) { + tmpinfo = malloc(size); + if (tmpinfo == NULL) { + sysctlmif_freeobject(obj); + return (NULL); + } + memset(tmpinfo, 0, size); + + if (sysctlmif_info(id, idlevel, tmpinfo, &size) < 0) { + sysctlmif_freeobject(obj); + free(tmpinfo); + return (NULL); + } + + if (flags & SYSCTLMIF_FFMT) { + obj->fmt = strndup(SYSCTLMIF_INFOFMT(tmpinfo), + size - sizeof(uint32_t)); + if (obj->fmt == NULL) { + sysctlmif_freeobject(obj); + return (NULL); + } + } + + if (flags & SYSCTLMIF_FFLAGS) { + obj->flags = SYSCTLMIF_INFOFLAGS(tmpinfo); + } + + if (flags & SYSCTLMIF_FTYPE) { + obj->type = SYSCTLMIF_INFOTYPE(tmpinfo); + } + + free(tmpinfo); + } + } + + return (obj); +} + +void +sysctlmif_freeobject(struct sysctlmif_object *object) +{ + if (object == NULL) { + return; + } + + free(object->id); + free(object->name); + free(object->desc); + free(object->label); + free(object->fmt); + free(object); + object = NULL; +} + +struct sysctlmif_object_list * +sysctlmif_filterlist(sysctlmif_filterfunc_t *filterfunc, unsigned int flags) +{ + int id[CTL_MAXNAME], idnext[CTL_MAXNAME]; + size_t idlevel, idnextlevel; + struct sysctlmif_object_list *list = NULL; + struct sysctlmif_object *last, *new; + + list = malloc(sizeof(struct sysctlmif_object_list)); + if (list == NULL) { + return (NULL); + } + + SLIST_INIT(list); + + id[0] = 0; + idlevel = 1; + + for (;;) { + if ((new = sysctlmif_object(id, idlevel, flags)) == NULL) { + sysctlmif_freelist(list); + return (NULL); + } + + if ((filterfunc == NULL) || (filterfunc(new) == 0)) { + if (SLIST_EMPTY(list)) { + SLIST_INSERT_HEAD(list, new, object_link); + } else { + SLIST_INSERT_AFTER(last, new, object_link); + } + + last = new; + } + + idnextlevel = CTL_MAXNAME; + if (sysctlmif_nextnode(id, idlevel, idnext, &idnextlevel) < 0) { + break; + } + memcpy(id, idnext, idnextlevel * sizeof(int)); + idlevel = idnextlevel; + } + + return (list); +} + +struct sysctlmif_object_list * +sysctlmif_grouplist(int *idstart, size_t idstartlen, unsigned int flags, + unsigned int depth) +{ + int id[CTL_MAXNAME], idnext[CTL_MAXNAME]; + size_t idlevel, idnextlevel; + struct sysctlmif_object_list *list = NULL; + struct sysctlmif_object *last, *new; + size_t i; + + list = malloc(sizeof(struct sysctlmif_object_list)); + if (list == NULL) { + return (NULL); + } + + SLIST_INIT(list); + + memcpy(id, idstart, idstartlen * sizeof(int)); + idlevel = idstartlen; + + if ((new = sysctlmif_object(id, idlevel, flags)) == NULL) { + free(list); + return (NULL); + } + + SLIST_INSERT_HEAD(list, new, object_link); + + last = new; + + for (;;) { + idnextlevel = CTL_MAXNAME; + if (sysctlmif_nextnode(id, idlevel, idnext, &idnextlevel) < 0) { + break; + } + memcpy(id, idnext, idnextlevel * sizeof(int)); + idlevel = idnextlevel; + + if (idlevel - idstartlen > depth) { + continue; + } + + if (idlevel < idstartlen) { + break; + } + + for (i = 0; i < idstartlen; i++) { + if (id[i] != idstart[i]) { + return (list); + } + } + + new = sysctlmif_object(id, idlevel, flags); + if (new == NULL) { + sysctlmif_freelist(list); + return (NULL); + } + SLIST_INSERT_AFTER(last, new, object_link); + last = new; + } + + return (list); +} + +void +sysctlmif_freelist(struct sysctlmif_object_list *list) +{ + struct sysctlmif_object *obj; + + if (list == NULL) { + return; + } + + while (!SLIST_EMPTY(list)) { + obj = SLIST_FIRST(list); + SLIST_REMOVE_HEAD(list, object_link); + sysctlmif_freeobject(obj); + } + + free(list); + list = NULL; +} + +struct sysctlmif_object * +sysctlmif_tree(int *id, size_t idlevel, unsigned int flags, + unsigned int max_depth) +{ + int error; + struct sysctlmif_object *root = NULL; + + if ((root = sysctlmif_object(id, idlevel, flags)) == NULL) { + return (NULL); + } + + if (max_depth < 1) { + return (root); + } + + error = sysctlmif_internal_subtree(root, flags, max_depth - 1); + if (error != 0) { + sysctlmif_freetree(root); + } + + return (root); +} + +/* postorder visit */ +void +sysctlmif_freetree(struct sysctlmif_object *node) +{ + struct sysctlmif_object *child; + + if (node == NULL) { + return; + } + + if (node->children != NULL) { + while (!SLIST_EMPTY(node->children)) { + child = SLIST_FIRST(node->children); + SLIST_REMOVE_HEAD(node->children, object_link); + sysctlmif_freetree(child); + } + } + + sysctlmif_freeobject(node); +}