version 1.1, 2020/07/06 07:09:50 |
version 1.2, 2020/08/10 01:13:28 |
|
|
#include <stddef.h> |
#include <stddef.h> |
#include <string.h> |
#include <string.h> |
#include <inttypes.h> |
#include <inttypes.h> |
|
#include <fnmatch.h> |
#include <fcntl.h> |
#include <fcntl.h> |
#include <errno.h> |
#include <errno.h> |
#include <err.h> |
#include <err.h> |
|
|
#include <sys/tree.h> |
#include <sys/tree.h> |
#include <sys/ioctl.h> |
#include <sys/ioctl.h> |
#include <sys/time.h> |
#include <sys/time.h> |
|
#include <sys/queue.h> |
|
|
#include <sys/kstat.h> |
#include <sys/kstat.h> |
|
|
#ifndef roundup |
#ifndef roundup |
#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) |
#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) |
#endif |
#endif |
|
|
|
#ifndef nitems |
|
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) |
|
#endif |
|
|
|
#ifndef ISSET |
|
#define ISSET(_i, _m) ((_i) & (_m)) |
|
#endif |
|
|
|
#ifndef SET |
|
#define SET(_i, _m) ((_i) |= (_m)) |
|
#endif |
|
|
|
#define str_is_empty(_str) (*(_str) == '\0') |
|
|
#define DEV_KSTAT "/dev/kstat" |
#define DEV_KSTAT "/dev/kstat" |
|
|
static void kstat_list(int, unsigned int); |
struct kstat_filter { |
|
TAILQ_ENTRY(kstat_filter) kf_entry; |
|
const char *kf_provider; |
|
const char *kf_name; |
|
unsigned int kf_flags; |
|
#define KSTAT_FILTER_F_INST (1 << 0) |
|
#define KSTAT_FILTER_F_UNIT (1 << 1) |
|
unsigned int kf_instance; |
|
unsigned int kf_unit; |
|
}; |
|
|
#if 0 |
TAILQ_HEAD(kstat_filters, kstat_filter); |
|
|
|
static struct kstat_filter * |
|
kstat_filter_parse(char *); |
|
static int kstat_filter_entry(struct kstat_filters *, |
|
const struct kstat_req *); |
|
|
|
static void kstat_list(int, unsigned int, struct kstat_filters *); |
|
|
__dead static void |
__dead static void |
usage(void) |
usage(void) |
{ |
{ |
extern char *__progname; |
extern char *__progname; |
fprintf(stderr, "usage: %s\n", __progname); |
|
|
fprintf(stderr, "usage: %s [name|provider:0:name:unit ...]\n", |
|
__progname); |
|
|
exit(1); |
exit(1); |
} |
} |
#endif |
|
|
|
int |
int |
main(int argc, char *argv[]) |
main(int argc, char *argv[]) |
{ |
{ |
|
struct kstat_filters kfs = TAILQ_HEAD_INITIALIZER(kfs); |
unsigned int version; |
unsigned int version; |
int fd; |
int fd; |
|
int i; |
|
|
|
for (i = 1; i < argc; i++) { |
|
struct kstat_filter *kf = kstat_filter_parse(argv[i]); |
|
TAILQ_INSERT_TAIL(&kfs, kf, kf_entry); |
|
} |
|
|
fd = open(DEV_KSTAT, O_RDONLY); |
fd = open(DEV_KSTAT, O_RDONLY); |
if (fd == -1) |
if (fd == -1) |
err(1, "%s", DEV_KSTAT); |
err(1, "%s", DEV_KSTAT); |
|
|
if (ioctl(fd, KSTATIOC_VERSION, &version) == -1) |
if (ioctl(fd, KSTATIOC_VERSION, &version) == -1) |
err(1, "kstat version"); |
err(1, "kstat version"); |
|
|
kstat_list(fd, version); |
kstat_list(fd, version, &kfs); |
|
|
return (0); |
return (0); |
} |
} |
|
|
|
static struct kstat_filter * |
|
kstat_filter_parse(char *arg) |
|
{ |
|
struct kstat_filter *kf; |
|
const char *errstr; |
|
char *argv[4]; |
|
size_t argc; |
|
|
|
for (argc = 0; argc < nitems(argv); argc++) { |
|
char *s = strsep(&arg, ":"); |
|
if (s == NULL) |
|
break; |
|
|
|
argv[argc] = s; |
|
} |
|
if (arg != NULL) |
|
usage(); |
|
|
|
kf = malloc(sizeof(*kf)); |
|
if (kf == NULL) |
|
err(1, NULL); |
|
|
|
memset(kf, 0, sizeof(*kf)); |
|
|
|
switch (argc) { |
|
case 1: |
|
if (str_is_empty(argv[0])) |
|
errx(1, "empty name"); |
|
|
|
kf->kf_name = argv[0]; |
|
break; |
|
case 4: |
|
if (!str_is_empty(argv[0])) |
|
kf->kf_provider = argv[0]; |
|
if (!str_is_empty(argv[1])) { |
|
kf->kf_instance = |
|
strtonum(argv[1], 0, 0xffffffffU, &errstr); |
|
if (errstr != NULL) { |
|
errx(1, "%s:%s:%s:%s: instance %s: %s", |
|
argv[0], argv[1], argv[2], argv[3], |
|
argv[1], errstr); |
|
} |
|
SET(kf->kf_flags, KSTAT_FILTER_F_INST); |
|
} |
|
if (!str_is_empty(argv[2])) |
|
kf->kf_name = argv[2]; |
|
if (!str_is_empty(argv[3])) { |
|
kf->kf_unit = |
|
strtonum(argv[3], 0, 0xffffffffU, &errstr); |
|
if (errstr != NULL) { |
|
errx(1, "%s:%s:%s:%s: instance %s: %s", |
|
argv[0], argv[1], argv[2], argv[3], |
|
argv[1], errstr); |
|
} |
|
SET(kf->kf_flags, KSTAT_FILTER_F_INST); |
|
} |
|
break; |
|
default: |
|
usage(); |
|
} |
|
|
|
return (kf); |
|
} |
|
|
|
static int |
|
kstat_filter_entry(struct kstat_filters *kfs, const struct kstat_req *ksreq) |
|
{ |
|
struct kstat_filter *kf; |
|
|
|
if (TAILQ_EMPTY(kfs)) |
|
return (1); |
|
|
|
TAILQ_FOREACH(kf, kfs, kf_entry) { |
|
if (kf->kf_provider != NULL) { |
|
if (fnmatch(kf->kf_provider, ksreq->ks_provider, |
|
FNM_NOESCAPE | FNM_LEADING_DIR) == FNM_NOMATCH) |
|
continue; |
|
} |
|
if (ISSET(kf->kf_flags, KSTAT_FILTER_F_INST)) { |
|
if (kf->kf_instance != ksreq->ks_instance) |
|
continue; |
|
} |
|
if (kf->kf_name != NULL) { |
|
if (fnmatch(kf->kf_name, ksreq->ks_name, |
|
FNM_NOESCAPE | FNM_LEADING_DIR) == FNM_NOMATCH) |
|
continue; |
|
} |
|
if (ISSET(kf->kf_flags, KSTAT_FILTER_F_UNIT)) { |
|
if (kf->kf_unit != ksreq->ks_unit) |
|
continue; |
|
} |
|
|
|
return (1); |
|
} |
|
|
|
return (0); |
|
} |
|
|
struct kstat_entry { |
struct kstat_entry { |
struct kstat_req kstat; |
struct kstat_req kstat; |
RBT_ENTRY(kstat_entry) entry; |
RBT_ENTRY(kstat_entry) entry; |
|
|
} |
} |
|
|
static void |
static void |
kstat_list(int fd, unsigned int version) |
kstat_list(int fd, unsigned int version, struct kstat_filters *kfs) |
{ |
{ |
struct kstat_entry *kse; |
struct kstat_entry *kse; |
struct kstat_req *ksreq; |
struct kstat_req *ksreq; |
|
|
} |
} |
|
|
kse->serrno = errno; |
kse->serrno = errno; |
goto next; |
} else |
|
id = ksreq->ks_id; |
|
|
|
if (!kstat_filter_entry(kfs, ksreq)) { |
|
free(ksreq->ks_data); |
|
free(kse); |
|
continue; |
} |
} |
|
|
|
if (RBT_INSERT(kstat_tree, &kstat_tree, kse) != NULL) |
|
errx(1, "duplicate kstat entry"); |
|
|
|
if (kse->serrno != 0) |
|
continue; |
|
|
while (ksreq->ks_datalen > len) { |
while (ksreq->ks_datalen > len) { |
len = ksreq->ks_datalen; |
len = ksreq->ks_datalen; |
ksreq->ks_data = realloc(ksreq->ks_data, len); |
ksreq->ks_data = realloc(ksreq->ks_data, len); |
|
|
err(1, "data resize (%zu)", len); |
err(1, "data resize (%zu)", len); |
|
|
if (ioctl(fd, KSTATIOC_FIND_ID, ksreq) == -1) |
if (ioctl(fd, KSTATIOC_FIND_ID, ksreq) == -1) |
err(1, "find id %llu", id); |
err(1, "find id %llu", ksreq->ks_id); |
} |
} |
|
|
next: |
|
if (RBT_INSERT(kstat_tree, &kstat_tree, kse) != NULL) |
|
errx(1, "duplicate kstat entry"); |
|
|
|
id = ksreq->ks_id; |
|
} |
} |
|
|
RBT_FOREACH(kse, kstat_tree, &kstat_tree) { |
RBT_FOREACH(kse, kstat_tree, &kstat_tree) { |