Index: lib/Makefile =================================================================== --- lib/Makefile +++ lib/Makefile @@ -82,6 +82,7 @@ libsqlite3 \ libstdbuf \ libstdthreads \ + libsysctl \ libsysdecode \ libtacplus \ libthread_db \ Index: lib/libsysctl/Makefile =================================================================== --- lib/libsysctl/Makefile +++ lib/libsysctl/Makefile @@ -0,0 +1,17 @@ +# $FreeBSD$ + +PACKAGE= lib${LIB} +LIB= sysctl +SHLIB_MAJOR= 1 + +SHLIBDIR?= /lib +SRCS= libsysctl.c + +INCSDIR= ${INCLUDEDIR} +INCS= libsysctl.h + +MAN= libsysctl.3 + +CFLAGS+= -I${.CURDIR} + +.include Index: lib/libsysctl/libsysctl.h =================================================================== --- lib/libsysctl/libsysctl.h +++ lib/libsysctl/libsysctl.h @@ -0,0 +1,159 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 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 _LIBSYSCTL_H +#define _LIBSYSCTL_H + +#include +#include +#include + +#define LIBSYSCTL_VERSION 1 +#define LIBSYSCTL_IDMAXLEN CTL_MAXNAME -2 + + +/* + * functions to wrap 'undocumented kern_sysctl.c API', + * return: 0 for success, negative value for failure. + */ + +int libsysctl_nametoid(const char *, size_t, int *, size_t *); +int libsysctl_name(int *, size_t, char *, size_t *); +int libsysctl_desc(int *, size_t, char *, size_t *); +int libsysctl_label(int *, size_t, char *, size_t *); + +int libsysctl_info(int *, size_t, void *, size_t *); +#define LIBSYSCTL_IKIND(info) (*((unsigned int *)info)) +#define LIBSYSCTL_ITYPE(info) (*((unsigned int *)info) & CTLTYPE) +#define LIBSYSCTL_IFLAGS(info) (*((unsigned int *)info) & 0xfffffff0) +#define LIBSYSCTL_IFMT(info) ((char *)info + sizeof(unsigned int)) + +/* kernel returns only next leaf, next node requires extra computation */ +int libsysctl_nextnode(int *, size_t, int *, size_t *); +int libsysctl_nextleaf(int *, size_t, int *, size_t *); + +#define LIBSYSCTL_NAMELEN(id, idlen, size) \ + libsysctl_name(id, idlen, NULL, size) +#define LIBSYSCTL_DESCLEN(id, idlen, size) \ + libsysctl_desc(id, idlen, NULL, size) +#define LIBSYSCTL_LABELLEN(id, idlen, size) \ + libsysctl_label(id, idlen, NULL, size) + + +/* + * functions related to "struct libsysctl_object" + * params: id and idlen to identify a mib entry + * return: NULL for failure, pointer to heap for success. + */ + +/* 'struct libsysctl_object': userspace mib entry definition */ + +SLIST_HEAD(libsysctl_object_list, libsysctl_object); + +struct libsysctl_object { + int *id; + size_t idlen; + char *name; + size_t namelen; + char *desc; + size_t desclen; + char *label; + size_t labellen; + uint8_t type; + uint32_t flags; + char *fmt; + size_t fmtlen; + struct libsysctl_object_list *childs; + SLIST_ENTRY(libsysctl_object) object_link; +}; + +/* OR-flags: object fields to set */ +#define LIBSYSCTL_FNAME 0x01 +#define LIBSYSCTL_FDESC 0x02 +#define LIBSYSCTL_FINFO 0x04 +#define LIBSYSCTL_FLABEL 0x08 +#define LIBSYSCTL_FTYPE 0x10 +#define LIBSYSCTL_FFLAGS 0x20 +#define LIBSYSCTL_FFMT 0x40 +#define LIBSYSCTL_FALL \ + LIBSYSCTL_FNAME | LIBSYSCTL_FDESC | LIBSYSCTL_FINFO \ + | LIBSYSCTL_FLABEL | LIBSYSCTL_FTYPE | LIBSYSCTL_FFLAGS | LIBSYSCTL_FFMT + + +/* libsysctl_object functions */ + +struct libsysctl_object * +libsysctl_object(int *, size_t, unsigned int); + +void libsysctl_freeobject(struct libsysctl_object *); + + +/* object list functions */ + +#define LIBSYSCTL_MAXDEPTH CTL_MAXNAME - 3 + +typedef int libsysctl_filterfunc_t (struct libsysctl_object *); + +struct libsysctl_object_list * +libsysctl_filterlist(libsysctl_filterfunc_t *, unsigned int); + +#define LIBSYSCTL_LIST(flags) libsysctl_filterlist(NULL, flags) + +struct libsysctl_object_list * +libsysctl_grouplist(int *, size_t, unsigned int, unsigned int); + +void libsysctl_freelist(struct libsysctl_object_list *); + + +/* tree fuctions */ + +#define LIBSYSCTL_MAXEDGES CTL_MAXNAME - 3 + +struct libsysctl_object * +libsysctl_tree(int *, size_t, unsigned int, unsigned int); + +void libsysctl_freetree(struct libsysctl_object *); + + +/* + * Macros to get/set value, + * libsysctl is not designed to get/set 'kernel states', use sysctl(2): + * + * sysctl(obj->id, obj->idlen, oldp, oldplen, newp, newplen); + * + * anyway some useful macro is defined: + */ + +#define LIBSYSCTL_GETVALUE(oidp, oidlen, oldp, oldplen) \ + sysctl(oidp, oidlen, oldp, oldplen, NULL, 0) +#define LIBSYSCTL_SETVALUE(oidp, oidlen, newp, newplen) \ + sysctl(oidp, oidlen, NULL, 0, newp, newplen) +#define LIBSYSCTL_OLDNEWVALUE(oidp, oidlen, oldp, oldplen, newp, newplen) \ + sysctl(oidp, oidlen, oldp, oldplen, newp, newplen) + + +#endif /* _LIBLIBSYSCTL_H */ Index: lib/libsysctl/libsysctl.3 =================================================================== --- lib/libsysctl/libsysctl.3 +++ lib/libsysctl/libsysctl.3 @@ -0,0 +1,362 @@ +.\" +.\" Copyright (c) 2018 Alfonso S. 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 November 26, 2018 +.Dt LIBSYSCTL 3 +.Os +.Sh NAME +.Nm LIBSYSCTL_IDMAXLEN, +.Nm libsysctl_nametoid, +.Nm libsysctl_name, +.Nm LIBSYSCTL_NAMELEN, +.Nm libsysctl_desc, +.Nm LIBSYSCTL_DESCLEN, +.Nm libsysctl_label, +.Nm LIBSYSCTL_LABELLEN, +.Nm libsysctl_info, +.Nm LIBSYSCTL_IKIND, +.Nm LIBSYSCTL_ITYPE, +.Nm LIBSYSCTL_IFLAGS, +.Nm LIBSYSCTL_IFMT, +.Nm libsysctl_nextnode, +.Nm libsysctl_nextleaf, +.Nm libsysctl_object, +.Nm libsysctl_freeobject, +.Nm libsysctl_filterlist, +.Nm LIBSYSCTL_LIST, +.Nm libsysctl_grouplist, +.Nm libsysctl_freelist, +.Nm libsysctl_tree, +.Nm libsysctl_freetree, +.Nm LIBSYSCTL_GETVALUE, +.Nm LIBSYSCTL_SETVALUE, +.Nm LIBSYSCTL_OLDNEWVALUE, +.Nd manage sysctl-tree in userpace +.Sh LIBRARY +.Lb libsysctl +.Sh SYNOPSIS +.In sys/types.h +.In sys/queue.h +.In libsysctl.h +.Vt LIBSYSCTL_IDMAXLEN; +.Ft "int" +.Fn libsysctl_nametoid "const char *name" "size_t namelen" "int *id" "size_t* idlen" +.Ft "int" +.Fn libsysctl_name "int *id" "size_t idlen" "char *name" "size_t *namelen" +.Fn LIBSYSCTL_NAMELEN "int *id" "size_t idlen" "size_t *namelen" +.Ft "int" +.Fn libsysctl_desc "int *id" "size_t idlen" "char *desc" "size_t *desclen" +.Fn LIBSYSCTL_DESCLEN "int *id" "size_t idlen" "size_t *desclen" +.Ft "int" +.Fn libsysctl_label "int *id" "size_t idlen" "char *label" "size_t *labellen" +.Fn LIBSYSCTL_LABELLEN "int *id" "size_t idlen" "size_t *labellen" +.Ft "int" +.Fn libsysctl_info "int *id" "size_t idlen" "void *info" "size_t *infolen" +.Ft "uint32_t" +.Fn LIBSYSCTL_IKIND "info" +.Ft "uint8_t" +.Fn LIBSYSCTL_ITYPE "info" +.Ft "uint32_t" +.Fn LIBSYSCTL_IFLAGS "info" +.Ft "char *" +.Fn LIBSYSCTL_IFMT "info" +.Ft "int" +.Fn libsysctl_nextleaf "int *id" "size_t idlen" "int *nextid" "size_t *nextidlen" +.Ft "int" +.Fn libsysctl_nextnode "int *id" "size_t idlen" "int *nextid" "size_t *nextidlen" +.Ft "struct libsysctl_object *" +.Fn libsysctl_object "int *id" "size_t idlen" "unsigned int flags" +.Ft "void" +.Fn libsysctl_freeobject "struct libsysctl_object *object" +.Ft "struct libsysctl_object_list *" +.Fn libsysctl_filterlist "libsysctl_filterfunc_t *filterfunc" "unsigned int flags" +.Ft "struct libsysctl_object_list *" +.Fn LIBSYSCTL_LIST "unsigned int flags" +.Ft "struct libsysctl_object_list *" +.Fn libsysctl_grouplist "int* idstart" "size_t idstartlen" "unsigned int flags" "unsigned int max_depth" +.Ft "void" +.Fn libsysctl_freelist "struct libsysctl_object_list* list" +.Ft "struct libsysctl_object *" +.Fn libsysctl_tree "int* id" "size_t idlen" "unsigned int flags" "unsigned int max_edge" +.Ft "void" +.Fn libsysctl_freetree "struct libsysctl_object *node" +.Ft "int" +.Fn LIBSYSCTL_GETVALUE "const int* id" "u_int idlen" "void* valuep" "size_t valuepsize" +.Ft "int" +.Fn LIBSYSCTL_SETVALUE "const int* id" "u_int idlen" "const void* valuep" "size_t valuepsize" +.Ft "int" +.Fn LIBSYSCTL_OLDNEWVALUE "const int* id" "u_int idlen" "void* oldp" "size_t* oldpsize" "const void* newp" "size_t newpsize" +.Sh DESCRIPTION +The +.Nm libsysctl +is a interface to the kernel sysctl-mib-tree. It implements wrappers around undocumented +.Dq sysctl.* +kernel states to get mib-entry info and +provides a convenient API to build a mib-entry, +entries-list and mib-tree in userspace. +.Pp +LIBSYSCTL_IDMAXLEN is useful for +.Em id +and +.Em idlen +(see +.Fn libsysctl_next +below). +.Pp +.Fn libsysctl_nametoid +sets +.Em id +and +.Em idlen +of the entry with +.Em name +and +.Em namelen. +.Pp +.Fn libsysctl_name +.Fn libsysctl_desc +.Fn libsysctl_label +set +.Em namelen, desclen, labellen +and +.Em name, desc, label +like the entry with +.Em id +and +.Em idlen. +.Pp +.Pp +.Fn LIBSYSCTL_NAMELEN +.Fn LIBSYSCTL_DESCLEN +.Fn LIBSYSCTL_LABELLEN +set +.Em namelen, desclen, labellen +of the entry with +.Em id +and +.Em idlen. +.Pp +.Fn libsysctl_info +sets +.Em info +and +.Em infolen +like the entry with +.Em id +and +.Em idlen, info +has not a basic format: +3 bytes for flags, 1 byte for type and a char* for string format; flags and type are defined in +.In sys/sysctl.h) . +Macros to deal with +.Em info: +.Bd -offset indent -compact +.Fn LIBSYSCTL_IFLAGS info +returns flags in an uint32_t; +.Ed +.Bd -offset indent -compact +.Fn LIBSYSCTL_ITYPE info +returns a unit8_t type; +.Ed +.Bd -offset indent -compact +.Fn LIBSYSCTL_IKIND info +returns an uint32_t with flags plus type; +.Ed +.Bd -offset indent -compact +.Fn LIBSYSCTL_IFMT info +returns a char* format_string. +.Ed +.Pp +.Fn libsysctl_nextleaf +sets +.Em nextid +and +.Em nextidlen +like the next-leaf-entry of the entry with +.Em id +and +.Em idlen. +.Fn libsysctl_nextnode +sets +.Em nextid +and +.Em nextidlen +like the next-[node|leaf]-entry of the entry with +.Em id +and +.Em idlen. +Notes: +.Em nextid +should have size LIBSYSCTL_IDMAXLEN and before every call to libsysctl_next*() +.Em idlen +should be set to LIBSYSCTL_IDMAXLEN. +.Pp +.Fn libsysctl_object +returns in the heap a +.Em struct libsysctl_object +(setting +.Em flags +fields) of the entry with +.Em id +and +.Em idlen. +A mib userspace object/entry is defined: +.Pp +.Bd -literal -offset indent -compact +/* 'struct libsysctl_object': userspace mib entry definition */ + +SLIST_HEAD(libsysctl_object_list, libsysctl_object); + +struct libsysctl_object { + int *id; + size_t idlen; + char *name; + size_t namelen; + char *desc; + size_t desclen; + char *label; + size_t labellen; + uint8_t type; + uint32_t flags; + char *fmt; + size_t fmtlen; + struct libsysctl_object_list *childs; + SLIST_ENTRY(libsysctl_object) object_link; +}; + +/* OR-flags: object fields to set */ +#define LIBSYSCTL_FNAME 0x01 +#define LIBSYSCTL_FDESC 0x02 +#define LIBSYSCTL_FINFO 0x04 +#define LIBSYSCTL_FLABEL 0x08 +#define LIBSYSCTL_FTYPE 0x10 +#define LIBSYSCTL_FFLAGS 0x20 +#define LIBSYSCTL_FFMT 0x40 +#define LIBSYSCTL_FALL LIBSYSCTL_FNAME | LIBSYSCTL_FDESC | + LIBSYSCTL_FINFO | LIBSYSCTL_FLABEL | + LIBSYSCTL_FTYPE | LIBSYSCTL_FFLAGS | + LIBSYSCTL_FFMT +.Ed +.Pp +.Fn libsysctl_filterlist +returns a SLIST of libsysctl_object (with +.Em flags +), an object o is added when +.Bd -offset indent -compact +int libsysctl_filterfunc_t (struct libsysctl_object *o) +.Ed +returns 0; note: object.childs is not set. +.Pp +.Fn LIBSYSCTL_LIST +is an alias for +.Fn libsysctl_filterlist NULL flags +.Pp +.Fn libsysctl_grouplist +returns a SLIST of libsysctl_object (with +.Em flags +) that represents a +.Dq linear +tree of +.Em max_depth +in heap memory, the root is the entry with +.Em id +and +.Em idlen; +note: object.childs is not set. +.Pp +.Fn libsysctl_tree +returns a tree with +.Em max_edge +of libsysctl_object node (with +.Em flags +) in heap memory, +the root is the entry with +.Em id +and +.Em idlen; +note: object.childs is set and iterable by SLIST macros. +.Pp +.Fn libsysctl_freeobject +.Fn libsysctl_freelist +.Fn libsysctl_freetree +to free heap memory +.Pp +.Nm libsysctl +is not designed to get and set kernel states (use +.Xr sysctl 3 +), +anyway useful macros are defined: +.Fn LIBSYSCTL_GETVALUE +.Fn LIBSYSCTL_SETVALUE +.Fn LIBSYSCTL_OLDNEWVALUE +.Sh IMPLEMENTATION NOTES +.Nm libsysctl +calls +.Fn sysctl +syscall for wrapping 0.[1-6] entries defined in kern_sysctl.c. +Kernel returns only next leaf, +.Fn libsysctl_nextnode +requires extra computation. +.Sh RETURN VALUES +.Rv -std libsysctl_nametoid libsysctl_name libsysctl_nametoid libsysctl_name libsysctl_desc libsysctl_label libsysctl_info libsysctl_nextnode libsysctl_nextleaf LIBSYSCTL_GETVALUE LIBSYSCTL_SETVALUE LIBSYSCTL_OLDNEWVALUE +.Pp +The +.Fn libsysctl_object , +.Fn libsysctl_filterlist , +.Fn LIBSYSCTL_LIST , +.Fn libsysctl_grouplist , +.Fn libsysctl_tree +return +.Dv NULL +upon error and a pointer to heap for success. +.Sh EXAMPLES +Complete set of examples: +.Dl https://gitlab.com/alfix/libsysctl/tree/master/examples +.\" .Sh ERRORS +.\" For sections 2, 3, 4, and 9 errno settings only. +.Sh SEE ALSO +.Xr sysctl 3 +.Xr queue 3 +.Xr nsysctl 8 +.Xr sysctlview 1 +.Sh HISTORY +The +.Nm libsysctl +library first appeared in +.Fx 13.0 . +.Sh AUTHORS +.Nm libsysctl +was written by +.An Alfonso S. Siciliano Aq Mt alf.siciliano@gmail.com +.\" .Sh CAVEATS +.Sh BUGS +Inherited problems from kern_sysctl.c: +.Bd -offset indent -compact +libsysctl_name(): false positive. +.Ed +.Bd -offset indent -compact +libsysctl_desc(): entries without desc could return +.Dq +or NULL. +.Ed Index: lib/libsysctl/libsysctl.c =================================================================== --- lib/libsysctl/libsysctl.c +++ lib/libsysctl/libsysctl.c @@ -0,0 +1,501 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 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 +#include +#include + +#include +#include +#include + +#include "libsysctl.h" + +/* + * sys/sysctl.h 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 +libsysctl_internal_subtree(struct libsysctl_object *obj, unsigned int flags, + unsigned int num_edge) +{ + struct libsysctl_object *child; + + if ((obj->childs = + libsysctl_grouplist(obj->id, obj->idlen, flags, 1)) == NULL) { + return (-1); + } + + SLIST_REMOVE_HEAD(obj->childs, object_link); + + if (num_edge > 0) { + SLIST_FOREACH(child, obj->childs, object_link) + libsysctl_internal_subtree(child, flags, num_edge - 1); + } + + return (0); +} + + +/* API implementation */ + +int +libsysctl_nametoid(const char *name, size_t namelen, int *id, size_t *idlen) +{ + int mib[2]; + int error = 0; + + mib[0] = CTL_SYSCTLMIB; + mib[1] = MIB_NAME2OID; + + *idlen *= sizeof(int); + error = sysctl(mib, 2, id, idlen, (const void *)name, namelen); + *idlen /= sizeof(int); + + return (error); +} + + +int +libsysctl_desc(int *id, size_t idlen, char *desc, size_t *desclen) +{ + int mib[CTL_MAXNAME]; + int error = 0; + + mib[0] = CTL_SYSCTLMIB; + mib[1] = MIB_OBJECTDESCR; + memcpy(mib + 2, id, idlen * sizeof(int)); + + error = sysctl(mib, idlen+2, (void *)desc, desclen, NULL, 0); + + return (error); +} + + +int +libsysctl_name(int *id, size_t idlen, char *name, size_t *namelen) +{ + int mib[CTL_MAXNAME]; + int error = 0; + + mib[0] = CTL_SYSCTLMIB; + mib[1] = MIB_OBJECTNAME; + memcpy(mib + 2, id, idlen * sizeof(int)); + + error = sysctl(mib, idlen+2, (void *)name, namelen, NULL, 0); + + return (error); +} + + +int +libsysctl_label(int *id, size_t idlen, char *label, size_t *labellen) +{ + int mib[CTL_MAXNAME]; + int error = 0; + + mib[0] = CTL_SYSCTLMIB; + mib[1] = MIB_OBJECTLABEL; + memcpy(mib + 2, id, idlen * sizeof(int)); + + error = sysctl(mib, idlen+2, (void *)label, labellen, NULL, 0); + + return (error); +} + + +int +libsysctl_info(int *id, size_t idlen, void *info, size_t *infosize) +{ + int mib[CTL_MAXNAME]; + int error = 0; + + mib[0] = CTL_SYSCTLMIB; + mib[1] = MIB_OBJECTFMT; + memcpy(mib + 2, id, idlen*sizeof(int)); + + error = sysctl(mib, idlen+2, info, infosize, NULL, 0); + + return (error); +} + + +int +libsysctl_nextleaf(int *id, size_t idlen, int *idnext, size_t *idnextlen) +{ + int mib[CTL_MAXNAME]; + int error = 0; + + mib[0] = CTL_SYSCTLMIB; + mib[1] = MIB_NEXTOID; + memcpy(mib + 2, id, idlen*sizeof(int)); + + *idnextlen = CTL_MAXNAME * sizeof(int); + error = sysctl(mib, idlen+2, idnext, idnextlen, NULL, 0); + *idnextlen /= sizeof(int); + + return (error); +} + + +int +libsysctl_nextnode(int *id, size_t idlen, int *idnext, size_t *idnextlen) +{ + int error = 0; + size_t i; + + /* + * it could be "id = idnex", so: + */ + int previd[CTL_MAXNAME]; + size_t prevlen = idlen; + + memcpy(previd, id, idlen * sizeof(int)); + + if ((error = libsysctl_nextleaf(id, idlen, idnext, idnextlen)) < 0) { + return (error); + } + + /* + * avoid: id 5.6 -> next 5.6.4.8.2 + * we want: id 5.6 -> next 5.6.4 (just 1 level) + */ + if (*idnextlen > prevlen) { + *idnextlen = prevlen + 1; + } + + size_t minlen = *idnextlen < prevlen ? *idnextlen : prevlen; + + for (i = 0; i < minlen; i++) { + if (previd[i] != idnext[i]) { + *idnextlen = i+1; + break; + } + } + + return (error); +} + + +struct libsysctl_object * +libsysctl_object(int *id, size_t idlen, unsigned int flags) +{ + struct libsysctl_object *obj = NULL; + size_t sizevalue = 0; + void *tmpinfo = NULL; + + obj = + (struct libsysctl_object *)malloc(sizeof(struct libsysctl_object)); + if (obj == NULL) { + return (NULL); + } + + /* init new object */ + obj->namelen = 0; + obj->name = NULL; + obj->desclen = 0; + obj->desc = NULL; + obj->labellen = 0; + obj->label = NULL; + obj->type = 0; + obj->flags = 0; + obj->fmtlen = 0; + obj->fmt = NULL; + + bzero(obj, sizeof(struct libsysctl_object)); + + /* id and idlen are always set */ + obj->id = malloc(idlen * sizeof(int)); + memcpy(obj->id, id, idlen * sizeof(int)); + obj->idlen = idlen; + + if (flags & LIBSYSCTL_FNAME) { + /* kernel returns false positive */ + if (LIBSYSCTL_NAMELEN(id, idlen, &sizevalue) == 0) { + obj->namelen = sizevalue; + if ((obj->name = malloc(sizevalue)) == NULL) { + return (NULL); + } + bzero(obj->name, sizevalue); + if (libsysctl_name(id, idlen, obj->name, + &obj->namelen) != 0) { + obj->name = NULL; + } + } + } + + + if (flags & LIBSYSCTL_FDESC) { + sizevalue = 0; + /* entry without descr could return "\0" or NULL */ + if (LIBSYSCTL_DESCLEN(id, idlen, &sizevalue) == 0) { + obj->desclen = sizevalue; + if ((obj->desc = malloc(sizevalue)) == NULL) { + return (NULL); + } + bzero(obj->desc, sizevalue); + if (libsysctl_desc(id, idlen, obj->desc, + &obj->desclen) != 0) { + obj->desc = NULL; + } + } + } + + + if (flags & LIBSYSCTL_FLABEL) { + sizevalue = 0; + if (LIBSYSCTL_LABELLEN(id, idlen, &sizevalue) == 0) { + obj->labellen = sizevalue; + if ((obj->label = malloc(sizevalue)) == NULL) { + return (NULL); + } + bzero(obj->label, sizevalue); + if (libsysctl_label(id, idlen, obj->label, + &obj->labellen) != 0) { + obj->label = NULL; + } + } + } + + + if ((flags & LIBSYSCTL_FFLAGS) || (flags & LIBSYSCTL_FFMT) || + (flags & LIBSYSCTL_FTYPE)) { + sizevalue = 0; + /* get info size (because fmt is variable) */ + if (libsysctl_info(id, idlen, NULL, &sizevalue) == 0) { + tmpinfo = malloc(sizevalue); + + if (libsysctl_info(id, idlen, tmpinfo, + &sizevalue) < 0) { + free(tmpinfo); + return (NULL); + } + + if (flags & LIBSYSCTL_FFMT) { + obj->fmtlen = sizevalue - sizeof(uint32_t); + obj->fmt = strndup(LIBSYSCTL_IFMT( + tmpinfo), obj->fmtlen); + } + + if (flags & LIBSYSCTL_FFLAGS) { + obj->flags = LIBSYSCTL_IFLAGS(tmpinfo); + } + + if (flags & LIBSYSCTL_FTYPE) { + obj->type = LIBSYSCTL_ITYPE(tmpinfo); + } + + free(tmpinfo); + } + } + + return (obj); +} + + +void +libsysctl_freeobject(struct libsysctl_object *object) +{ + if (object == NULL) { + return; + } + + free(object->id); + free(object->name); + free(object->desc); + free(object->label); + free(object); + object = NULL; +} + + +struct libsysctl_object_list * +libsysctl_filterlist(libsysctl_filterfunc_t *filterfunc, unsigned int flags) +{ + int id[CTL_MAXNAME]; + size_t idlen; + struct libsysctl_object_list *list = NULL; + struct libsysctl_object *last, *new; + + list = malloc(sizeof(struct libsysctl_object_list)); + SLIST_INIT(list); + + id[0] = 0; + idlen = 1; + + for (;;) { + if ((new = libsysctl_object(id, idlen, flags)) == NULL) { + 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; + } + + if (libsysctl_nextnode(id, idlen, id, &idlen) < 0) { + break; + } + } + + return (list); +} + + +struct libsysctl_object_list * +libsysctl_grouplist(int *idstart, size_t idstartlen, unsigned int flags, + unsigned int depth) +{ + int id[CTL_MAXNAME]; + size_t idlen; + struct libsysctl_object_list *list = NULL; + struct libsysctl_object *last, *new; + size_t i; + + list = malloc(sizeof(struct libsysctl_object_list)); + SLIST_INIT(list); + + memcpy(id, idstart, idstartlen * sizeof(int)); + idlen = idstartlen; + + if ((new = libsysctl_object(id, idlen, flags)) == NULL) { + return (NULL); + } + + SLIST_INSERT_HEAD(list, new, object_link); + + last = new; + + + for (;;) { + if (libsysctl_nextnode(id, idlen, id, &idlen) < 0) { + break; + } + + if (idlen - idstartlen > depth) { + continue; + } + + if (idlen < idstartlen) { + break; + } + + for (i = 0; i < idstartlen; i++) { + if (id[i] != idstart[i]) { + return (list); + } + } + + new = libsysctl_object(id, idlen, flags); + SLIST_INSERT_AFTER(last, new, object_link); + last = new; + } + + return (list); +} + + +void +libsysctl_freelist(struct libsysctl_object_list *list) +{ + if (list == NULL) { + return; + } + + struct libsysctl_object *obj; + + while (!SLIST_EMPTY(list)) { + obj = SLIST_FIRST(list); + SLIST_REMOVE_HEAD(list, object_link); + libsysctl_freeobject(obj); + } + + free(list); + list = NULL; +} + + +struct libsysctl_object * +libsysctl_tree(int *id, size_t idlen, unsigned int flags, unsigned int num_edge) +{ + struct libsysctl_object *obj = NULL; + + if ((obj = libsysctl_object(id, idlen, flags)) == NULL) { + return (NULL); + } + + if (num_edge < 1) { + return (obj); + } + + libsysctl_internal_subtree(obj, flags, num_edge - 1); + + return (obj); +} + + +/* postorder visit */ +void +libsysctl_freetree(struct libsysctl_object *node) +{ + if (node == NULL) { + return; + } + + struct libsysctl_object *child; + + if (node->childs != NULL) { + while (!SLIST_EMPTY(node->childs)) { + child = SLIST_FIRST(node->childs); + SLIST_REMOVE_HEAD(node->childs, object_link); + libsysctl_freetree(child); + } + } + + + libsysctl_freeobject(node); + node = NULL; +}