summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_sysctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/kern_sysctl.c')
-rw-r--r--sys/kern/kern_sysctl.c193
1 files changed, 149 insertions, 44 deletions
diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c
index fec4aea..cb5a266 100644
--- a/sys/kern/kern_sysctl.c
+++ b/sys/kern/kern_sysctl.c
@@ -98,10 +98,13 @@ static struct sx sysctlmemlock;
static int sysctl_root(SYSCTL_HANDLER_ARGS);
-struct sysctl_oid_list sysctl__children; /* root list */
+/* Root list */
+struct sysctl_oid_list sysctl__children = SLIST_HEAD_INITIALIZER(&sysctl__children);
static int sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del,
int recurse);
+static int sysctl_old_kernel(struct sysctl_req *, const void *, size_t);
+static int sysctl_new_kernel(struct sysctl_req *, void *, size_t);
static struct sysctl_oid *
sysctl_find_oidname(const char *name, struct sysctl_oid_list *list)
@@ -136,6 +139,118 @@ sysctl_unlock(void)
SYSCTL_XUNLOCK();
}
+static int
+sysctl_root_handler_locked(struct sysctl_oid *oid, void *arg1, intptr_t arg2,
+ struct sysctl_req *req)
+{
+ int error;
+
+ oid->oid_running++;
+ SYSCTL_XUNLOCK();
+
+ if (!(oid->oid_kind & CTLFLAG_MPSAFE))
+ mtx_lock(&Giant);
+ error = oid->oid_handler(oid, arg1, arg2, req);
+ if (!(oid->oid_kind & CTLFLAG_MPSAFE))
+ mtx_unlock(&Giant);
+
+ SYSCTL_XLOCK();
+ oid->oid_running--;
+ if (oid->oid_running == 0 && (oid->oid_kind & CTLFLAG_DYING) != 0)
+ wakeup(&oid->oid_running);
+
+ return (error);
+}
+
+static void
+sysctl_load_tunable_by_oid_locked(struct sysctl_oid *oidp)
+{
+ struct sysctl_req req;
+ struct sysctl_oid *curr;
+ char *penv;
+ char path[64];
+ ssize_t rem = sizeof(path);
+ ssize_t len;
+ int val_int;
+ long val_long;
+ int64_t val_64;
+ int error;
+
+ path[--rem] = 0;
+
+ for (curr = oidp; curr != NULL; curr = SYSCTL_PARENT(curr)) {
+ len = strlen(curr->oid_name);
+ rem -= len;
+ if (curr != oidp)
+ rem -= 1;
+ if (rem < 0) {
+ printf("OID path exceeds %d bytes\n", (int)sizeof(path));
+ return;
+ }
+ memcpy(path + rem, curr->oid_name, len);
+ if (curr != oidp)
+ path[rem + len] = '.';
+ }
+
+ penv = getenv(path + rem);
+ if (penv == NULL)
+ return;
+
+ memset(&req, 0, sizeof(req));
+
+ req.td = curthread;
+ req.oldfunc = sysctl_old_kernel;
+ req.newfunc = sysctl_new_kernel;
+ req.lock = REQ_UNWIRED;
+
+ switch (oidp->oid_kind & CTLTYPE) {
+ case CTLTYPE_INT:
+ val_int = strtoq(penv, NULL, 0);
+ req.newlen = sizeof(val_int);
+ req.newptr = &val_int;
+ break;
+ case CTLTYPE_UINT:
+ val_int = strtouq(penv, NULL, 0);
+ req.newlen = sizeof(val_int);
+ req.newptr = &val_int;
+ break;
+ case CTLTYPE_LONG:
+ val_long = strtoq(penv, NULL, 0);
+ req.newlen = sizeof(val_long);
+ req.newptr = &val_long;
+ break;
+ case CTLTYPE_ULONG:
+ val_long = strtouq(penv, NULL, 0);
+ req.newlen = sizeof(val_long);
+ req.newptr = &val_long;
+ break;
+ case CTLTYPE_S64:
+ val_64 = strtoq(penv, NULL, 0);
+ req.newlen = sizeof(val_64);
+ req.newptr = &val_64;
+ break;
+ case CTLTYPE_U64:
+ val_64 = strtouq(penv, NULL, 0);
+ req.newlen = sizeof(val_64);
+ req.newptr = &val_64;
+ break;
+ case CTLTYPE_STRING:
+ req.newlen = strlen(penv);
+ req.newptr = penv;
+ break;
+ default:
+ freeenv(penv);
+ return;
+ }
+ error = sysctl_root_handler_locked(oidp, oidp->oid_arg1,
+ oidp->oid_arg2, &req);
+ if (error != 0) {
+ printf("Setting sysctl '%s' to '%s' failed: %d\n",
+ path, penv, error);
+ }
+ freeenv(penv);
+}
+
void
sysctl_register_oid(struct sysctl_oid *oidp)
{
@@ -192,6 +307,15 @@ sysctl_register_oid(struct sysctl_oid *oidp)
SLIST_INSERT_AFTER(q, oidp, oid_link);
else
SLIST_INSERT_HEAD(parent, oidp, oid_link);
+
+ if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE &&
+#ifdef VIMAGE
+ (oidp->oid_kind & CTLFLAG_VNET) == 0 &&
+#endif
+ (oidp->oid_kind & CTLFLAG_TUN) != 0 &&
+ (oidp->oid_kind & CTLFLAG_NOFETCH) == 0) {
+ sysctl_load_tunable_by_oid_locked(oidp);
+ }
}
void
@@ -423,8 +547,6 @@ sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del, int recurse)
if (error)
return (error);
}
- if (del)
- free(SYSCTL_CHILDREN(oidp), M_SYSCTLOID);
}
}
if (oidp->oid_refcnt > 1 ) {
@@ -489,24 +611,16 @@ sysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent,
}
oidp = malloc(sizeof(struct sysctl_oid), M_SYSCTLOID, M_WAITOK|M_ZERO);
oidp->oid_parent = parent;
- SLIST_NEXT(oidp, oid_link) = NULL;
+ SLIST_INIT(&oidp->oid_children);
oidp->oid_number = number;
oidp->oid_refcnt = 1;
oidp->oid_name = strdup(name, M_SYSCTLOID);
oidp->oid_handler = handler;
oidp->oid_kind = CTLFLAG_DYN | kind;
- if ((kind & CTLTYPE) == CTLTYPE_NODE) {
- /* Allocate space for children */
- SYSCTL_CHILDREN_SET(oidp, malloc(sizeof(struct sysctl_oid_list),
- M_SYSCTLOID, M_WAITOK));
- SLIST_INIT(SYSCTL_CHILDREN(oidp));
- oidp->oid_arg2 = arg2;
- } else {
- oidp->oid_arg1 = arg1;
- oidp->oid_arg2 = arg2;
- }
+ oidp->oid_arg1 = arg1;
+ oidp->oid_arg2 = arg2;
oidp->oid_fmt = fmt;
- if (descr)
+ if (descr != NULL)
oidp->oid_descr = strdup(descr, M_SYSCTLOID);
/* Update the context, if used */
if (clist != NULL)
@@ -577,7 +691,7 @@ sysctl_register_all(void *arg)
sysctl_register_oid(*oidp);
SYSCTL_XUNLOCK();
}
-SYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_ANY, sysctl_register_all, 0);
+SYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_FIRST, sysctl_register_all, 0);
/*
* "Staff-functions"
@@ -625,7 +739,7 @@ sysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i)
printf(" Node\n");
if (!oidp->oid_handler) {
sysctl_sysctl_debug_dump_node(
- oidp->oid_arg1, i+2);
+ SYSCTL_CHILDREN(oidp), i + 2);
}
break;
case CTLTYPE_INT: printf(" Int\n"); break;
@@ -1093,26 +1207,28 @@ sysctl_handle_64(SYSCTL_HANDLER_ARGS)
int
sysctl_handle_string(SYSCTL_HANDLER_ARGS)
{
- int error=0;
- char *tmparg;
size_t outlen;
+ int error = 0;
- /*
- * Attempt to get a coherent snapshot by copying to a
- * temporary kernel buffer.
- */
-retry:
- outlen = strlen((char *)arg1)+1;
- tmparg = malloc(outlen, M_SYSCTLTMP, M_WAITOK);
+ /* check for zero-length buffer */
+ if (arg2 == 0)
+ return (ENOMEM);
- if (strlcpy(tmparg, (char *)arg1, outlen) >= outlen) {
- free(tmparg, M_SYSCTLTMP);
- goto retry;
- }
+ if (req->oldptr != NULL) {
+ char *tmparg;
+
+ /* try to make a coherent snapshot of the string */
+ tmparg = malloc(arg2, M_SYSCTLTMP, M_WAITOK);
+ memcpy(tmparg, arg1, arg2);
- error = SYSCTL_OUT(req, tmparg, outlen);
- free(tmparg, M_SYSCTLTMP);
+ outlen = strnlen(tmparg, arg2 - 1) + 1;
+ error = SYSCTL_OUT(req, tmparg, outlen);
+ free(tmparg, M_SYSCTLTMP);
+ } else {
+ outlen = strnlen((char *)arg1, arg2 - 1) + 1;
+ error = SYSCTL_OUT(req, NULL, outlen);
+ }
if (error || !req->newptr)
return (error);
@@ -1123,7 +1239,6 @@ retry:
error = SYSCTL_IN(req, arg1, arg2);
((char *)arg1)[arg2] = '\0';
}
-
return (error);
}
@@ -1489,24 +1604,14 @@ sysctl_root(SYSCTL_HANDLER_ARGS)
if (error != 0)
return (error);
#endif
- oid->oid_running++;
- SYSCTL_XUNLOCK();
#ifdef VIMAGE
if ((oid->oid_kind & CTLFLAG_VNET) && arg1 != NULL)
arg1 = (void *)(curvnet->vnet_data_base + (uintptr_t)arg1);
#endif
- if (!(oid->oid_kind & CTLFLAG_MPSAFE))
- mtx_lock(&Giant);
- error = oid->oid_handler(oid, arg1, arg2, req);
- if (!(oid->oid_kind & CTLFLAG_MPSAFE))
- mtx_unlock(&Giant);
+ error = sysctl_root_handler_locked(oid, arg1, arg2, req);
KFAIL_POINT_ERROR(_debug_fail_point, sysctl_running, error);
- SYSCTL_XLOCK();
- oid->oid_running--;
- if (oid->oid_running == 0 && (oid->oid_kind & CTLFLAG_DYING) != 0)
- wakeup(&oid->oid_running);
return (error);
}
OpenPOWER on IntegriCloud