/* * 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; }
int sysctlfs_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf, off_t offset, size_t *resid, const struct puffs_cred *cred, int ioflag) { struct puffs_node *pn = opc; struct sfsnode *sfs = pn->pn_data; long long ll; int i, rv; bool b; /* * I picked the wrong day to ... um, the wrong place to return errors */ /* easy to support, but just unavailable now */ if (rflag) return EOPNOTSUPP; if (puffs_cred_isjuggernaut(cred) == 0) return EACCES; if (ISADIR(sfs)) return EISDIR; if (offset != 0) return EINVAL; if (ioflag & PUFFS_IO_APPEND) return EINVAL; switch (SYSCTL_TYPE(sfs->sysctl_flags)) { case CTLTYPE_BOOL: if (strcasestr((const char *)buf, "true")) b = true; else if (strcasestr((const char *)buf, "false")) b = false; else return EINVAL; rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL, &b, sizeof(b)); break; case CTLTYPE_INT: if (sscanf((const char *)buf, "%d", &i) != 1) return EINVAL; rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL, &i, sizeof(int)); break; case CTLTYPE_QUAD: if (sscanf((const char *)buf, "%lld", &ll) != 1) return EINVAL; rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL, &ll, sizeof(long long)); break; case CTLTYPE_STRING: rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL, buf, *resid); break; default: rv = EINVAL; break; } if (rv) return rv; *resid = 0; return 0; }
int sysctlfs_node_readdir(struct puffs_usermount *pu, void *opc, struct dirent *dent, off_t *readoff, size_t *reslen, const struct puffs_cred *pcr, int *eofflag, off_t *cookies, size_t *ncookies) { struct sysctlnode sn[SFS_NODEPERDIR]; struct sysctlnode qnode; struct puffs_node *pn_dir = opc; struct puffs_node *pn_res; struct puffs_pathobj po; struct sfsnode *sfs_dir = pn_dir->pn_data, *sfs_ent; SfsName *sname; size_t sl, i; enum vtype vt; ino_t id; *ncookies = 0; again: if (*readoff == DENT_DOT || *readoff == DENT_DOTDOT) { puffs_gendotdent(&dent, sfs_dir->myid, *readoff, reslen); (*readoff)++; PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff); goto again; } memset(&qnode, 0, sizeof(qnode)); sl = SFS_NODEPERDIR * sizeof(struct sysctlnode); qnode.sysctl_flags = SYSCTL_VERSION; sname = PNPATH(pn_dir); (*sname)[PNPLEN(pn_dir)] = CTL_QUERY; if (sysctl(*sname, PNPLEN(pn_dir) + 1, sn, &sl, &qnode, sizeof(qnode)) == -1) return ENOENT; po.po_path = sname; po.po_len = PNPLEN(pn_dir)+1; for (i = DENT_ADJ(*readoff); i < sl / sizeof(struct sysctlnode); i++) { if (SYSCTL_TYPE(sn[i].sysctl_flags) == CTLTYPE_NODE) vt = VDIR; else vt = VREG; /* * check if the node exists. if so, give it the real * inode number. otherwise just fake it. */ (*sname)[PNPLEN(pn_dir)] = sn[i].sysctl_num; pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp, &po); if (pn_res) { sfs_ent = pn_res->pn_data; id = sfs_ent->myid; } else { id = nextid++; } if (!puffs_nextdent(&dent, sn[i].sysctl_name, id, puffs_vtype2dt(vt), reslen)) return 0; (*readoff)++; PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff); } *eofflag = 1; return 0; }
static void getnodedata(struct sfsnode *sfs, struct puffs_pathobj *po, char *buf, size_t *bufsize) { size_t sz; int error = 0; assert(!ISADIR(sfs)); memset(buf, 0, *bufsize); switch (SYSCTL_TYPE(sfs->sysctl_flags)) { case CTLTYPE_BOOL: { bool b; sz = sizeof(bool); assert(sz <= *bufsize); if (sysctl(po->po_path, po->po_len, &b, &sz, NULL, 0) == -1) { error = errno; break; } if (rflag) memcpy(buf, &b, sz); else snprintf(buf, *bufsize, "%s", b ? "true" : "false"); break; } case CTLTYPE_INT: { int i; sz = sizeof(int); assert(sz <= *bufsize); if (sysctl(po->po_path, po->po_len, &i, &sz, NULL, 0) == -1) { error = errno; break; } if (rflag) memcpy(buf, &i, sz); else snprintf(buf, *bufsize, "%d", i); break; } case CTLTYPE_QUAD: { quad_t q; sz = sizeof(q); assert(sz <= *bufsize); if (sysctl(po->po_path, po->po_len, &q, &sz, NULL, 0) == -1) { error = errno; break; } if (rflag) memcpy(buf, &q, sz); else snprintf(buf, *bufsize, "%" PRId64, q); break; } case CTLTYPE_STRUCT: { uint8_t snode[SFS_MAXFILE/2-1]; unsigned i; sz = sizeof(snode); assert(sz <= *bufsize); if (sysctl(po->po_path, po->po_len, snode, &sz, NULL, 0) == -1){ error = errno; break; } if (rflag) { memcpy(buf, &snode, sz); } else { for (i = 0; i < sz && 2*i < *bufsize; i++) { sprintf(&buf[2*i], "%02x", snode[i]); } buf[2*i] = '\0'; } break; } case CTLTYPE_STRING: { sz = *bufsize; assert(sz <= *bufsize); if (sysctl(po->po_path, po->po_len, buf, &sz, NULL, 0) == -1) { error = errno; break; } break; } default: snprintf(buf, *bufsize, "invalid sysctl CTLTYPE %d", SYSCTL_TYPE(sfs->sysctl_flags)); break; } if (error) { *bufsize = 0; return; } if (rflag) *bufsize = sz; else *bufsize = strlen(buf); }
static int sysctlgetmibinfo_unlocked(const char *gname, int *iname, u_int *namelenp, char *cname, size_t *csz, struct sysctlnode **rnode, int v) #endif /* _REENTRANT */ { struct sysctlnode *pnode, *node; int name[CTL_MAXNAME], n, haven; u_int ni, nl; intmax_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) { if (*rnode == NULL) { /* XXX later deal with dealing back a sub version */ if (v != SYSCTL_VERSION) return (EINVAL); pnode = &sysctl_mibroot; } else { /* this is just someone being silly */ if (SYSCTL_VERS((*rnode)->sysctl_flags) != (uint32_t)v) return (EINVAL); /* XXX later deal with other people's trees */ if (SYSCTL_VERS((*rnode)->sysctl_flags) != SYSCTL_VERSION) return (EINVAL); pnode = *rnode; } } else pnode = &sysctl_mibroot; if (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 > (intptr_t)(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 = strtoimax(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 & CTLFLAG_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); }
/* * 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) { struct sysctlnode qnode; uint32_t 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; memset(&qnode, 0, sizeof(qnode)); qnode.sysctl_flags = SYSCTL_VERSION; rc = sysctl(name, namelen + 1, pnode->sysctl_child, &sz, &qnode, sizeof(qnode)); if (sz == 0) { free(pnode->sysctl_child); pnode->sysctl_child = NULL; return (rc); } if (rc) { free(pnode->sysctl_child); pnode->sysctl_child = NULL; if ((sz % sizeof(struct sysctlnode)) != 0) errno = EINVAL; 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, &qnode, sizeof(qnode)); if (rc) { free(pnode->sysctl_child); pnode->sysctl_child = NULL; return (rc); } } /* * how many did we get? */ sz /= sizeof(struct sysctlnode); pnode->sysctl_csize = pnode->sysctl_clen = (uint32_t)sz; if (pnode->sysctl_clen != sz) { free(pnode->sysctl_child); pnode->sysctl_child = NULL; errno = EINVAL; return (-1); } /* * 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; } pnode->sysctl_child[rc].sysctl_desc = NULL; } return (0); }
/* * 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, qnode; size_t si, so; int rc, name; size_t nlen, olen, ni, oi; uint32_t 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; memset(&qnode, 0, sizeof(qnode)); qnode.sysctl_flags = SYSCTL_VERSION; do { si = so; h = malloc(si); rc = sysctl(&name, 1, h, &so, &qnode, sizeof(qnode)); 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, 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 */ _DIAGASSERT(__type_fit(uint32_t, nlen)); sysctl_mibroot.sysctl_csize = sysctl_mibroot.sysctl_clen = (uint32_t)nlen; sysctl_mibroot.sysctl_child = h; free(o); }
/* Basic name -> sysctl MIB translation */ int _rtld_sysctl(const char *name, void *oldp, size_t *oldlen) { const char *node, *ep; struct sysctlnode query, *result, *newresult; int mib[CTL_MAXNAME], i, r; size_t res_size, n; u_int miblen = 0; /* Start with 16 entries, will grow it up as needed. */ res_size = 16 * sizeof(struct sysctlnode); result = xmalloc(res_size); if (result == NULL) return (-1); ep = name + strlen(name); do { i = -1; while (*name == '/' || *name == '.') name++; if (name >= ep) break; mib[miblen] = CTL_QUERY; memset(&query, 0, sizeof(query)); query.sysctl_flags = SYSCTL_VERSION; n = res_size; if (sysctl(mib, miblen + 1, result, &n, &query, sizeof(query)) == -1) { if (errno != ENOMEM) goto bad; /* Grow up result */ res_size = n; newresult = xrealloc(result, res_size); if (newresult == NULL) goto bad; result = newresult; if (sysctl(mib, miblen + 1, result, &n, &query, sizeof(query)) == -1) goto bad; } n /= sizeof(struct sysctlnode); node = getstr(&name, ep, "./"); for (i = 0; i < n; i++) if (matchstr(result[i].sysctl_name, node, name)) { mib[miblen] = result[i].sysctl_num; miblen++; break; } } while (name < ep && miblen <= CTL_MAXNAME); if (name < ep || i == -1) goto bad; r = SYSCTL_TYPE(result[i].sysctl_flags); xfree(result); if (sysctl(mib, miblen, oldp, oldlen, NULL, 0) == -1) return (-1); return r; bad: xfree(result); return (-1); }