Changeset View
Standalone View
lib/libsysctl/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 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/queue.h> | |||||
#include <sys/types.h> | |||||
#include <sys/sysctl.h> | |||||
#include <stdlib.h> | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#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); | |||||
brooks: Casting to void* before passing as a void* argument seems unnecessary. | |||||
asicilianoAuthorUnsubmitted Done Inline Actionsfixed, delete: Casting to void* before passing as a void* argument asiciliano: fixed, delete: Casting to void* before passing as a void* argument | |||||
*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); | |||||
brooksUnsubmitted Not Done Inline ActionsThis feels like it should be done in a temporary with *idnextlen only updated on success (or perhaps unconditionally zeroed). brooks: This feels like it should be done in a temporary with *idnextlen only updated on success (or… | |||||
asicilianoAuthorUnsubmitted Done Inline Actionsfixed: done in a temporary, *idnextlen only updated on success. asiciliano: fixed: done in a temporary, *idnextlen only updated on success. | |||||
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)); | |||||
brooksUnsubmitted Not Done Inline ActionsNo need to cast malloc(). brooks: No need to cast malloc(). | |||||
asicilianoAuthorUnsubmitted Done Inline Actionsfixed, delete: cast malloc() asiciliano: fixed, delete: cast malloc() | |||||
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; | |||||
brooksUnsubmitted Not Done Inline ActionsThese are redundant to the bzero. brooks: These are redundant to the bzero. | |||||
asicilianoAuthorUnsubmitted Done Inline Actionsfixed, delete: redundant to the bzero. asiciliano: fixed, delete: redundant to the bzero. | |||||
bzero(obj, sizeof(struct libsysctl_object)); | |||||
brooksUnsubmitted Not Done Inline ActionsUsing memset() would allow the compiler to elide much of the zeroing. brooks: Using memset() would allow the compiler to elide much of the zeroing. | |||||
asicilianoAuthorUnsubmitted Done Inline Actionsfixed, change: bzero() -> memset() asiciliano: fixed, change: bzero() -> memset() | |||||
/* 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); | |||||
brooksUnsubmitted Not Done Inline ActionsThis leaks memory as do all the other malloc failure paths in this function. brooks: This leaks memory as do all the other malloc failure paths in this function. | |||||
asicilianoAuthorUnsubmitted Done Inline Actionsfixed, add: libsysctl_freeobject(obj) after each malloc failure asiciliano: fixed, add: libsysctl_freeobject(obj) after each malloc failure | |||||
} | |||||
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); | |||||
brooksUnsubmitted Not Done Inline Actionslooks like too much indentation for style(9) (but could be phabricator). brooks: looks like too much indentation for style(9) (but could be phabricator). | |||||
asicilianoAuthorUnsubmitted Done Inline Actionsfixed: used 'uncrustify' to check style(9) asiciliano: fixed: used 'uncrustify' to check style(9) | |||||
} | |||||
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; | |||||
brooksUnsubmitted Not Done Inline ActionsDeclaration should be at the top of the function. brooks: Declaration should be at the top of the function. | |||||
asicilianoAuthorUnsubmitted Done Inline Actionsfixed: Declarations are at the top of the functions. asiciliano: fixed: Declarations are at the top of the functions. | |||||
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; | |||||
} |
Casting to void* before passing as a void* argument seems unnecessary.