From c915b3168ccc04a13409f0a22e6d3b2f10efe75e Mon Sep 17 00:00:00 2001 From: atatat Date: Thu, 4 Dec 2003 19:49:39 +0000 Subject: [PATCH] New sysctl(8) binary. Performs auto-discovery and can add/remove nodes from the tree. Never needs to be recompiled again. --- sbin/sysctl/Makefile | 5 +- sbin/sysctl/sysctl.c | 2886 +++++++++++++++++++++++--------------- sbin/sysctl/sysctlutil.c | 518 +++++++ 3 files changed, 2283 insertions(+), 1126 deletions(-) create mode 100644 sbin/sysctl/sysctlutil.c diff --git a/sbin/sysctl/Makefile b/sbin/sysctl/Makefile index 6ff4e825b08c..85074e1ed90f 100644 --- a/sbin/sysctl/Makefile +++ b/sbin/sysctl/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.14 2002/11/30 03:10:54 lukem Exp $ +# $NetBSD: Makefile,v 1.15 2003/12/04 19:49:39 atatat Exp $ # @(#)Makefile 8.1 (Berkeley) 6/6/93 .include @@ -6,7 +6,6 @@ PROG= sysctl MAN= sysctl.8 -CPPFLAGS+=-DINET6 -DIPSEC -I${.CURDIR} -I${NETBSDSRCDIR}/sys -#CPPFLAGS+=-DTCP6 +SRCS= sysctl.c sysctlutil.c .include diff --git a/sbin/sysctl/sysctl.c b/sbin/sysctl/sysctl.c index 2ccb19ee048f..7f4cae9d3ed9 100644 --- a/sbin/sysctl/sysctl.c +++ b/sbin/sysctl/sysctl.c @@ -1,4 +1,36 @@ -/* $NetBSD: sysctl.c,v 1.74 2003/11/10 20:03:29 jonathan Exp $ */ +/* $NetBSD: sysctl.c,v 1.75 2003/12/04 19:49:39 atatat Exp $ */ + +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Andrew Brown. + * + * 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. + * 3. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ /* * Copyright (c) 1993 @@ -40,238 +72,250 @@ __COPYRIGHT( #if 0 static char sccsid[] = "@(#)sysctl.c 8.1 (Berkeley) 6/6/93"; #else -__RCSID("$NetBSD: sysctl.c,v 1.74 2003/11/10 20:03:29 jonathan Exp $"); +__RCSID("$NetBSD: sysctl.c,v 1.75 2003/12/04 19:49:39 atatat Exp $"); #endif #endif /* not lint */ +#include #include -#include -#include #include -#include #include -#include #include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include - +#include +#include +#include #include -#include -#include -#include -#include #include -#include -#include #include #include #include - -#ifdef INET6 -#include #include -#include -#include -#include -#ifdef TCP6 -#include -#include -#include -#endif -#include -#endif /* INET6 */ - -#include "../../sys/compat/linux/common/linux_exec.h" -#include "../../sys/compat/irix/irix_sysctl.h" -#include "../../sys/compat/darwin/darwin_sysctl.h" - -#ifdef IPSEC -#include -#include +#include +#include +#include +#include #include -#endif /* IPSEC */ -#include - -#include +#include #include +#include #include +#include +#include #include #include #include +#include #include -struct ctlname topname[] = CTL_NAMES; -struct ctlname kernname[] = CTL_KERN_NAMES; -struct ctlname vmname[] = CTL_VM_NAMES; -struct ctlname vfsname[] = CTL_VFS_NAMES; -struct ctlname netname[] = CTL_NET_NAMES; -struct ctlname hwname[] = CTL_HW_NAMES; -struct ctlname username[] = CTL_USER_NAMES; -struct ctlname ddbname[] = CTL_DDB_NAMES; -struct ctlname debugname[CTL_DEBUG_MAXID]; -#ifdef CTL_MACHDEP_NAMES -struct ctlname machdepname[] = CTL_MACHDEP_NAMES; -#endif -struct ctlname emulname[] = CTL_EMUL_NAMES; -struct ctlname vendorname[] = { { 0, 0 } }; +/* + * this needs to be able to do the printing and the setting + */ +#define HANDLER_PROTO const char *, const char *, char *, \ + int *, u_int, const struct sysctlnode *, \ + u_int, void * +#define HANDLER_ARGS const char *sname, const char *dname, char *value, \ + int *name, u_int namelen, const struct sysctlnode *pnode, \ + u_int type, void *v +#define DISPLAY_VALUE 0 +#define DISPLAY_OLD 1 +#define DISPLAY_NEW 2 -/* this one is dummy, it's used only for '-a' or '-A' */ -struct ctlname procname[] = { {0, 0}, {"curproc", CTLTYPE_NODE} }; +/* + * generic routines + */ +static struct handlespec *findprinter(const int *, u_int); +static struct handlespec *findwriter(const int *, u_int); +static void print_tree(int *, u_int, struct sysctlnode *, u_int, int); +static void write_number(int *, u_int, struct sysctlnode *, char *); +static void write_string(int *, u_int, struct sysctlnode *, char *); +static void display_number(const struct sysctlnode *, const char *, + const void *, size_t, int); +static void display_string(const struct sysctlnode *, const char *, + const void *, size_t, int); +static void display_struct(const struct sysctlnode *, const char *, + const void *, size_t, int); +static void hex_dump(const unsigned char *, size_t); +static void usage(void); +static void parse(char *); +static void cparse(char *); +static void dparse(char *); +static void sysctlerror(int); -char names[BUFSIZ]; +/* + * unexported from some place else (XXX tbd) + */ +int learn_tree(int *, u_int, struct sysctlnode *); -struct list { - struct ctlname *list; - int size; -}; -struct list toplist = { topname, CTL_MAXID }; -struct list secondlevel[] = { - { 0, 0 }, /* CTL_UNSPEC */ - { kernname, KERN_MAXID }, /* CTL_KERN */ - { vmname, VM_MAXID }, /* CTL_VM */ - { vfsname, VFS_MAXID }, /* CTL_VFS */ - { netname, NET_MAXID }, /* CTL_NET */ - { 0, CTL_DEBUG_MAXID }, /* CTL_DEBUG */ - { hwname, HW_MAXID }, /* CTL_HW */ -#ifdef CTL_MACHDEP_NAMES - { machdepname, CPU_MAXID }, /* CTL_MACHDEP */ -#else - { 0, 0 }, /* CTL_MACHDEP */ -#endif - { username, USER_MAXID }, /* CTL_USER_NAMES */ - { ddbname, DDBCTL_MAXID }, /* CTL_DDB_NAMES */ - { procname, 2 }, /* dummy name */ - { vendorname, 0 }, /* CTL_VENDOR_NAMES */ - { emulname, EMUL_MAXID }, /* CTL_EMUL_NAMES */ +/* + * "handlers" + */ +static void printother(HANDLER_PROTO); +static void kern_clockrate(HANDLER_PROTO); +static void kern_boottime(HANDLER_PROTO); +static void kern_consdev(HANDLER_PROTO); +static void kern_cp_time(HANDLER_PROTO); +static void vm_loadavg(HANDLER_PROTO); +static void proc_limit(HANDLER_PROTO); +#ifdef CPU_DISKINFO +static void machdep_diskinfo(HANDLER_PROTO); +#endif /* CPU_DISKINFO */ - { 0, 0}, +struct handlespec { + int ps_name[CTL_MAXNAME]; + void (*ps_p)(HANDLER_PROTO); + void (*ps_w)(HANDLER_PROTO); + void *ps_d; +} handlers[] = { + { { CTL_KERN, KERN_CLOCKRATE }, kern_clockrate }, + { { CTL_KERN, KERN_VNODE }, printother, NULL, "pstat" }, + { { CTL_KERN, KERN_PROC }, printother, NULL, "ps" }, + { { CTL_KERN, KERN_PROC2 }, printother, NULL, "ps" }, + { { CTL_KERN, KERN_PROC_ARGS }, printother, NULL, "ps" }, + { { CTL_KERN, KERN_FILE }, printother, NULL, "pstat" }, + { { CTL_KERN, KERN_NTPTIME }, printother, NULL, + "ntpdc -c kerninfo" }, + { { CTL_KERN, KERN_MSGBUF }, printother, NULL, "dmesg" }, + { { CTL_KERN, KERN_BOOTTIME }, kern_boottime }, + { { CTL_KERN, KERN_CONSDEV }, kern_consdev }, + { { CTL_KERN, KERN_CP_TIME }, kern_cp_time }, + { { CTL_KERN, KERN_SYSVIPC_INFO }, printother, NULL, "ipcs" }, + { { CTL_VM, VM_METER }, printother, NULL, + "vmstat' or 'systat" }, + { { CTL_VM, VM_LOADAVG }, vm_loadavg }, + { { CTL_VM, VM_UVMEXP }, printother, NULL, + "vmstat' or 'systat" }, + { { CTL_VM, VM_UVMEXP2 }, printother, NULL, + "vmstat' or 'systat" }, + { { CTL_VFS, 2 /* NFS */, NFS_NFSSTATS }, + printother, NULL, "nfsstat" }, + { { CTL_NET }, printother, NULL, NULL }, + { { CTL_NET, PF_LOCAL }, printother, NULL, NULL }, + { { CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_IDENT }, + printother, NULL, "identd" }, + { { CTL_NET, PF_INET6, IPPROTO_TCP, TCPCTL_IDENT }, + printother, NULL, "identd" }, + + { { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_DRLIST }, + printother, NULL, "something else" }, + { { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_PRLIST }, + printother, NULL, "something else" }, + + + { { CTL_NET, PF_KEY, KEYCTL_DUMPSA }, + printother, NULL, "setkey" }, + { { CTL_NET, PF_KEY, KEYCTL_DUMPSP }, + printother, NULL, "setkey" }, + /* { { CTL_DEBUG }, printother, NULL, NULL }, */ + { { CTL_HW, HW_DISKSTATS }, printother, NULL, "iostat" }, +#ifdef CPU_CONSDEV + { { CTL_MACHDEP, CPU_CONSDEV }, kern_consdev }, +#endif /* CPU_CONSDEV */ +#ifdef CPU_DISKINFO + { { CTL_MACHDEP, CPU_DISKINFO },machdep_diskinfo }, +#endif /* CPU_CONSDEV */ + { { CTL_DDB }, printother, NULL, NULL }, + { { CTL_PROC, -1, PROC_PID_LIMIT, -1, -1 }, proc_limit, proc_limit }, + { { CTL_UNSPEC }, }, }; -int Aflag, aflag, eflag, nflag, qflag, wflag; +struct sysctlnode my_root = { +#if defined(lint) + 0 +#else /* defined(lint) */ + .sysctl_flags = SYSCTL_ROOT| + SYSCTL_TYPE(CTLTYPE_NODE), + .sysctl_size = sizeof(struct sysctlnode), + .sysctl_num = 0, + .sysctl_name = "(prog_root)", +#endif /* defined(lint) */ +}; + +int Aflag, aflag, Mflag, nflag, qflag, rflag, wflag, xflag; +int req; FILE *warnfp = stderr; /* - * Variables requiring special processing. + * vah-riables n stuff */ -#define CLOCK 0x00000001 -#define BOOTTIME 0x00000002 -#define CONSDEV 0x00000003 -#define DISKINFO 0x00000004 -#define CPTIME 0x00000005 -#define CNMAGIC 0x00000006 +char gsname[SYSCTL_NAMELEN * CTL_MAXNAME + CTL_MAXNAME], + gdname[10 * CTL_MAXNAME + CTL_MAXNAME]; +char sep[2] = ".", *eq = " = "; +const char *lname[] = { + "top", "second", "third", "fourth", "fifth", "sixth", + "seventh", "eighth", "ninth", "tenth", "eleventh", "twelfth" +}; /* - * A dummy type for limits, which requires special parsing + * you've heard of main, haven't you? */ -#define CTLTYPE_LIMIT ((~0x1) << 31) - -int main(int, char *[]); - -static void listall(const char *, struct list *); -static void parse(char *, int); -static void debuginit(void); -static int sysctl_inet(char *, char **, int[], int, int *); -#ifdef INET6 -static int sysctl_inet6(char *, char **, int[], int, int *); -#endif -static int sysctl_vfs(char *, char **, int[], int, int *); -static int sysctl_proc(char *, char **, int[], int, int *); -static int sysctl_3rd(struct list *, char *, char **, int[], int, int *); - -#ifdef IPSEC -struct ctlname keynames[] = KEYCTL_NAMES; -struct list keyvars = { keynames, KEYCTL_MAXID }; -#endif /*IPSEC*/ -struct ctlname vfsgenname[] = CTL_VFSGENCTL_NAMES; -struct list vfsgenvars = { vfsgenname, VFSGEN_MAXID }; -struct ctlname mbufnames[] = CTL_MBUF_NAMES; -struct list mbufvars = { mbufnames, MBUF_MAXID }; -struct ctlname pipenames[] = CTL_PIPE_NAMES; -struct list pipevars = { pipenames, KERN_PIPE_MAXID }; -struct ctlname tkstatnames[] = KERN_TKSTAT_NAMES; -struct list tkstatvars = { tkstatnames, KERN_TKSTAT_MAXID }; - -static int sysctl_linux(char *, char **, int[], int, int *); -static int sysctl_irix(char *, char **, int[], int, int *); -static int sysctl_darwin(char *, char **, int[], int, int *); -static int findname(char *, char *, char **, struct list *); -static void usage(void); - -#define USEAPP(s, a) \ - if (flags) fprintf(warnfp, "%s: use '%s' to view this information\n", s, a) - - int main(int argc, char *argv[]) { char *fn = NULL; - int ch, lvl1; + int name[CTL_MAXNAME]; + int ch; - while ((ch = getopt(argc, argv, "Aaef:nqw")) != -1) { + while ((ch = getopt(argc, argv, "Aaef:Mnqrwx")) != -1) { switch (ch) { - case 'A': - Aflag = 1; + Aflag++; break; - case 'a': - aflag = 1; + aflag++; break; - case 'e': - eflag = 1; + eq = "="; break; - case 'f': fn = optarg; - wflag = 1; + wflag++; + break; + case 'M': + Mflag++; break; - case 'n': - nflag = 1; + nflag++; break; - case 'q': - qflag = 1; + qflag++; + break; + case 'r': + rflag++; break; - case 'w': - wflag = 1; + wflag++; + break; + case 'x': + xflag++; break; - default: usage(); } } - if (qflag && !wflag) - usage(); - argc -= optind; argv += optind; - if (Aflag || aflag) { + if (qflag && !wflag) + usage(); + if (xflag && rflag) + usage(); + /* if ((xflag || rflag) && wflag) + usage(); */ + /* if (aflag && Mflag) + usage(); */ + if ((Aflag || Mflag) && argc == 0) + aflag = 1; + + if (Aflag) warnfp = stdout; - debuginit(); - for (lvl1 = 1; lvl1 < CTL_MAXID; lvl1++) { - if (secondlevel[lvl1].list == NULL && - secondlevel[lvl1].size == 0) - break; - listall(topname[lvl1].ctl_name, &secondlevel[lvl1]); - } - return 0; + req = 0; + + if (aflag) { + print_tree(&name[0], 0, NULL, CTLTYPE_NODE, 1); + /* if (argc == 0) */ + return (0); } if (fn) { @@ -282,1021 +326,1617 @@ main(int argc, char *argv[]) if (fp == NULL) { err(1, "%s", fn); } else { - for (; (l = fparseln(fp, NULL, NULL, NULL, 0)) != NULL; - free(l)) { - if (*l) - parse(l, 1); + while ((l = fparseln(fp, NULL, NULL, NULL, 0)) != NULL) + { + if (*l) { + parse(l); + free(l); + } } fclose(fp); } - } else { - if (argc == 0) - usage(); - while (argc-- > 0) - parse(*argv++, 1); + return (0); } - return 0; + + if (argc == 0) + usage(); + + while (argc-- > 0) + parse(*argv++); + + return (0); } /* - * List all variables known to the system. + * ******************************************************************** + * how to find someone special to handle the reading (or maybe even + * writing) of a particular node + * ******************************************************************** */ -static void -listall(const char *prefix, struct list *lp) +static struct handlespec * +findprinter(const int *name, u_int namelen) { - int lvl2; - char name[BUFSIZ], *cp; + struct handlespec *p; + int i, j; - if (lp->list == 0) - return; - strlcpy(name, prefix, sizeof(name)); - strlcat(name, ".", sizeof(name)); - cp = &name[strlen(name)]; - for (lvl2 = 0; lvl2 < lp->size; lvl2++) { - if (lp->list[lvl2].ctl_name == 0) - continue; - strlcpy(cp, lp->list[lvl2].ctl_name, - sizeof(name) - (cp - name)); - parse(name, Aflag); + if (namelen < 1) + return (NULL); + + p = &handlers[0]; + for (i = 0; p[i].ps_name[0] != CTL_UNSPEC; i++) { + for (j = 0; j < namelen; j++) + if (p[i].ps_name[j] != name[j] && + p[i].ps_name[j] != -1) + break; + if (j == namelen && p[i].ps_p != NULL) + return (&p[i]); } + + return (NULL); +} + +static struct handlespec * +findwriter(const int *name, u_int namelen) +{ + struct handlespec *p; + int i, j; + + if (namelen < 1) + return (NULL); + + p = &handlers[0]; + for (i = 0; p[i].ps_name[0] != CTL_UNSPEC; i++) { + for (j = 0; j < namelen; j++) + if (p[i].ps_name[j] != name[j] && + p[i].ps_name[j] != -1) + break; + if (j == namelen && p[i].ps_w != NULL) + return (&p[i]); + } + + return (NULL); } /* - * Parse a name into a MIB entry. - * Lookup and print out the MIB entry if it exists. - * Set a new value if requested. + * ******************************************************************** + * convert this special number to a special string so we can print the + * mib + * ******************************************************************** + */ +static const char * +sf(u_int f) +{ + static char s[256]; + char *c; + + s[0] = '\0'; + c = ""; + +#define print_flag(_f, _s, _c, _q, _x) \ + if ((/*CONSTCOND*/_x) ? \ + (((_f) & (_x)) == (__CONCAT(SYSCTL_,_q))) : \ + ((_f) & (__CONCAT(SYSCTL_,_q)))) { \ + strlcat((_s), (_c), sizeof(_s)); \ + strlcat((_s), __STRING(_q), sizeof(_s)); \ + (_c) = ","; \ + (_f) &= ~(__CONCAT(SYSCTL_,_q)|(_x)); \ + } + print_flag(f, s, c, READONLY, SYSCTL_READWRITE); + print_flag(f, s, c, READONLY1, SYSCTL_READWRITE); + print_flag(f, s, c, READONLY2, SYSCTL_READWRITE); + print_flag(f, s, c, READWRITE, SYSCTL_READWRITE); + print_flag(f, s, c, ANYWRITE, 0); + print_flag(f, s, c, PRIVATE, 0); + print_flag(f, s, c, PERMANENT, 0); + print_flag(f, s, c, OWNDATA, 0); + print_flag(f, s, c, IMMEDIATE, 0); + print_flag(f, s, c, HEX, 0); + print_flag(f, s, c, ROOT, 0); + print_flag(f, s, c, ANYNUMBER, 0); + print_flag(f, s, c, HIDDEN, 0); + print_flag(f, s, c, ALIAS, 0); +#undef print_flag + + if (f) { + char foo[9]; + snprintf(foo, sizeof(foo), "%x", f); + strlcat(s, c, sizeof(s)); + strlcat(s, foo, sizeof(s)); + } + + return (s); +} + +static const char * +st(u_int t) +{ + + switch (t) { + case CTLTYPE_NODE: + return "NODE"; + case CTLTYPE_INT: + return "INT"; + case CTLTYPE_STRING: + return "STRING"; + case CTLTYPE_QUAD: + return "QUAD"; + case CTLTYPE_STRUCT: + return "STRUCT"; + } + + return "???"; +} + +/* + * ******************************************************************** + * print this node and any others underneath it + * ******************************************************************** */ static void -parse(char *string, int flags) +print_tree(int *name, u_int namelen, struct sysctlnode *pnode, u_int type, + int add) { - int indx, type, state, len; - int special = 0; - void *newval = 0; - int intval, newsize = 0; - long long quadval; - size_t size; - struct list *lp; - int mib[CTL_MAXNAME]; - char *cp, *bufp, buf[BUFSIZ], *eq; - double loads[3]; + struct sysctlnode *node; + int rc, ni; + size_t sz; + char *sp, *dp, n[20]; + struct handlespec *p; - if (eflag) - eq = "="; - else - eq = " = "; + sp = &gsname[strlen(gsname)]; + dp = &gdname[strlen(gdname)]; - bufp = buf; - snprintf(buf, BUFSIZ, "%s", string); - if ((cp = strchr(string, '=')) != NULL) { - if (!wflag) - errx(2, "Must specify -w to set variables"); - *strchr(buf, '=') = '\0'; - *cp++ = '\0'; - while (isspace((unsigned char) *cp)) - cp++; - newval = cp; - newsize = strlen(cp); - } - if ((indx = findname(string, "top", &bufp, &toplist)) == -1) - return; - mib[0] = indx; - if (indx == CTL_DEBUG) - debuginit(); - if (mib[0] == CTL_PROC) { - type = CTLTYPE_NODE; - len = 1; - } else { - lp = &secondlevel[indx]; - if (lp->list == 0) { - warnx("Class `%s' is not implemented", - topname[indx].ctl_name); - return; - } - if (bufp == NULL) { - listall(topname[indx].ctl_name, lp); - return; - } - if ((indx = findname(string, "second", &bufp, lp)) == -1) - return; - mib[1] = indx; - type = lp->list[indx].ctl_type; - len = 2; - } - switch (mib[0]) { - - case CTL_KERN: - switch (mib[1]) { - case KERN_PROF: - mib[2] = GPROF_STATE; - size = sizeof state; - if (sysctl(mib, 3, &state, &size, NULL, 0) < 0) { - if (flags == 0) - return; - if (!nflag) - fprintf(warnfp, "%s: ", string); - fprintf(warnfp, - "kernel is not compiled for profiling\n"); - return; - } - if (!nflag) - printf("%s: %s\n", string, - state == GMON_PROF_OFF ? "off" : "running"); - return; - case KERN_VNODE: - case KERN_FILE: - USEAPP(string, "pstat"); - return; - case KERN_PROC: - case KERN_PROC2: - case KERN_PROC_ARGS: - USEAPP(string, "ps"); - return; - case KERN_CLOCKRATE: - special = CLOCK; - break; - case KERN_BOOTTIME: - special = BOOTTIME; - break; - case KERN_NTPTIME: - USEAPP(string, "ntpdc -c kerninfo"); - return; - case KERN_MBUF: - len = sysctl_3rd(&mbufvars, string, &bufp, mib, flags, - &type); - if (len < 0) - return; - break; - case KERN_CP_TIME: - special = CPTIME; - break; - case KERN_MSGBUF: - USEAPP(string, "dmesg"); - return; - case KERN_CONSDEV: - special = CONSDEV; - break; - case KERN_PIPE: - len = sysctl_3rd(&pipevars, string, &bufp, mib, flags, - &type); - if (len < 0) - return; - break; - case KERN_TKSTAT: - len = sysctl_3rd(&tkstatvars, string, &bufp, mib, flags, - &type); - if (len < 0) - return; - break; - } - break; - - case CTL_HW: - switch (mib[1]) { - case HW_DISKSTATS: - USEAPP(string, "iostat"); - return; - case HW_CNMAGIC: - if (!nflag) - special = CNMAGIC; - break; - } - break; - - case CTL_VM: - switch (mib[1]) { - case VM_LOADAVG: - getloadavg(loads, 3); - if (!nflag) - printf("%s: ", string); - printf("%.2f %.2f %.2f\n", loads[0], loads[1], - loads[2]); - return; - - case VM_METER: - case VM_UVMEXP: - case VM_UVMEXP2: - USEAPP(string, "vmstat' or 'systat"); - return; - } - break; - - case CTL_NET: - if (mib[1] == PF_INET) { - len = sysctl_inet(string, &bufp, mib, flags, &type); - if (len >= 0) - break; - return; - } -#ifdef INET6 - else if (mib[1] == PF_INET6) { - len = sysctl_inet6(string, &bufp, mib, flags, &type); - if (len >= 0) - break; - return; - } -#endif /* INET6 */ -#ifdef IPSEC - else if (mib[1] == PF_KEY) { - len = sysctl_3rd(&keyvars, string, &bufp, mib, flags, - &type); - if (len >= 0) - break; - return; - } -#endif /* IPSEC */ - if (flags == 0) - return; - USEAPP(string, "netstat"); - return; - - case CTL_DEBUG: - mib[2] = CTL_DEBUG_VALUE; - len = 3; - break; - - case CTL_MACHDEP: -#ifdef CPU_CONSDEV - if (mib[1] == CPU_CONSDEV) - special = CONSDEV; -#endif -#ifdef CPU_DISKINFO - if (mib[1] == CPU_DISKINFO) - special = DISKINFO; -#endif - break; - - case CTL_VFS: - if (mib[1] == VFS_GENERIC) { - len = sysctl_3rd(&vfsgenvars, string, &bufp, mib, flags, - &type); - /* Don't bother with VFS_CONF. */ - if (mib[2] == VFS_CONF) - len = -1; - } else - len = sysctl_vfs(string, &bufp, mib, flags, &type); - if (len < 0) - return; - - /* XXX Special-case for NFS stats. */ - if (mib[1] == 2 && mib[2] == NFS_NFSSTATS) { - USEAPP(string, "nfsstat"); - return; - } - break; - - case CTL_VENDOR: - case CTL_USER: - case CTL_DDB: - break; - case CTL_PROC: - len = sysctl_proc(string, &bufp, mib, flags, &type); - if (len < 0) - return; - break; - case CTL_EMUL: - switch (mib[1]) { - case EMUL_IRIX: - len = sysctl_irix(string, &bufp, mib, flags, &type); - break; - - case EMUL_LINUX: - len = sysctl_linux(string, &bufp, mib, flags, &type); - break; - - case EMUL_DARWIN: - len = sysctl_darwin(string, &bufp, mib, flags, &type); - break; - - default: - warnx("Illegal emul level value: %d", mib[0]); - break; - } - if (len < 0) - return; - break; - default: - warnx("Illegal top level value: %d", mib[0]); - return; - - } - if (bufp) { - warnx("Name %s in %s is unknown", bufp, string); - return; - } - if (newsize > 0) { - switch (type) { - case CTLTYPE_INT: - intval = atoi(newval); - newval = &intval; - newsize = sizeof intval; - break; - - case CTLTYPE_LIMIT: - if (strcmp(newval, "unlimited") == 0) { - quadval = RLIM_INFINITY; - newval = &quadval; - newsize = sizeof quadval; - break; - } - /* FALLTHROUGH */ - case CTLTYPE_QUAD: - sscanf(newval, "%lld", &quadval); - newval = &quadval; - newsize = sizeof quadval; - break; + if (sp != &gsname[0] && dp == &gdname[0]) { + /* + * aw...shucks. now we must play catch up + */ + for (ni = 0; ni < namelen; ni++) { + (void)snprintf(n, sizeof(n), "%d", name[ni]); + if (ni > 0) + strncat(gdname, ".", sizeof(gdname)); + strncat(gdname, n, sizeof(gdname)); } } - size = BUFSIZ; - if (sysctl(mib, len, buf, &size, newsize ? newval : 0, newsize) == -1) { - if (flags == 0) - return; - switch (errno) { - case EOPNOTSUPP: - fprintf(warnfp, - "%s: the value is not available\n", string); - return; - case ENOTDIR: - fprintf(warnfp, - "%s: the specification is incomplete\n", string); - return; - case ENOMEM: - fprintf(warnfp, - "%s: this type is unknown to this program\n", - string); - return; - default: - fprintf(warnfp, "%s: sysctl() failed with %s\n", - string, strerror(errno)); - return; + + if (pnode == NULL) + pnode = &my_root; + else if (add) { + snprintf(n, sizeof(n), "%d", pnode->sysctl_num); + if (namelen > 1) { + strncat(gsname, sep, sizeof(gsname)); + strncat(gdname, ".", sizeof(gdname)); } + strncat(gsname, pnode->sysctl_name, sizeof(gsname)); + strncat(gdname, n, sizeof(gdname)); } - if (qflag && (newsize > 0)) - return; - if (special == CLOCK) { - struct clockinfo *clkp = (struct clockinfo *)buf; - if (!nflag) - printf("%s: ", string); - printf( - "tick = %d, tickadj = %d, hz = %d, profhz = %d, stathz = %d\n", - clkp->tick, clkp->tickadj, clkp->hz, clkp->profhz, clkp->stathz); - return; - } - if (special == BOOTTIME) { - struct timeval *btp = (struct timeval *)buf; - time_t boottime; - - if (!nflag) { - boottime = btp->tv_sec; - /* ctime() provides the trailing newline */ - printf("%s%s%s", string, eq, ctime(&boottime)); - } else - printf("%ld\n", (long) btp->tv_sec); - return; - } - if (special == CONSDEV) { - dev_t dev = *(dev_t *)buf; - - if (!nflag) - printf("%s%s%s\n", string, eq, devname(dev, S_IFCHR)); + if (Mflag && pnode != &my_root) { + if (nflag) + printf("%s: ", gdname); else - printf("0x%x\n", dev); - return; - } - if (special == DISKINFO) { -#ifdef CPU_DISKINFO - /* Don't know a good way to deal with this i386 specific one */ - /* OTOH this does pass the info out... */ - struct disklist *dl = (void *)buf; - struct biosdisk_info *bi; - struct nativedisk_info *ni; - uint i, b, lim; - if (!nflag) - printf("%s: ", string); - lim = dl->dl_nbiosdisks; - if (lim > MAX_BIOSDISKS) - lim = MAX_BIOSDISKS; - for (bi = dl->dl_biosdisks, i = 0; i < lim; bi++, i++) - printf("%x:%lld(%d/%d/%d),%x ", - bi->bi_dev, (long long)bi->bi_lbasecs, - bi->bi_cyl, bi->bi_head, bi->bi_sec, - bi->bi_flags); - lim = dl->dl_nnativedisks; - ni = dl->dl_nativedisks; - bi = dl->dl_biosdisks; - if ((char *)&ni[lim] != (char *)buf + size) { - fprintf(warnfp, "size mismatch\n"); + printf("%s (%s): ", gsname, gdname); + printf("CTLTYPE_%s", st(type)); + if (type == CTLTYPE_NODE) { + if (SYSCTL_FLAGS(pnode->sysctl_flags) & SYSCTL_ALIAS) + printf(", alias %d", + pnode->sysctl_alias); + else + printf(", children %d/%d", + pnode->sysctl_clen, + pnode->sysctl_csize); + } + printf(", size %zu", pnode->sysctl_size); + printf(", flags 0x%x<%s>", + SYSCTL_FLAGS(pnode->sysctl_flags), + sf(SYSCTL_FLAGS(pnode->sysctl_flags))); + if (pnode->sysctl_func) + printf(", func=%p", pnode->sysctl_func); + printf(", ver=%d", pnode->sysctl_ver); + printf("\n"); + if (type != CTLTYPE_NODE) { + *sp = *dp = '\0'; return; } - for (i = 0; i < lim; ni++, i++) { - char sep = ':'; - printf(" %.*s", (int)sizeof ni->ni_devname, - ni->ni_devname); - for (b = 0; b < ni->ni_nmatches; sep = ',', b++) - printf("%c%x", sep, - bi[ni->ni_biosmatches[b]].bi_dev); - } - printf("\n"); -#endif - return; } - if (special == CPTIME) { - u_int64_t *cp_time = (u_int64_t *)buf; - if (!nflag) - printf("%s: ", string); - printf("user = %llu, nice = %llu, sys = %llu, intr = %llu, " - "idle = %llu\n", (unsigned long long) cp_time[0], - (unsigned long long) cp_time[1], - (unsigned long long) cp_time[2], - (unsigned long long) cp_time[3], - (unsigned long long) cp_time[4]); + /* + * if this is an alias and we added our name, that means we + * got here by recursing down into the tree, so skip it. The + * only way to print an aliased node is with either -M or by + * name specifically. + */ + if (SYSCTL_FLAGS(pnode->sysctl_flags) & SYSCTL_ALIAS && add) { + *sp = *dp = '\0'; return; } - if (special == CNMAGIC) { + + p = findprinter(name, namelen); + if (type != CTLTYPE_NODE && p != NULL) { + (*p->ps_p)(gsname, gdname, NULL, name, namelen, pnode, type, + p->ps_d); + *sp = *dp = '\0'; + return; + } + + if (type != CTLTYPE_NODE && pnode->sysctl_size == 0) { + rc = sysctl(&name[0], namelen, NULL, &sz, NULL, 0); + if (rc == -1) { + sysctlerror(1); + *sp = *dp = '\0'; + return; + } + if (sz == 0) { + if ((Aflag || req) && !Mflag) + printf("%s: node contains no data\n", gsname); + *sp = *dp = '\0'; + return; + } + } + else + sz = pnode->sysctl_size; + + switch (type) { + case CTLTYPE_NODE: { + learn_tree(name, namelen, pnode); + node = pnode->sysctl_child; + if (node == NULL) { + if (p != NULL) + (*p->ps_p)(gsname, gdname, NULL, name, namelen, + pnode, type, p->ps_d); + else if ((Aflag || req) && !Mflag) + printf("%s: no children\n", gsname); + } + else { + req = 0; + for (ni = 0; ni < pnode->sysctl_clen; ni++) { + name[namelen] = node[ni].sysctl_num; + if ((node[ni].sysctl_flags & SYSCTL_HIDDEN) && + !(Aflag || req)) + continue; + print_tree(name, namelen + 1, &node[ni], + SYSCTL_TYPE(node[ni].sysctl_flags), + 1); + } + } + break; + } + case CTLTYPE_INT: { int i; - - if (!nflag) - printf("%s%s", string, eq); - for (i = 0; i < size - 1; i++) - printf("\\x%2.2x", buf[i]); - if (newsize != 0) { - printf(" -> "); - for (i = 0; i < newsize - 1; i++) - printf("\\x%2.2x", ((unsigned char *)newval)[i]); + rc = sysctl(name, namelen, &i, &sz, NULL, 0); + if (rc == -1) { + sysctlerror(1); + break; } - printf("\n"); + display_number(pnode, gsname, &i, sizeof(i), DISPLAY_VALUE); + break; + } + case CTLTYPE_STRING: { + unsigned char buf[1024], *tbuf; + tbuf = buf; + sz = sizeof(buf); + rc = sysctl(&name[0], namelen, tbuf, &sz, NULL, 0); + if (rc == -1 && errno == ENOMEM) { + tbuf = malloc(sz); + if (tbuf == NULL) { + sysctlerror(1); + break; + } + rc = sysctl(&name[0], namelen, tbuf, &sz, NULL, 0); + } + if (rc == -1) + sysctlerror(1); + else + display_string(pnode, gsname, buf, sz, DISPLAY_VALUE); + if (tbuf != buf) + free(tbuf); + break; + } + case CTLTYPE_QUAD: { + u_quad_t q; + sz = sizeof(q); + rc = sysctl(&name[0], namelen, &q, &sz, NULL, 0); + if (rc == -1) { + sysctlerror(1); + break; + } + display_number(pnode, gsname, &q, sizeof(q), DISPLAY_VALUE); + break; + } + case CTLTYPE_STRUCT: { + /* + * we shouldn't actually get here, but if we + * do, would it be nice to have *something* to + * do other than completely ignore the + * request. + */ + unsigned char *d; + if ((d = malloc(sz)) == NULL) { + fprintf(warnfp, "%s: !malloc failed!\n", gsname); + break; + } + rc = sysctl(&name[0], namelen, d, &sz, NULL, 0); + if (rc == -1) { + sysctlerror(1); + break; + } + display_struct(pnode, gsname, d, sz, DISPLAY_VALUE); + free(d); + break; + } + default: + /* should i print an error here? */ + break; + } + + *sp = *dp = '\0'; +} + +/* + * ******************************************************************** + * parse a request, possibly determining that it's a create or destroy + * request + * ******************************************************************** + */ +static void +parse(char *l) +{ + struct sysctlnode *node; + struct handlespec *w; + int name[CTL_MAXNAME]; + u_int namelen, type; + char *key, *value, *dot; + size_t sz; + + req = 1; + key = l; + value = strchr(l, '='); + if (value != NULL) { + if (!wflag) { + fprintf(warnfp, + "%s: Must specify -w to set variables\n", + getprogname()); + exit(1); + } + *value++ = '\0'; + } + + if ((dot = strpbrk(key, "./")) == NULL) + sep[0] = '.'; + else + sep[0] = dot[0]; + sep[1] = '\0'; + + if (key[0] == sep[0] && key[1] == sep[0]) { + if (value != NULL) + value[-1] = '='; + if (strncmp(key + 2, "create=", 7) == 0) + cparse(key + 9); + else if (strncmp(key + 2, "destroy=", 8) == 0) + dparse(key + 10); + else + fprintf(warnfp, "%s: unable to parse '%s'\n", + getprogname(), key); + return; + } + + node = &my_root; + namelen = CTL_MAXNAME; + sz = sizeof(gsname); + + if (sysctlnametomib(key, &name[0], &namelen, gsname, &sz, &node) == -1) + { + fprintf(warnfp, "%s: %s level name '%s' in '%s' is invalid\n", + getprogname(), lname[namelen], gsname, l); + exit(1); + } + + type = SYSCTL_TYPE(node->sysctl_flags); + + if (value == NULL) { + print_tree(&name[0], namelen, node, type, 0); + gsname[0] = '\0'; + return; + } + + if (type != CTLTYPE_NODE && (w = findwriter(name, namelen)) != NULL) { + (*w->ps_w)(gsname, gdname, value, name, namelen, node, type, + w->ps_d); + gsname[0] = '\0'; return; } switch (type) { - case CTLTYPE_INT: - if (newsize == 0) { - if (!nflag) - printf("%s%s", string, eq); - printf("%d\n", *(int *)buf); - } else { - if (!nflag) - printf("%s: %d -> ", string, *(int *)buf); - printf("%d\n", *(int *)newval); - } - return; - - case CTLTYPE_STRING: - /* If sysctl() didn't return any data, don't print a string. */ - if (size == 0) - buf[0] = '\0'; - if (newsize == 0) { - if (!nflag) - printf("%s%s", string, eq); - printf("%s\n", buf); - } else { - if (!nflag) - printf("%s: %s -> ", string, buf); - printf("%s\n", (char *) newval); - } - return; - - case CTLTYPE_LIMIT: -#define PRINTF_LIMIT(lim) { \ -if ((lim) == RLIM_INFINITY) \ - printf("unlimited");\ -else \ - printf("%lld", (long long)(lim)); \ -} - - if (newsize == 0) { - if (!nflag) - printf("%s%s", string, eq); - PRINTF_LIMIT((long long)(*(quad_t *)buf)); - } else { - if (!nflag) { - printf("%s: ", string); - PRINTF_LIMIT((long long)(*(quad_t *)buf)); - printf(" -> "); - } - PRINTF_LIMIT((long long)(*(quad_t *)newval)); - } - printf("\n"); - return; -#undef PRINTF_LIMIT - - case CTLTYPE_QUAD: - if (newsize == 0) { - if (!nflag) - printf("%s%s", string, eq); - printf("%lld\n", (long long)(*(quad_t *)buf)); - } else { - if (!nflag) - printf("%s: %lld -> ", string, - (long long)(*(quad_t *)buf)); - printf("%lld\n", (long long)(*(quad_t *)newval)); - } - return; - - case CTLTYPE_STRUCT: - warnx("%s: unknown structure returned", string); - return; - - default: case CTLTYPE_NODE: - warnx("%s: unknown type returned", string); - return; + /* + * XXX old behavior is to print. should we error instead? + */ + print_tree(&name[0], namelen, node, CTLTYPE_NODE, 1); + break; + case CTLTYPE_INT: + write_number(&name[0], namelen, node, value); + break; + case CTLTYPE_STRING: + write_string(&name[0], namelen, node, value); + break; + case CTLTYPE_QUAD: + write_number(&name[0], namelen, node, value); + break; + case CTLTYPE_STRUCT: + /* + * XXX old behavior is to print. should we error instead? + */ + /* fprintf(warnfp, "you can't write to %s\n", gsname); */ + print_tree(&name[0], namelen, node, type, 0); + break; } } /* - * Initialize the set of debugging names - */ + + //create=foo.bar.whatever..., + [type=(int|quad|string|struct|node),] + [size=###,] + [n=###,] + [flags=(tiohxparw12),] + [addr=0x####,|symbol=...|value=...] + + size is optional for some types. type must be set before anything + else. nodes can have [r12whp], but nothing else applies. if no + size or type is given, node is asserted. writeable is the default, + with [r12w] being read-only, writeable below securelevel 1, + writeable below securelevel 2, and unconditionally writeable + respectively. if you specify addr, it is assumed to be the name of + a kernel symbol, if value, SYSCTL_OWNDATA will be asserted for + strings, SYSCTL_IMMEDIATE for ints and u_quad_ts. you cannot + specify both value and addr. + +*/ + static void -debuginit(void) +cparse(char *l) { - int mib[3], loc, i; - size_t size; + struct sysctlnode node; + size_t sz; + char *nname, *key, *value, *data, *addr, *c, *t; + int name[CTL_MAXNAME], i, rc, method, flags, rw; + u_int namelen, type; + u_quad_t q; + long li, lo; - if (secondlevel[CTL_DEBUG].list != 0) - return; - secondlevel[CTL_DEBUG].list = debugname; - mib[0] = CTL_DEBUG; - mib[2] = CTL_DEBUG_NAME; - for (loc = 0, i = 0; i < CTL_DEBUG_MAXID; i++) { - mib[1] = i; - size = BUFSIZ - loc; - if (sysctl(mib, 3, &names[loc], &size, NULL, 0) == -1) - continue; - debugname[i].ctl_name = &names[loc]; - debugname[i].ctl_type = CTLTYPE_INT; - loc += size; - } -} + /* + * these are the pieces that make up the description of a new + * node + */ + memset(&node, 0, sizeof(node)); + node.sysctl_num = CTL_CREATE; /* any number is fine */ + flags = 0; + rw = -1; + type = 0; + sz = 0; + data = addr = NULL; + memset(name, 0, sizeof(name)); + namelen = 0; + method = 0; -struct ctlname ifqname[] = CTL_IFQ_NAMES; -struct list ifqvars[] = { -/*0*/ { ifqname, 5 }, /* ip.ifq */ -}; + /* + * misc stuff used when constructing + */ + i = 0; + q = 0; + key = NULL; + value = NULL; -struct ctlname inetname[] = CTL_IPPROTO_NAMES; -struct ctlname ipname[] = IPCTL_NAMES; -struct ctlname icmpname[] = ICMPCTL_NAMES; -struct ctlname tcpname[] = TCPCTL_NAMES; -struct ctlname udpname[] = UDPCTL_NAMES; -#ifdef IPSEC -struct ctlname ipsecname[] = IPSECCTL_NAMES; -#endif -struct list inetlist = { inetname, IPPROTO_MAXID }; -struct list inetvars[] = { -/*0*/ { ipname, IPCTL_MAXID }, /* ip */ - { icmpname, ICMPCTL_MAXID }, /* icmp */ - { 0, 0 }, /* igmp */ - { 0, 0 }, /* ggmp */ - { 0, 0 }, - { 0, 0 }, - { tcpname, TCPCTL_MAXID }, /* tcp */ - { 0, 0 }, - { 0, 0 }, /* egp */ - { 0, 0 }, -/*10*/ { 0, 0 }, - { 0, 0 }, - { 0, 0 }, /* pup */ - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { udpname, UDPCTL_MAXID }, /* udp */ - { 0, 0 }, - { 0, 0 }, -/*20*/ { 0, 0 }, - { 0, 0 }, - { 0, 0 }, /* idp */ - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, -/*30*/ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, - { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, -/*40*/ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, - { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, -#ifdef IPSEC - { ipsecname, IPSECCTL_MAXID }, /* esp - for backward compatibility */ - { ipsecname, IPSECCTL_MAXID }, /* ah */ -#else - { 0, 0 }, - { 0, 0 }, -#endif -}; + /* + * the name of the thing we're trying to create is first, so + * pick it off. + */ + nname = l; + if ((c = strchr(nname, ',')) != NULL) + *c++ = '\0'; -/* - * handle internet requests - */ -static int -sysctl_inet(char *string, char **bufpp, int mib[], int flags, int *typep) -{ - struct list *lp; - int indx; + while (c != NULL) { - if (*bufpp == NULL) { - listall(string, &inetlist); - return (-1); - } - if ((indx = findname(string, "third", bufpp, &inetlist)) == -1) - return (-1); - mib[2] = indx; - if (indx <= IPPROTO_MAXID && inetvars[indx].list != NULL) - lp = &inetvars[indx]; - else if (!flags) - return (-1); - else { - fprintf(warnfp, - "%s: no variables defined for protocol\n", string); - return (-1); - } - if (*bufpp == NULL) { - listall(string, lp); - return (-1); - } - if ((indx = findname(string, "fourth", bufpp, lp)) == -1) - return (-1); - mib[3] = indx; - *typep = lp->list[indx].ctl_type; - - if (*typep == CTLTYPE_NODE) { - int tindx; - - if (*bufpp == 0) { - listall(string, &ifqvars[0]); - return(-1); + /* + * pull off the next "key=value" pair + */ + key = c; + if ((t = strchr(key, '=')) != NULL) { + *t++ = '\0'; + value = t; } - lp = &ifqvars[0]; - if ((tindx = findname(string, "fifth", bufpp, lp)) == -1) - return (-1); - mib[4] = tindx; - *typep = lp->list[tindx].ctl_type; - return(5); - } + else + value = NULL; - return (4); -} - -#ifdef INET6 -struct ctlname inet6name[] = CTL_IPV6PROTO_NAMES; -struct ctlname ip6name[] = IPV6CTL_NAMES; -struct ctlname icmp6name[] = ICMPV6CTL_NAMES; -struct ctlname udp6name[] = UDP6CTL_NAMES; -struct ctlname pim6name[] = PIM6CTL_NAMES; -struct ctlname ipsec6name[] = IPSEC6CTL_NAMES; -struct list inet6list = { inet6name, IPV6PROTO_MAXID }; -struct list inet6vars[] = { -/*0*/ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, - { 0, 0 }, - { tcpname, TCPCTL_MAXID }, /* tcp6 */ - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, -/*10*/ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { udp6name, UDP6CTL_MAXID }, /* udp6 */ - { 0, 0 }, - { 0, 0 }, -/*20*/ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, - { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, -/*30*/ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, - { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, -/*40*/ { 0, 0 }, - { ip6name, IPV6CTL_MAXID }, /* ipv6 */ - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, -#ifdef IPSEC -/*50*/ { ipsec6name, IPSECCTL_MAXID }, /* esp6 - for backward compatibility */ - { ipsec6name, IPSECCTL_MAXID }, /* ah6 */ -#else - { 0, 0 }, - { 0, 0 }, -#endif - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { icmp6name, ICMPV6CTL_MAXID }, /* icmp6 */ - { 0, 0 }, -/*60*/ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, - { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, -/*70*/ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, - { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, -/*80*/ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, - { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, -/*90*/ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, - { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, -/*100*/ { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { pim6name, PIM6CTL_MAXID }, /* pim6 */ -}; - -/* - * handle internet6 requests - */ -static int -sysctl_inet6(char *string, char **bufpp, int mib[], int flags, int *typep) -{ - struct list *lp; - int indx; - - if (*bufpp == NULL) { - listall(string, &inet6list); - return (-1); - } - if ((indx = findname(string, "third", bufpp, &inet6list)) == -1) - return (-1); - mib[2] = indx; - if (indx <= sizeof(inet6vars)/sizeof(inet6vars[0]) - && inet6vars[indx].list != NULL) { - lp = &inet6vars[indx]; - } else if (!flags) { - return (-1); - } else { - fprintf(stderr, "%s: no variables defined for this protocol\n", - string); - return (-1); - } - if (*bufpp == NULL) { - listall(string, lp); - return (-1); - } - if ((indx = findname(string, "fourth", bufpp, lp)) == -1) - return (-1); - mib[3] = indx; - *typep = lp->list[indx].ctl_type; - return (4); -} -#endif /* INET6 */ - -struct ctlname ffsname[] = FFS_NAMES; -struct ctlname nfsname[] = NFS_NAMES; -struct list vfsvars[] = { - { 0, 0 }, /* generic */ - { ffsname, FFS_MAXID }, /* FFS */ - { nfsname, NFS_MAXID }, /* NFS */ - { 0, 0 }, /* MFS */ - { 0, 0 }, /* MSDOS */ - { 0, 0 }, /* LFS */ - { 0, 0 }, /* old LOFS */ - { 0, 0 }, /* FDESC */ - { 0, 0 }, /* PORTAL */ - { 0, 0 }, /* NULL */ - { 0, 0 }, /* UMAP */ - { 0, 0 }, /* KERNFS */ - { 0, 0 }, /* PROCFS */ - { 0, 0 }, /* AFS */ - { 0, 0 }, /* CD9660 */ - { 0, 0 }, /* UNION */ - { 0, 0 }, /* ADOSFS */ - { 0, 0 }, /* EXT2FS */ - { 0, 0 }, /* CODA */ - { 0, 0 }, /* FILECORE */ -}; - -/* - * handle vfs requests - */ -static int -sysctl_vfs(char *string, char **bufpp, int mib[], int flags, int *typep) -{ - struct list *lp = &vfsvars[mib[1]]; - int indx; - - if (lp->list == NULL) { - if (flags) - fprintf(warnfp, - "%s: no variables defined for file system\n", - string); - return (-1); - } - if (*bufpp == NULL) { - listall(string, lp); - return (-1); - } - if ((indx = findname(string, "third", bufpp, lp)) == -1) - return (-1); - mib[2] = indx; - *typep = lp->list[indx].ctl_type; - return (3); -} - -/* - * handle 3rd level requests. - */ -static int -sysctl_3rd(struct list *lp, char *string, char **bufpp, int mib[], int flags, - int *typep) -{ - int indx; - - if (*bufpp == NULL) { - listall(string, lp); - return (-1); - } - if ((indx = findname(string, "third", bufpp, lp)) == -1) - return (-1); - mib[2] = indx; - *typep = lp->list[indx].ctl_type; - return (3); -} - -struct ctlname procnames[] = PROC_PID_NAMES; -struct list procvars = {procnames, PROC_PID_MAXID}; -struct ctlname proclimitnames[] = PROC_PID_LIMIT_NAMES; -struct list proclimitvars = {proclimitnames, PROC_PID_LIMIT_MAXID}; -struct ctlname proclimittypenames[] = PROC_PID_LIMIT_TYPE_NAMES; -struct list proclimittypevars = {proclimittypenames, - PROC_PID_LIMIT_TYPE_MAXID}; -/* - * handle kern.proc requests - */ -static int -sysctl_proc(char *string, char **bufpp, int mib[], int flags, int *typep) -{ - char *cp, name[BUFSIZ]; - struct list *lp; - int indx; - - if (*bufpp == NULL) { - strlcpy(name, string, sizeof(name)); - strlcat(name, ".curproc", sizeof(name)); - parse(name, Aflag); - return (-1); - } - cp = strsep(bufpp, "."); - if (cp == NULL) { - warnx("%s: incomplete specification", string); - return (-1); - } - if (strcmp(cp, "curproc") == 0) { - mib[1] = PROC_CURPROC; - } else { - mib[1] = atoi(cp); - if (mib[1] == 0) { - warnx("second level name %s in %s is invalid", cp, - string); - return (-1); - } - } - *typep = CTLTYPE_NODE; - lp = &procvars; - if (*bufpp == NULL) { - listall(string, lp); - return (-1); - } - if ((indx = findname(string, "third", bufpp, lp)) == -1) - return (-1); - mib[2] = indx; - *typep = lp->list[indx].ctl_type; - if (*typep != CTLTYPE_NODE) - return(3); - lp = &proclimitvars; - if (*bufpp == NULL) { - listall(string, lp); - return (-1); - } - if ((indx = findname(string, "fourth", bufpp, lp)) == -1) - return (-1); - mib[3] = indx; - lp = &proclimittypevars; - if (*bufpp == NULL) { - listall(string, lp); - return (-1); - } - if ((indx = findname(string, "fifth", bufpp, lp)) == -1) - return (-1); - mib[4] = indx; - *typep = CTLTYPE_LIMIT; - return(5); -} - -struct ctlname linuxnames[] = EMUL_LINUX_NAMES; -struct list linuxvars = { linuxnames, EMUL_LINUX_MAXID }; -struct ctlname linuxkernnames[] = EMUL_LINUX_KERN_NAMES; -struct list linuxkernvars = { linuxkernnames, EMUL_LINUX_KERN_MAXID }; - -static int -sysctl_linux(char *string, char **bufpp, int mib[], int flags, int *typep) -{ - struct list *lp = &linuxvars; - int indx; - char name[BUFSIZ]; - - if (*bufpp == NULL) { - strlcpy(name, string, sizeof(name)); - strlcat(name, ".kern", sizeof(name)); - listall(name, &linuxkernvars); - return (-1); - } - if ((indx = findname(string, "third", bufpp, lp)) == -1) - return (-1); - mib[2] = indx; - lp = &linuxkernvars; - *typep = lp->list[indx].ctl_type; - if ((indx = findname(string, "fourth", bufpp, lp)) == -1) - return (-1); - mib[3] = indx; - *typep = lp->list[indx].ctl_type; - return (4); -} - -struct ctlname irixnames[] = EMUL_IRIX_NAMES; -struct list irixvars = { irixnames, EMUL_IRIX_MAXID }; -struct ctlname irixkernnames[] = EMUL_IRIX_KERN_NAMES; -struct list irixkernvars = { irixkernnames, EMUL_IRIX_KERN_MAXID }; - -static int -sysctl_irix(char *string, char **bufpp, int mib[], int flags, int *typep) -{ - struct list *lp = &irixvars; - int indx; - char name[BUFSIZ]; - - if (*bufpp == NULL) { - strlcpy(name, string, sizeof(name)); - strlcat(name, ".kern", sizeof(name)); - listall(name, &irixkernvars); - return (-1); - } - if ((indx = findname(string, "third", bufpp, lp)) == -1) - return (-1); - mib[2] = indx; - lp = &irixkernvars; - *typep = lp->list[indx].ctl_type; - if ((indx = findname(string, "fourth", bufpp, lp)) == -1) - return (-1); - mib[3] = indx; - *typep = lp->list[indx].ctl_type; - return (4); -} - -struct ctlname darwinnames[] = EMUL_DARWIN_NAMES; -struct list darwinvars = { darwinnames, EMUL_DARWIN_MAXID }; - -static int -sysctl_darwin(char *string, char **bufpp, int mib[], int flags, int *typep) -{ - struct list *lp = &darwinvars; - int indx; - - if (*bufpp == NULL) { - listall(string, &darwinvars); - return (-1); - } - if ((indx = findname(string, "third", bufpp, lp)) == -1) - return (-1); - mib[2] = indx; - lp = &darwinvars; - *typep = lp->list[indx].ctl_type; - return (3); -} - -/* - * Scan a list of names searching for a particular name. - */ -static int -findname(char *string, char *level, char **bufp, struct list *namelist) -{ - char *name; - int i; - - if (namelist->list == 0 || (name = strsep(bufp, ".")) == NULL) { - warnx("%s: incomplete specification", string); - return (-1); - } - for (i = 0; i < namelist->size; i++) - if (namelist->list[i].ctl_name != NULL && - strcmp(name, namelist->list[i].ctl_name) == 0) + /* + * if the "key" is "value", then that eats the rest of + * the string, so we're done, otherwise bite it off at + * the next comma. + */ + if (strcmp(key, "value") == 0) { + c = NULL; + data = value; break; - if (i == namelist->size) { - warnx("%s level name %s in %s is invalid", - level, name, string); - return (-1); + } + else { + if ((c = strchr(value, ',')) != NULL) + *c++ = '\0'; + } + + /* + * note that we (mostly) let the invoker of sysctl(8) + * play rampant here and depend on the kernel to tell + * them that they were wrong. well...within reason. + * we later check the various parameters against each + * other to make sure it makes some sort of sense. + */ + if (strcmp(key, "addr") == 0) { + /* + * we can't check these two. only the kernel + * can tell us when it fails to find the name + * (or if the address is invalid). + */ + if (method != 0) { + fprintf(warnfp, + "%s: %s: already have %s for new node\n", + getprogname(), nname, + method == CTL_CREATE ? "addr" : "symbol"); + exit(1); + } + errno = 0; + addr = (void*)strtoul(value, &t, 0); + if (*t != '\0' || errno != 0) { + fprintf(warnfp, + "%s: %s: '%s' is not a valid address\n", + getprogname(), nname, value); + exit(1); + } + method = CTL_CREATE; + } + else if (strcmp(key, "symbol") == 0) { + if (method != 0) { + fprintf(warnfp, + "%s: %s: already have %s for new node\n", + getprogname(), nname, + method == CTL_CREATE ? "addr" : "symbol"); + exit(1); + } + addr = value; + method = CTL_CREATESYM; + } + else if (strcmp(key, "type") == 0) { + if (strcmp(value, "node") == 0) + type = CTLTYPE_NODE; + else if (strcmp(value, "int") == 0) { + sz = sizeof(int); + type = CTLTYPE_INT; + } + else if (strcmp(value, "string") == 0) + type = CTLTYPE_STRING; + else if (strcmp(value, "quad") == 0) { + sz = sizeof(u_quad_t); + type = CTLTYPE_QUAD; + } + else if (strcmp(value, "struct") == 0) + type = CTLTYPE_STRUCT; + else { + fprintf(warnfp, + "%s: %s: '%s' is not a valid type\n", + getprogname(), nname, value); + exit(1); + } + } + else if (strcmp(key, "size") == 0) { + errno = 0; + /* + * yes, i know size_t is not an unsigned long, + * but we can all agree that it ought to be, + * right? + */ + sz = strtoul(value, &t, 0); + if (*t != '\0' || errno != 0) { + fprintf(warnfp, + "%s: %s: '%s' is not a valid size\n", + getprogname(), nname, value); + exit(1); + } + } + else if (strcmp(key, "n") == 0) { + errno = 0; + li = strtol(value, &t, 0); + node.sysctl_num = li; + lo = node.sysctl_num; + if (*t != '\0' || errno != 0 || li != lo || lo < 0) { + fprintf(warnfp, + "%s: %s: '%s' is not a valid mib number\n", + getprogname(), nname, value); + exit(1); + } + } + else if (strcmp(key, "flags") == 0) { + t = value; + while (*t != '\0') { + switch (*t) { + case 'a': + flags |= SYSCTL_ANYWRITE; + break; + case 'h': + flags |= SYSCTL_HIDDEN; + break; + case 'i': + flags |= SYSCTL_IMMEDIATE; + break; + case 'o': + flags |= SYSCTL_OWNDATA; + break; + case 'p': + flags |= SYSCTL_PRIVATE; + break; + case 'x': + flags |= SYSCTL_HEX; + break; + + case 'r': + rw = SYSCTL_READONLY; + break; + case '1': + rw = SYSCTL_READONLY1; + break; + case '2': + rw = SYSCTL_READONLY2; + break; + case 'w': + rw = SYSCTL_READWRITE; + break; + default: + fprintf(warnfp, + "%s: %s: '%c' is not a valid flag\n", + getprogname(), nname, *t); + exit(1); + } + t++; + } + } + else { + fprintf(warnfp, "%s: %s: unrecognized keyword '%s'\n", + getprogname(), nname, key); + exit(1); + } } - return (i); + + /* + * now that we've finished parsing the given string, fill in + * anything they didn't specify + */ + if (type == 0) + type = CTLTYPE_NODE; + + /* + * the "data" can be interpreted various ways depending on the + * type of node we're creating, as can the size + */ + if (data != NULL) { + if (addr != NULL) { + fprintf(warnfp, + "%s: %s: cannot specify both value and " + "address\n", getprogname(), nname); + exit(1); + } + + switch (type) { + case CTLTYPE_INT: + errno = 0; + li = strtol(data, &t, 0); + i = li; + lo = i; + if (*t != '\0' || errno != 0 || li != lo || lo < 0) { + fprintf(warnfp, + "%s: %s: '%s' is not a valid integer\n", + getprogname(), nname, value); + exit(1); + } + if (!(flags & SYSCTL_OWNDATA)) { + flags |= SYSCTL_IMMEDIATE; + node.sysctl_idata = i; + } + else + node.sysctl_data = &i; + if (sz == 0) + sz = sizeof(int); + break; + case CTLTYPE_STRING: + flags |= SYSCTL_OWNDATA; + node.sysctl_data = data; + if (sz == 0) + sz = strlen(data) + 1; + else if (sz < strlen(data) + 1) { + fprintf(warnfp, "%s: %s: ignoring size=%zu for " + "string node, too small for given " + "value\n", getprogname(), nname, sz); + sz = strlen(data) + 1; + } + break; + case CTLTYPE_QUAD: + errno = 0; + q = strtouq(data, &t, 0); + if (*t != '\0' || errno != 0) { + fprintf(warnfp, + "%s: %s: '%s' is not a valid quad\n", + getprogname(), nname, value); + exit(1); + } + if (!(flags & SYSCTL_OWNDATA)) { + flags |= SYSCTL_IMMEDIATE; + node.sysctl_qdata = q; + } + else + node.sysctl_data = &q; + if (sz == 0) + sz = sizeof(u_quad_t); + break; + case CTLTYPE_STRUCT: + fprintf(warnfp, + "%s: %s: struct not initializable\n", + getprogname(), nname); + exit(1); + } + + /* + * these methods have all provided local starting + * values that the kernel must copy in + */ + } + + /* + * hmm...no data, but we have an address of data. that's + * fine. + */ + else if (addr != 0) + node.sysctl_data = (void*)addr; + + /* + * no data and no address? well...okay. we might be able to + * manage that. + */ + else if (type != CTLTYPE_NODE) { + if (sz == 0) { + fprintf(warnfp, + "%s: %s: need a size or a starting value\n", + getprogname(), nname); + exit(1); + } + if (!(flags & SYSCTL_IMMEDIATE)) + flags |= SYSCTL_OWNDATA; + } + + /* + * now we do a few sanity checks on the description we've + * assembled + */ + if ((flags & SYSCTL_IMMEDIATE) && + (type == CTLTYPE_STRING || type == CTLTYPE_STRUCT)) { + fprintf(warnfp, + "%s: %s: cannot make an immediate %s\n", + getprogname(), nname, + (type == CTLTYPE_STRING) ? "string" : "struct"); + exit(1); + } + if (type == CTLTYPE_NODE && node.sysctl_data != NULL) { + fprintf(warnfp, "%s: %s: nodes do not have data\n", + getprogname(), nname); + exit(1); + } + + /* + * some types must have a particular size + */ + if (sz != 0) { + if ((type == CTLTYPE_INT && sz != sizeof(int)) || + (type == CTLTYPE_QUAD && sz != sizeof(u_quad_t)) || + (type == CTLTYPE_NODE && sz != 0)) { + fprintf(warnfp, "%s: %s: wrong size for type\n", + getprogname(), nname); + exit(1); + } + } + else if (type == CTLTYPE_STRUCT) { + fprintf(warnfp, "%s: %s: struct must have size\n", + getprogname(), nname); + exit(1); + } + + /* + * now...if no one said anything yet, we default nodes or + * any type that owns data being writeable, and everything + * else being readonly. + */ + if (rw == -1) { + if (type == CTLTYPE_NODE || + (flags & (SYSCTL_OWNDATA|SYSCTL_IMMEDIATE))) + rw = SYSCTL_READWRITE; + else + rw = SYSCTL_READONLY; + } + + /* + * if a kernel address was specified, that can't be made + * writeable by us. + if (rw != SYSCTL_READONLY && addr) { + fprintf(warnfp, "%s: %s: kernel data can only be readable\n", + getprogname(), nname); + exit(1); + } + */ + + /* + * what separator were they using in the full name of the new + * node? + */ + if ((t = strpbrk(nname, "./")) == NULL) + sep[0] = '.'; + else + sep[0] = t[0]; + sep[1] = '\0'; + + /* + * put it all together, now. t'ain't much, is it? + */ + node.sysctl_flags = flags|rw|type; + node.sysctl_size = sz; + t = strrchr(nname, sep[0]); + if (t != NULL) + strlcpy(node.sysctl_name, t + 1, sizeof(node.sysctl_name)); + else + strlcpy(node.sysctl_name, nname, sizeof(node.sysctl_name)); + + /* + * if this is a new top-level node, then we don't need to find + * the mib for its parent + */ + if (t == NULL) { + namelen = 0; + gsname[0] = '\0'; + } + + /* + * on the other hand, if it's not a top-level node... + */ + else { + namelen = sizeof(name) / sizeof(name[0]); + sz = sizeof(gsname); + *t = '\0'; + rc = sysctlnametomib(nname, &name[0], &namelen, + gsname, &sz, NULL); + *t = sep[0]; + if (rc == -1) { + fprintf(warnfp, + "%s: %s level name '%s' in '%s' is invalid\n", + getprogname(), lname[namelen], gsname, nname); + exit(1); + } + } + + /* + * yes, a new node is being created + */ + if (method != 0) + name[namelen++] = method; + else + name[namelen++] = CTL_CREATE; + + sz = sizeof(node); + rc = sysctl(&name[0], namelen, &node, &sz, &node, sizeof(node)); + + if (rc == -1) { + fprintf(warnfp, + "%s: %s: CTL_CREATE failed: %s\n", + getprogname(), nname, strerror(errno)); + exit(1); + } + else if (!qflag && !nflag) + printf("%s(%s): (created)\n", nname, st(type)); } +static void +dparse(char *l) +{ + struct sysctlnode node; + size_t sz; + int name[CTL_MAXNAME], rc; + u_int namelen; + + memset(name, 0, sizeof(name)); + namelen = sizeof(name) / sizeof(name[0]); + sz = sizeof(gsname); + rc = sysctlnametomib(l, &name[0], &namelen, gsname, &sz, NULL); + if (rc == -1) { + fprintf(warnfp, + "%s: %s level name '%s' in '%s' is invalid\n", + getprogname(), lname[namelen], gsname, l); + exit(1); + } + + memset(&node, 0, sizeof(node)); + node.sysctl_num = name[namelen - 1]; + name[namelen - 1] = CTL_DESTROY; + + sz = sizeof(node); + rc = sysctl(&name[0], namelen, &node, &sz, &node, sizeof(node)); + + if (rc == -1) { + fprintf(warnfp, + "%s: %s: CTL_DESTROY failed: %s\n", + getprogname(), l, strerror(errno)); + exit(1); + } + else if (!qflag && !nflag) + printf("%s(%s): (destroyed)\n", gsname, + st(SYSCTL_TYPE(node.sysctl_flags))); +} + +/* + * ******************************************************************** + * when things go wrong... + * ******************************************************************** + */ static void usage(void) { const char *progname = getprogname(); (void)fprintf(stderr, - "Usage:\t%s %s\n\t%s %s\n\t%s %s\n\t%s %s\n\t%s %s\n", - progname, "[-en] variable ...", - progname, "[-nq] -w variable=value ...", - progname, "[-en] -a", - progname, "[-en] -A", - progname, "[-nq] -f file"); + "Usage:\t%s %s\n" + "\t%s %s\n" + "\t%s %s\n" + "\t%s %s\n" + "\t%s %s\n" + "\t%s %s\n", + progname, "[-ne] [-x[x]|-r] variable ...", + progname, "[-ne] [-q] -w variable=value ...", + progname, "[-ne] -a", + progname, "[-ne] -A", + progname, "[-ne] -M", + progname, "[-ne] [-q] -f file"); exit(1); } + +void +sysctlerror(int soft) +{ + if (soft) { + switch (errno) { + case ENOENT: + case ENOPROTOOPT: + case ENOTDIR: + case EOPNOTSUPP: + case EPROTONOSUPPORT: + if (Aflag || req) + fprintf(warnfp, + "%s: the value is not available\n", + gsname); + return; + } + } + + fprintf(warnfp, "%s: sysctl() failed with %s\n", + gsname, strerror(errno)); + if (!soft) + exit(1); +} + +/* + * ******************************************************************** + * how to write to a "simple" node + * ******************************************************************** + */ +static void +write_number(int *name, u_int namelen, struct sysctlnode *node, char *value) +{ + int ii, io; + u_quad_t qi, qo; + size_t si, so; + int rc; + void *i, *o; + char *t; + + si = so = 0; + i = o = NULL; + errno = 0; + qi = strtouq(value, &t, 0); + if (errno != 0) { + fprintf(warnfp, "%s: value too large\n", value); + exit(1); + } + if (*t != '\0') { + fprintf(warnfp, "%s: not a number\n", value); + exit(1); + } + + switch (SYSCTL_TYPE(node->sysctl_flags)) { + case CTLTYPE_INT: + ii = (int)qi; + qo = ii; + if (qo != qi) { + fprintf(warnfp, "%s: value too large\n", value); + exit(1); + } + o = &io; + so = sizeof(io); + i = ⅈ + si = sizeof(ii); + break; + case CTLTYPE_QUAD: + o = &qo; + so = sizeof(qo); + i = &qi; + si = sizeof(qi); + break; + } + + rc = sysctl(name, namelen, o, &so, i, si); + if (rc == -1) + sysctlerror(0); + + switch (SYSCTL_TYPE(node->sysctl_flags)) { + case CTLTYPE_INT: + display_number(node, gsname, &io, sizeof(io), DISPLAY_OLD); + display_number(node, gsname, &ii, sizeof(ii), DISPLAY_NEW); + break; + case CTLTYPE_QUAD: + display_number(node, gsname, &qo, sizeof(qo), DISPLAY_OLD); + display_number(node, gsname, &qi, sizeof(qi), DISPLAY_NEW); + break; + } +} + +static void +write_string(int *name, u_int namelen, struct sysctlnode *node, char *value) +{ + char *i, *o; + size_t si, so; + int rc; + + i = value; + si = strlen(i) + 1; + so = node->sysctl_size; + if (si > so && so != 0) { + fprintf(warnfp, "%s: string too long\n", value); + exit(1); + } + o = malloc(so); + if (o == NULL) { + fprintf(warnfp, "%s: !malloc failed!\n", gsname); + exit(1); + } + + rc = sysctl(name, namelen, o, &so, i, si); + if (rc == -1) + sysctlerror(0); + + display_string(node, gsname, o, so, DISPLAY_OLD); + display_string(node, gsname, i, si, DISPLAY_NEW); + free(o); +} + +/* + * ******************************************************************** + * simple ways to print stuff consistently + * ******************************************************************** + */ +static void +display_number(const struct sysctlnode *node, const char *name, + const void *data, size_t sz, int n) +{ + u_quad_t q; + int i; + + if (qflag) + return; + if ((nflag || rflag) && (n == DISPLAY_OLD)) + return; + + if (rflag && n != DISPLAY_OLD) { + fwrite(data, sz, 1, stdout); + return; + } + + if (!nflag) { + if (n == DISPLAY_VALUE) + printf("%s%s", name, eq); + else if (n == DISPLAY_OLD) + printf("%s: ", name); + } + + if (xflag > 1) { + printf("\n"); + hex_dump(data, sz); + return; + } + + switch (SYSCTL_TYPE(node->sysctl_flags)) { + case CTLTYPE_INT: + memcpy(&i, data, sz); + if (xflag) + printf("0x%0*x", (int)sz * 2, i); + else if (node->sysctl_flags & SYSCTL_HEX) + printf("%#x", i); + else + printf("%d", i); + break; + case CTLTYPE_QUAD: + memcpy(&q, data, sz); + if (xflag) + printf("0x%0*" PRIx64, (int)sz * 2, q); + else if (node->sysctl_flags & SYSCTL_HEX) + printf("%#" PRIx64, q); + else + printf("%" PRIu64, q); + break; + } + + if (n == DISPLAY_OLD) + printf(" -> "); + else + printf("\n"); +} + +static void +display_string(const struct sysctlnode *node, const char *name, + const void *data, size_t sz, int n) +{ + const unsigned char *buf = data; + int ni; + + if (qflag) + return; + if ((nflag || rflag) && (n == DISPLAY_OLD)) + return; + + if (rflag && n != DISPLAY_OLD) { + fwrite(data, sz, 1, stdout); + return; + } + + if (!nflag) { + if (n == DISPLAY_VALUE) + printf("%s%s", name, eq); + else if (n == DISPLAY_OLD) + printf("%s: ", name); + } + + if (xflag > 1) { + printf("\n"); + hex_dump(data, sz); + return; + } + + if (xflag || node->sysctl_flags & SYSCTL_HEX) { + for (ni = 0; ni < (int)sz; ni++) { + if (xflag) + printf("%02x", buf[ni]); + if (buf[ni] == '\0') + break; + if (!xflag) + printf("\\x%2.2x", buf[ni]); + } + } + else + printf("%.*s", (int)sz, buf); + + if (n == DISPLAY_OLD) + printf(" -> "); + else + printf("\n"); +} + +/*ARGSUSED*/ +static void +display_struct(const struct sysctlnode *node, const char *name, + const void *data, size_t sz, int n) +{ + const unsigned char *buf = data; + int ni; + size_t more; + + if (qflag) + return; + if (!(xflag || rflag)) { + if (Aflag || req) + fprintf(warnfp, + "%s: this type is unknown to this program\n", + gsname); + return; + } + if ((nflag || rflag) && (n == DISPLAY_OLD)) + return; + + if (rflag && n != DISPLAY_OLD) { + fwrite(data, sz, 1, stdout); + return; + } + + if (!nflag) { + if (n == DISPLAY_VALUE) + printf("%s%s", name, eq); + else if (n == DISPLAY_OLD) + printf("%s: ", name); + } + + if (xflag > 1) { + printf("\n"); + hex_dump(data, sz); + return; + } + + if (sz > 16) { + more = sz - 16; + sz = 16; + } + else + more = 0; + for (ni = 0; ni < (int)sz; ni++) + printf("%02x", buf[ni]); + if (more) + printf("...(%zu more bytes)", more); + printf("\n"); +} + +static void +hex_dump(const unsigned char *buf, size_t len) +{ + int i, j; + char line[80], tmp[12]; + + memset(line, ' ', sizeof(line)); + for (i = 0, j = 15; i < len; i++) { + j = i % 16; + /* reset line */ + if (j == 0) { + line[58] = '|'; + line[77] = '|'; + line[78] = 0; + snprintf(tmp, sizeof(tmp), "%07d", i); + memcpy(&line[0], tmp, 7); + } + /* copy out hex version of byte */ + snprintf(tmp, sizeof(tmp), "%02x", buf[i]); + memcpy(&line[9 + j * 3], tmp, 2); + /* copy out plain version of byte */ + line[60 + j] = (isprint(buf[i])) ? buf[i] : '.'; + /* print a full line and erase it */ + if (j == 15) { + printf("%s\n", line); + memset(line, ' ', sizeof(line)); + } + } + if (line[0] != ' ') + printf("%s\n", line); + printf("%07zu bytes\n", len); +} + +/* + * ******************************************************************** + * functions that handle particular nodes + * ******************************************************************** + */ +/*ARGSUSED*/ +static void +printother(HANDLER_ARGS) +{ + int rc; + void *p; + size_t sz1, sz2; + + if (!(Aflag || req) || Mflag) + return; + + /* + * okay...you asked for it, so let's give it a go + */ + while (type != CTLTYPE_NODE && (xflag || rflag)) { + rc = sysctl(name, namelen, NULL, &sz1, NULL, 0); + if (rc == -1 || sz1 == 0) + break; + p = malloc(sz1); + if (p == NULL) + break; + sz2 = sz1; + rc = sysctl(name, namelen, p, &sz2, NULL, 0); + if (rc == -1 || sz1 != sz2) { + free(p); + break; + } + display_struct(pnode, gsname, p, sz1, DISPLAY_VALUE); + free(p); + return; + } + + /* + * that didn't work...do we have a specific message for this + * thing? + */ + if (v != NULL) { + fprintf(warnfp, "%s: use '%s' to view this information\n", + gsname, (const char *)v); + return; + } + + /* + * hmm...i wonder if we have any generic hints? + */ + switch (name[0]) { + case CTL_NET: + fprintf(warnfp, "%s: use 'netstat' to view this information\n", + sname); + break; + case CTL_DEBUG: + fprintf(warnfp, "%s: missing 'options DEBUG' from kernel?\n", + sname); + break; + case CTL_DDB: + fprintf(warnfp, "%s: missing 'options DDB' from kernel?\n", + sname); + break; + } +} + +/*ARGSUSED*/ +static void +kern_clockrate(HANDLER_ARGS) +{ + struct clockinfo clkinfo; + size_t sz; + int rc; + + sz = sizeof(clkinfo); + rc = sysctl(name, namelen, &clkinfo, &sz, NULL, 0); + if (rc == -1) { + sysctlerror(1); + return; + } + if (sz != sizeof(clkinfo)) + errx(1, "%s: !returned size wrong!", sname); + + if (xflag || rflag) + display_struct(pnode, sname, &clkinfo, sz, + DISPLAY_VALUE); + else if (!nflag) + printf("%s: ", sname); + printf("tick = %d, tickadj = %d, hz = %d, profhz = %d, stathz = %d\n", + clkinfo.tick, clkinfo.tickadj, + clkinfo.hz, clkinfo.profhz, clkinfo.stathz); +} + +/*ARGSUSED*/ +static void +kern_boottime(HANDLER_ARGS) +{ + struct timeval timeval; + time_t boottime; + size_t sz; + int rc; + + sz = sizeof(timeval); + rc = sysctl(name, namelen, &timeval, &sz, NULL, 0); + if (rc == -1) { + sysctlerror(1); + return; + } + if (sz != sizeof(timeval)) + errx(1, "%s: !returned size wrong!", sname); + + boottime = timeval.tv_sec; + if (xflag || rflag) + display_struct(pnode, sname, &timeval, sz, + DISPLAY_VALUE); + else if (!nflag) + /* ctime() provides the \n */ + printf("%s%s%s", sname, eq, ctime(&boottime)); + else if (nflag == 1) + printf("%ld\n", (long)boottime); + else + printf("%ld.%06ld\n", (long)timeval.tv_sec, + (long)timeval.tv_usec); +} + +/*ARGSUSED*/ +static void +kern_consdev(HANDLER_ARGS) +{ + dev_t cons; + size_t sz; + int rc; + + sz = sizeof(cons); + rc = sysctl(name, namelen, &cons, &sz, NULL, 0); + if (rc == -1) { + sysctlerror(1); + return; + } + if (sz != sizeof(cons)) + errx(1, "%s: !returned size wrong!", sname); + + if (xflag || rflag) + display_struct(pnode, sname, &cons, sz, + DISPLAY_VALUE); + else if (!nflag) + printf("%s%s%s\n", sname, eq, devname(cons, S_IFCHR)); + else + printf("0x%x\n", cons); +} + +/*ARGSUSED*/ +static void +kern_cp_time(HANDLER_ARGS) +{ + u_int64_t cp_time[CPUSTATES]; + size_t sz; + int rc; + + sz = sizeof(cp_time); + rc = sysctl(name, namelen, &cp_time, &sz, NULL, 0); + if (rc == -1) { + sysctlerror(1); + return; + } + if (sz != sizeof(cp_time)) + errx(1, "%s: !returned size wrong!", sname); + + if (xflag || rflag) + display_struct(pnode, sname, &cp_time, sz, + DISPLAY_VALUE); + else if (!nflag) + printf("%s: ", sname); + printf("user = %" PRIu64 + ", nice = %" PRIu64 + ", sys = %" PRIu64 + ", intr = %" PRIu64 + ", idle = %" PRIu64 + "\n", + cp_time[CP_USER], + cp_time[CP_NICE], + cp_time[CP_SYS], + cp_time[CP_INTR], + cp_time[CP_IDLE]); +} + +/*ARGSUSED*/ +static void +vm_loadavg(HANDLER_ARGS) +{ + struct loadavg loadavg; + size_t sz; + int rc; + + sz = sizeof(loadavg); + rc = sysctl(name, namelen, &loadavg, &sz, NULL, 0); + if (rc == -1) { + sysctlerror(1); + return; + } + if (sz != sizeof(loadavg)) + errx(1, "%s: !returned size wrong!", sname); + + if (xflag || rflag) + display_struct(pnode, sname, &loadavg, sz, + DISPLAY_VALUE); + else if (!nflag) + printf("%s: ", sname); + printf("%.2f %.2f %.2f\n", + (double) loadavg.ldavg[0] / loadavg.fscale, + (double) loadavg.ldavg[1] / loadavg.fscale, + (double) loadavg.ldavg[2] / loadavg.fscale); +} + +/*ARGSUSED*/ +static void +proc_limit(HANDLER_ARGS) +{ + u_quad_t olim, *newp, nlim; + size_t osz, nsz; + char *t; + int rc; + + osz = sizeof(olim); + if (value != NULL) { + nsz = sizeof(nlim); + newp = &nlim; + if (strcmp(value, "unlimited") == 0) + nlim = RLIM_INFINITY; + else { + errno = 0; + nlim = strtouq(value, &t, 0); + if (*t != '\0' || errno != 0) { + fprintf(warnfp, + "%s: %s: '%s' is not a valid limit\n", + getprogname(), sname, value); + exit(1); + } + } + } + else { + nsz = 0; + newp = NULL; + } + + rc = sysctl(name, namelen, &olim, &osz, newp, nsz); + if (rc == -1) { + sysctlerror(newp == NULL); + return; + } + + if (newp && qflag) + return; + + if (rflag || xflag || olim != RLIM_INFINITY) + display_number(pnode, sname, &olim, sizeof(olim), + newp ? DISPLAY_OLD : DISPLAY_VALUE); + else + display_string(pnode, sname, "unlimited", 10, + newp ? DISPLAY_OLD : DISPLAY_VALUE); + + if (newp) { + if (rflag || xflag || nlim != RLIM_INFINITY) + display_number(pnode, sname, &nlim, sizeof(nlim), + DISPLAY_NEW); + else + display_string(pnode, sname, "unlimited", 10, + DISPLAY_NEW); + } +} + +#ifdef CPU_DISKINFO +/*ARGSUSED*/ +static void +machdep_diskinfo(HANDLER_ARGS) +{ + struct disklist *dl; + struct biosdisk_info *bi; + struct nativedisk_info *ni; + int rc; + size_t sz; + uint i, b, lim; + + rc = sysctl(name, namelen, NULL, &sz, NULL, 0); + if (rc == -1) { + sysctlerror(1); + return; + } + dl = malloc(sz); + if (dl == NULL) { + sysctlerror(1); + return; + } + rc = sysctl(name, namelen, dl, &sz, NULL, 0); + if (rc == -1) { + sysctlerror(1); + return; + } + + if (!nflag) + printf("%s: ", sname); + lim = dl->dl_nbiosdisks; + if (lim > MAX_BIOSDISKS) + lim = MAX_BIOSDISKS; + for (bi = dl->dl_biosdisks, i = 0; i < lim; bi++, i++) + printf("%x:%" PRIu64 "(%d/%d/%d),%x ", + bi->bi_dev, bi->bi_lbasecs, + bi->bi_cyl, bi->bi_head, bi->bi_sec, + bi->bi_flags); + lim = dl->dl_nnativedisks; + ni = dl->dl_nativedisks; + bi = dl->dl_biosdisks; + /* LINTED -- pointer casts are tedious */ + if ((char *)&ni[lim] != (char *)dl + sz) { + fprintf(warnfp, "size mismatch\n"); + return; + } + for (i = 0; i < lim; ni++, i++) { + char t = ':'; + printf(" %.*s", (int)sizeof ni->ni_devname, + ni->ni_devname); + for (b = 0; b < ni->ni_nmatches; t = ',', b++) + printf("%c%x", t, + bi[ni->ni_biosmatches[b]].bi_dev); + } + printf("\n"); +} +#endif /* CPU_DISKINFO */ diff --git a/sbin/sysctl/sysctlutil.c b/sbin/sysctl/sysctlutil.c new file mode 100644 index 000000000000..f60fe887b7b8 --- /dev/null +++ b/sbin/sysctl/sysctlutil.c @@ -0,0 +1,518 @@ +/* $NetBSD: sysctlutil.c,v 1.1 2003/12/04 19:49:39 atatat Exp $ */ + +#include +#define __USE_NEW_SYSCTL +#include + +#include +#include +#include + +#ifdef _REENTRANT +#include "reentrant.h" +#endif /* _REENTRANT */ + +/* + * the place where we attach stuff we learn on the fly, not + * necessarily used. + */ +static struct sysctlnode sysctl_mibroot = { +#if defined(lint) + /* + * lint doesn't like my initializers + */ + 0 +#else /* !lint */ + .sysctl_flags = SYSCTL_ROOT|SYSCTL_PERMANENT|SYSCTL_TYPE(CTLTYPE_NODE), + .sysctl_size = sizeof(struct sysctlnode), + .sysctl_name = "(root)", +#endif /* !lint */ +}; + +/* + * routines to handle learning and cleanup + */ +static int compar(const void *, const void *); +int learn_tree(int *, u_int, struct sysctlnode *); +static void free_children(struct sysctlnode *); +static void relearnhead(void); + +/* + * for ordering nodes -- a query may or may not be given them in + * numeric order + */ +static int +compar(const void *a, const void *b) +{ + + return (((const struct sysctlnode *)a)->sysctl_num - + ((const struct sysctlnode *)b)->sysctl_num); +} + +/* + * sucks in the children at a given level and attaches it to the tree. + */ +int +learn_tree(int *name, u_int namelen, struct sysctlnode *pnode) +{ + int rc; + size_t sz; + + if (pnode == NULL) + pnode = &sysctl_mibroot; + if (SYSCTL_TYPE(pnode->sysctl_flags) != CTLTYPE_NODE) { + errno = EINVAL; + return (-1); + } + if (pnode->sysctl_child != NULL) + return (0); + + if (pnode->sysctl_clen == 0) + sz = SYSCTL_DEFSIZE * sizeof(struct sysctlnode); + else + sz = pnode->sysctl_clen * sizeof(struct sysctlnode); + pnode->sysctl_child = malloc(sz); + if (pnode->sysctl_child == NULL) + return (-1); + + name[namelen] = CTL_QUERY; + pnode->sysctl_clen = 0; + pnode->sysctl_csize = 0; + rc = sysctl(name, namelen + 1, pnode->sysctl_child, &sz, NULL, 0); + if (sz == 0) + return (rc); + if (rc) { + free(pnode->sysctl_child); + pnode->sysctl_child = NULL; + if (errno != ENOMEM) + return (rc); + } + + if (pnode->sysctl_child == NULL) { + pnode->sysctl_child = malloc(sz); + if (pnode->sysctl_child == NULL) + return (-1); + + rc = sysctl(name, namelen + 1, pnode->sysctl_child, &sz, NULL, 0); + if (rc) { + free(pnode->sysctl_child); + pnode->sysctl_child = NULL; + return (rc); + } + } + + /* + * how many did we get? + */ + pnode->sysctl_clen = sz / sizeof(struct sysctlnode); + pnode->sysctl_csize = sz / sizeof(struct sysctlnode); + + /* + * you know, the kernel doesn't really keep them in any + * particular order...just like entries in a directory + */ + qsort(pnode->sysctl_child, pnode->sysctl_clen, + sizeof(struct sysctlnode), compar); + + /* + * rearrange parent<->child linkage + */ + for (rc = 0; rc < pnode->sysctl_clen; rc++) { + pnode->sysctl_child[rc].sysctl_parent = pnode; + if (SYSCTL_TYPE(pnode->sysctl_child[rc].sysctl_flags) == + CTLTYPE_NODE) { + /* + * these nodes may have children, but we + * haven't discovered that yet. + */ + pnode->sysctl_child[rc].sysctl_child = NULL; + } + } + + return (0); +} + +/* + * recursively nukes a branch or an entire tree from the given node + */ +static void +free_children(struct sysctlnode *rnode) +{ + struct sysctlnode *node; + + if (rnode == NULL || + SYSCTL_TYPE(rnode->sysctl_flags) != CTLTYPE_NODE || + rnode->sysctl_child == NULL) + return; + + for (node = rnode->sysctl_child; + node < &rnode->sysctl_child[rnode->sysctl_clen]; + node++) { + free_children(node); + } + free(rnode->sysctl_child); + rnode->sysctl_child = NULL; +} + +/* + * verifies that the head of the tree in the kernel is the same as the + * head of the tree we already got, integrating new stuff and removing + * old stuff, if it's not. + */ +static void +relearnhead(void) +{ + struct sysctlnode *h, *i, *o; + size_t si, so; + int rc, name, nlen, olen, ni, oi, t; + + /* + * if there's nothing there, there's no need to expend any + * effort + */ + if (sysctl_mibroot.sysctl_child == NULL) + return; + + /* + * attempt to pull out the head of the tree, starting with the + * size we have now, and looping if we need more (or less) + * space + */ + si = 0; + so = sysctl_mibroot.sysctl_clen * sizeof(struct sysctlnode); + name = CTL_QUERY; + do { + si = so; + h = malloc(si); + rc = sysctl(&name, 1, h, &so, NULL, 0); + if (rc == -1 && errno != ENOMEM) + return; + if (si < so) + free(h); + } while (si < so); + + /* + * order the new copy of the head + */ + nlen = so / sizeof(struct sysctlnode); + qsort(h, (size_t)nlen, sizeof(struct sysctlnode), compar); + + /* + * verify that everything is the same. if it is, we don't + * need to do any more work here. + */ + olen = sysctl_mibroot.sysctl_clen; + rc = (nlen == olen) ? 0 : 1; + o = sysctl_mibroot.sysctl_child; + for (ni = 0; rc == 0 && ni < nlen; ni++) { + if (h[ni].sysctl_num != o[ni].sysctl_num || + h[ni].sysctl_ver != o[ni].sysctl_ver) + rc = 1; + } + if (rc == 0) { + free(h); + return; + } + + /* + * something changed. h will become the new head, and we need + * pull over any subtrees we already have if they're the same + * version. + */ + i = h; + ni = oi = 0; + while (ni < nlen && oi < olen) { + /* + * something was inserted or deleted + */ + if (SYSCTL_TYPE(i[ni].sysctl_flags) == CTLTYPE_NODE) + i[ni].sysctl_child = NULL; + if (i[ni].sysctl_num != o[oi].sysctl_num) { + if (i[ni].sysctl_num < o[oi].sysctl_num) { + ni++; + } + else { + free_children(&o[oi]); + oi++; + } + continue; + } + + /* + * same number, but different version, so throw away + * any accumulated children + */ + if (i[ni].sysctl_ver != o[oi].sysctl_ver) + free_children(&o[oi]); + + /* + * this node is the same, but we only need to + * move subtrees. + */ + else if (SYSCTL_TYPE(i[ni].sysctl_flags) == CTLTYPE_NODE) { + /* + * move subtree to new parent + */ + i[ni].sysctl_clen = o[oi].sysctl_clen; + i[ni].sysctl_csize = o[oi].sysctl_csize; + i[ni].sysctl_child = o[oi].sysctl_child; + /* + * reparent inherited subtree + */ + for (t = 0; + i[ni].sysctl_child != NULL && + t < i[ni].sysctl_clen; + t++) + i[ni].sysctl_child[t].sysctl_parent = &i[ni]; + } + ni++; + oi++; + } + + /* + * left over new nodes need to have empty subtrees cleared + */ + while (ni < nlen) { + if (SYSCTL_TYPE(i[ni].sysctl_flags) == CTLTYPE_NODE) + i[ni].sysctl_child = NULL; + ni++; + } + + /* + * left over old nodes need to be cleaned out + */ + while (oi < olen) { + free_children(&o[oi]); + oi++; + } + + /* + * pop new head in + */ + sysctl_mibroot.sysctl_clen = nlen; + sysctl_mibroot.sysctl_csize = nlen; + sysctl_mibroot.sysctl_child = h; + free(o); +} + +/* + * that's "given name" as a string, the integer form of the name fit + * to be passed to sysctl(), "canonicalized name" (optional), and a + * pointer to the length of the integer form. oh, and then a pointer + * to the node, in case you (the caller) care. you can leave them all + * NULL except for gname, though that might be rather pointless, + * unless all you wanna do is verify that a given name is acceptable. + * + * returns either 0 (everything was fine) or -1 and sets errno + * accordingly. if errno is set to EAGAIN, we detected a change to + * the mib while parsing, and you should try again. in the case of an + * invalid node name, cname will be set to contain the offending name. + */ +#ifdef _REENTRANT +static mutex_t sysctl_mutex = MUTEX_INITIALIZER; +static int sysctlnametomib_unlocked(const char *, int *, u_int *, + char *, size_t *, struct sysctlnode **); +#endif /* __REENTRANT */ + +int +sysctlnametomib(const char *gname, int *iname, u_int *namelenp, + char *cname, size_t *csz, struct sysctlnode **rnode) +#ifdef _REENTRANT +{ + int rc; + + mutex_lock(&sysctl_mutex); + rc = sysctlnametomib_unlocked(gname, iname, namelenp, cname, csz, + rnode); + mutex_unlock(&sysctl_mutex); + + return (rc); +} + +static int +sysctlnametomib_unlocked(const char *gname, int *iname, u_int *namelenp, + char *cname, size_t *csz, struct sysctlnode **rnode) +#endif /* _REENTRANT */ +{ + struct sysctlnode *pnode, *node; + int name[CTL_MAXNAME], ni, n, haven; + u_int nl; + quad_t q; + char sep[2], token[SYSCTL_NAMELEN], + pname[SYSCTL_NAMELEN * CTL_MAXNAME + CTL_MAXNAME]; + const char *piece, *dot; + char *t; + size_t l; + + if (rnode == NULL || *rnode == NULL) + pnode = &sysctl_mibroot; + else + pnode = *rnode; + + if (sysctl_rootof(pnode) == &sysctl_mibroot) + relearnhead(); + + nl = ni = 0; + token[0] = '\0'; + pname[0] = '\0'; + node = NULL; + + /* + * default to using '.' as the separator, but allow '/' as + * well, and then allow a leading separator + */ + if ((dot = strpbrk(gname, "./")) == NULL) + sep[0] = '.'; + else + sep[0] = dot[0]; + sep[1] = '\0'; + if (gname[0] == sep[0]) { + strlcat(pname, sep, sizeof(pname)); + gname++; + } + +#define COPY_OUT_DATA(t, c, cs, nlp, l) do { \ + if ((c) != NULL && (cs) != NULL) \ + *(cs) = strlcpy((c), (t), *(cs)); \ + else if ((cs) != NULL) \ + *(cs) = strlen(t) + 1; \ + if ((nlp) != NULL) \ + *(nlp) = (l); \ + } while (/*CONSTCOND*/0) + + piece = gname; + while (piece != NULL && *piece != '\0') { + /* + * what was i looking for? + */ + dot = strchr(piece, sep[0]); + if (dot == NULL) { + l = strlcpy(token, piece, sizeof(token)); + if (l > sizeof(token)) { + COPY_OUT_DATA(piece, cname, csz, namelenp, nl); + errno = ENAMETOOLONG; + return (-1); + } + } + else if (dot - piece > sizeof(token) - 1) { + COPY_OUT_DATA(token, cname, csz, namelenp, nl); + errno = ENAMETOOLONG; + return (-1); + } + else { + strncpy(token, piece, (size_t)(dot - piece)); + token[dot - piece] = '\0'; + } + + /* + * i wonder if this "token" is an integer? + */ + errno = 0; + q = strtoq(token, &t, 0); + n = (int)q; + if (errno != 0 || *t != '\0') + haven = 0; + else if (q < INT_MIN || q > UINT_MAX) + haven = 0; + else + haven = 1; + + /* + * make sure i have something to look at + */ + if (SYSCTL_TYPE(pnode->sysctl_flags) != CTLTYPE_NODE) { + if (haven && nl > 0) { + strlcat(pname, sep, sizeof(pname)); + goto just_numbers; + } + COPY_OUT_DATA(token, cname, csz, namelenp, nl); + errno = ENOTDIR; + return (-1); + } + if (pnode->sysctl_child == NULL) { + if (learn_tree(name, nl, pnode) == -1) { + COPY_OUT_DATA(token, cname, csz, namelenp, nl); + return (-1); + } + } + node = pnode->sysctl_child; + if (node == NULL) { + COPY_OUT_DATA(token, cname, csz, namelenp, nl); + errno = ENOENT; + return (-1); + } + + /* + * now...is it there? + */ + for (ni = 0; ni < pnode->sysctl_clen; ni++) + if ((haven && ((n == node[ni].sysctl_num) || + (node[ni].sysctl_flags & SYSCTL_ANYNUMBER))) || + strcmp(token, node[ni].sysctl_name) == 0) + break; + if (ni >= pnode->sysctl_clen) { + COPY_OUT_DATA(token, cname, csz, namelenp, nl); + errno = ENOENT; + return (-1); + } + + /* + * ah...it is. + */ + pnode = &node[ni]; + if (nl > 0) + strlcat(pname, sep, sizeof(pname)); + if (haven && n != pnode->sysctl_num) { + just_numbers: + strlcat(pname, token, sizeof(pname)); + name[nl] = n; + } + else { + strlcat(pname, pnode->sysctl_name, sizeof(pname)); + name[nl] = pnode->sysctl_num; + } + piece = (dot != NULL) ? dot + 1 : NULL; + nl++; + if (nl == CTL_MAXNAME) { + COPY_OUT_DATA(token, cname, csz, namelenp, nl); + errno = ERANGE; + return (-1); + } + } + + if (nl == 0) { + if (namelenp != NULL) + *namelenp = 0; + errno = EINVAL; + return (-1); + } + + COPY_OUT_DATA(pname, cname, csz, namelenp, nl); + if (iname != NULL && namelenp != NULL) + memcpy(iname, &name[0], MIN(nl, *namelenp) * sizeof(int)); + if (namelenp != NULL) + *namelenp = nl; + if (rnode != NULL) { + if (*rnode != NULL) + /* + * they gave us a private tree to work in, so + * we give back a pointer into that private + * tree + */ + *rnode = pnode; + else { + /* + * they gave us a place to put the node data, + * so give them a copy + */ + *rnode = malloc(sizeof(struct sysctlnode)); + if (*rnode != NULL) { + **rnode = *pnode; + (*rnode)->sysctl_child = NULL; + (*rnode)->sysctl_parent = NULL; + } + } + } + + return (0); +}