Changeset View
Changeset View
Standalone View
Standalone View
lib/libutil/libsysctl.c
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/*- | |||||
* 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 <sys/types.h> | |||||
#include <sys/queue.h> | |||||
#include <sys/sysctl.h> | |||||
#include <libsysctl.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.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 depth) | |||||
{ | |||||
int error = 0; | |||||
struct libsysctl_object *child; | |||||
if ((obj->children = | |||||
libsysctl_grouplist(obj->id, obj->idlevel, flags, 1)) == NULL) { | |||||
return (1); | |||||
} | |||||
SLIST_REMOVE_HEAD(obj->children, object_link); | |||||
if (depth > 0) { | |||||
SLIST_FOREACH(child, obj->children, object_link) | |||||
{ | |||||
error = libsysctl_internal_subtree(child, flags, | |||||
depth - 1); | |||||
if (error != 0) { | |||||
return (error); | |||||
} | |||||
} | |||||
} | |||||
return (error); | |||||
} | |||||
/* API implementation */ | |||||
int | |||||
libsysctl_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 | |||||
libsysctl_desc(int *id, size_t idlevel, char *desc, size_t *desclen) | |||||
{ | |||||
int mib[CTL_MAXNAME]; | |||||
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 | |||||
libsysctl_name(int *id, size_t idlevel, char *name, size_t *namelen) | |||||
{ | |||||
int mib[CTL_MAXNAME]; | |||||
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 | |||||
libsysctl_label(int *id, size_t idlevel, char *label, size_t *labellen) | |||||
{ | |||||
int mib[CTL_MAXNAME]; | |||||
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 | |||||
libsysctl_info(int *id, size_t idlevel, void *info, size_t *infosize) | |||||
{ | |||||
int mib[CTL_MAXNAME]; | |||||
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 | |||||
libsysctl_nextleaf(int *id, size_t idlevel, int *idnext, size_t *idnextlevel) | |||||
{ | |||||
int mib[CTL_MAXNAME]; | |||||
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 | |||||
libsysctl_nextnode(int *id, size_t idlevel, int *idnext, size_t *idnextlevel) | |||||
{ | |||||
int error = 0; | |||||
size_t i, minlen; | |||||
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 = libsysctl_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; | |||||
} | |||||
minlen = *idnextlevel < prevlevel ? *idnextlevel : prevlevel; | |||||
for (i = 0; i < minlen; i++) { | |||||
if (previd[i] != idnext[i]) { | |||||
*idnextlevel = i+1; | |||||
break; | |||||
} | |||||
} | |||||
return (error); | |||||
} | |||||
struct libsysctl_object * | |||||
libsysctl_object(int *id, size_t idlevel, unsigned int flags) | |||||
{ | |||||
struct libsysctl_object *obj = NULL; | |||||
size_t size = 0; | |||||
void *tmpinfo = NULL; | |||||
obj = malloc(sizeof(struct libsysctl_object)); | |||||
if (obj == NULL) { | |||||
return (NULL); | |||||
} | |||||
/* init new object */ | |||||
memset(obj, 0, sizeof(struct libsysctl_object)); | |||||
/* id and idlevel are always set */ | |||||
obj->id = malloc(idlevel * sizeof(int)); | |||||
if (obj->id == NULL) { | |||||
libsysctl_freeobject(obj); | |||||
return (NULL); | |||||
} | |||||
memcpy(obj->id, id, idlevel * sizeof(int)); | |||||
obj->idlevel = idlevel; | |||||
if (flags & LIBSYSCTL_FNAME) { | |||||
/* kernel returns false positive */ | |||||
if (LIBSYSCTL_NAMELEN(id, idlevel, &size) == 0) { | |||||
if ((obj->name = malloc(size)) == NULL) { | |||||
libsysctl_freeobject(obj); | |||||
return (NULL); | |||||
} | |||||
bzero(obj->name, size); | |||||
if (libsysctl_name(id, idlevel, obj->name, | |||||
&size) != 0) { | |||||
obj->name = NULL; | |||||
} | |||||
} | |||||
} | |||||
if (flags & LIBSYSCTL_FDESC) { | |||||
size = 0; | |||||
/* entry without descr could return "\0" or NULL */ | |||||
if (LIBSYSCTL_DESCLEN(id, idlevel, &size) == 0) { | |||||
if ((obj->desc = malloc(size)) == NULL) { | |||||
libsysctl_freeobject(obj); | |||||
return (NULL); | |||||
} | |||||
bzero(obj->desc, size); | |||||
if (libsysctl_desc(id, idlevel, obj->desc, | |||||
&size) != 0) { | |||||
obj->desc = NULL; | |||||
} | |||||
} | |||||
} | |||||
if (flags & LIBSYSCTL_FLABEL) { | |||||
size = 0; | |||||
if (LIBSYSCTL_LABELLEN(id, idlevel, &size) == 0) { | |||||
if ((obj->label = malloc(size)) == NULL) { | |||||
libsysctl_freeobject(obj); | |||||
return (NULL); | |||||
} | |||||
bzero(obj->label, size); | |||||
if (libsysctl_label(id, idlevel, obj->label, | |||||
&size) != 0) { | |||||
obj->label = NULL; | |||||
} | |||||
} | |||||
} | |||||
if ((flags & LIBSYSCTL_FFLAGS) || (flags & LIBSYSCTL_FFMT) || | |||||
(flags & LIBSYSCTL_FTYPE)) { | |||||
size = 0; | |||||
/* get info size because fmt is variable */ | |||||
if (libsysctl_info(id, idlevel, NULL, &size) == 0) { | |||||
tmpinfo = malloc(size); | |||||
if (tmpinfo == NULL) { | |||||
libsysctl_freeobject(obj); | |||||
return (NULL); | |||||
} | |||||
if (libsysctl_info(id, idlevel, tmpinfo, &size) < 0) { | |||||
libsysctl_freeobject(obj); | |||||
free(tmpinfo); | |||||
return (NULL); | |||||
} | |||||
if (flags & LIBSYSCTL_FFMT) { | |||||
obj->fmt = strndup(LIBSYSCTL_INFOFMT(tmpinfo), | |||||
size - sizeof(uint32_t)); | |||||
if (obj->fmt == NULL) { | |||||
libsysctl_freeobject(obj); | |||||
return (NULL); | |||||
} | |||||
} | |||||
if (flags & LIBSYSCTL_FFLAGS) { | |||||
obj->flags = LIBSYSCTL_INFOFLAGS(tmpinfo); | |||||
} | |||||
if (flags & LIBSYSCTL_FTYPE) { | |||||
obj->type = LIBSYSCTL_INFOTYPE(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], idnext[CTL_MAXNAME]; | |||||
size_t idlevel, idnextlevel; | |||||
struct libsysctl_object_list *list = NULL; | |||||
struct libsysctl_object *last, *new; | |||||
list = malloc(sizeof(struct libsysctl_object_list)); | |||||
if (list == NULL) { | |||||
return (NULL); | |||||
} | |||||
SLIST_INIT(list); | |||||
id[0] = 0; | |||||
idlevel = 1; | |||||
for (;;) { | |||||
if ((new = libsysctl_object(id, idlevel, flags)) == NULL) { | |||||
libsysctl_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 (libsysctl_nextnode(id, idlevel, idnext, &idnextlevel) < 0) { | |||||
break; | |||||
} | |||||
memcpy(id, idnext, idnextlevel * sizeof(int)); | |||||
idlevel = idnextlevel; | |||||
} | |||||
return (list); | |||||
} | |||||
struct libsysctl_object_list * | |||||
libsysctl_grouplist(int *idstart, size_t idstartlen, unsigned int flags, | |||||
unsigned int depth) | |||||
{ | |||||
int id[CTL_MAXNAME], idnext[CTL_MAXNAME]; | |||||
size_t idlevel, idnextlevel; | |||||
struct libsysctl_object_list *list = NULL; | |||||
struct libsysctl_object *last, *new; | |||||
size_t i; | |||||
list = malloc(sizeof(struct libsysctl_object_list)); | |||||
if (list == NULL) { | |||||
return (NULL); | |||||
} | |||||
SLIST_INIT(list); | |||||
memcpy(id, idstart, idstartlen * sizeof(int)); | |||||
idlevel = idstartlen; | |||||
if ((new = libsysctl_object(id, idlevel, flags)) == NULL) { | |||||
free(list); | |||||
return (NULL); | |||||
} | |||||
SLIST_INSERT_HEAD(list, new, object_link); | |||||
last = new; | |||||
for (;;) { | |||||
idnextlevel = CTL_MAXNAME; | |||||
if (libsysctl_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 = libsysctl_object(id, idlevel, flags); | |||||
if (new == NULL) { | |||||
libsysctl_freelist(list); | |||||
return (NULL); | |||||
} | |||||
SLIST_INSERT_AFTER(last, new, object_link); | |||||
last = new; | |||||
} | |||||
return (list); | |||||
} | |||||
void | |||||
libsysctl_freelist(struct libsysctl_object_list *list) | |||||
{ | |||||
struct libsysctl_object *obj; | |||||
if (list == NULL) { | |||||
return; | |||||
} | |||||
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 idlevel, unsigned int flags, | |||||
unsigned int max_depth) | |||||
{ | |||||
int error; | |||||
struct libsysctl_object *root = NULL; | |||||
if ((root = libsysctl_object(id, idlevel, flags)) == NULL) { | |||||
return (NULL); | |||||
} | |||||
if (max_depth < 1) { | |||||
return (root); | |||||
} | |||||
error = libsysctl_internal_subtree(root, flags, max_depth - 1); | |||||
if (error != 0) { | |||||
libsysctl_freetree(root); | |||||
} | |||||
return (root); | |||||
} | |||||
/* postorder visit */ | |||||
void | |||||
libsysctl_freetree(struct libsysctl_object *node) | |||||
{ | |||||
struct libsysctl_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); | |||||
libsysctl_freetree(child); | |||||
} | |||||
} | |||||
libsysctl_freeobject(node); | |||||
node = NULL; | |||||
} |