1 /* MIB service - main.c - request abstraction and first-level tree */ 2 /* 3 * This is the Management Information Base (MIB) service. Its one and only 4 * task is to implement the sysctl(2) system call, which plays a fairly 5 * important role in parts of *BSD userland. 6 * 7 * The sysctl(2) interface is used to access a variety of information. In 8 * order to obtain that information, and possibly modify it, the MIB service 9 * calls into many other services. The MIB service must therefore not be 10 * called directly from other services, with the exception of ProcFS. In fact, 11 * ProcFS is currently the only service that is modeled as logically higher in 12 * the MINIX3 service stack than MIB, something that itself is possible only 13 * due to the nonblocking nature of VFS. MIB may issue blocking calls to VFS. 14 * 15 * The MIB service is in the boot image because even init(8) makes use of 16 * sysctl(2) during its own startup, so launching the MIB service at any later 17 * time would make a proper implementation of sysctl(2) impossible. Also, the 18 * service needs superuser privileges because it may need to issue privileged 19 * calls and obtain privileged information from other services. 20 * 21 * The MIB service was created by David van Moolenbroek <david@minix3.org>. 22 */ 23 24 #include "mib.h" 25 26 /* 27 * Most of these initially empty nodes are filled in by their corresponding 28 * modules' _init calls; see mib_init below. However, CTL_USER stays empty: 29 * the libc sysctl(3) wrapper code takes care of that subtree. It must have 30 * an entry here though, or sysctl(8) will not list it. CTL_VENDOR is also 31 * empty, but writable, so that it may be used by third parties. 32 */ 33 static struct mib_node mib_table[] = { 34 /* 1*/ [CTL_KERN] = MIB_ENODE(_P | _RO, "kern", "High kernel"), 35 /* 8*/ [CTL_USER] = MIB_ENODE(_P | _RO, "user", "User-level"), 36 /*11*/ [CTL_VENDOR] = MIB_ENODE(_P | _RW, "vendor", "Vendor specific"), 37 /*32*/ [CTL_MINIX] = MIB_ENODE(_P | _RO, "minix", "MINIX3 specific"), 38 }; 39 40 /* 41 * The root node of the tree. The root node is used internally only--it is 42 * impossible to access the root node itself from userland in any way. The 43 * node is writable by default, so that programs such as init(8) may create 44 * their own top-level entries. 45 */ 46 static struct mib_node mib_root = MIB_NODE(_RW, mib_table, "", ""); 47 48 /* 49 * Structures describing old and new data as provided by userland. The primary 50 * advantage of these opaque structures is that we could in principle use them 51 * to implement storage of small data results in the sysctl reply message, so 52 * as to avoid the kernel copy, without changing any of the handler code. 53 */ 54 struct mib_oldp { 55 endpoint_t oldp_endpt; 56 vir_bytes oldp_addr; 57 size_t oldp_len; 58 }; 59 /* 60 * Same structure, different type: prevent accidental mixups, and avoid the 61 * need to use __restrict everywhere. 62 */ 63 struct mib_newp { 64 endpoint_t newp_endpt; 65 vir_bytes newp_addr; 66 size_t newp_len; 67 }; 68 69 /* 70 * Return TRUE or FALSE indicating whether the given offset is within the range 71 * of data that is to be copied out. This call can be used to test whether 72 * certain bits of data need to be prepared for copying at all. 73 */ 74 int 75 mib_inrange(struct mib_oldp * oldp, size_t off) 76 { 77 78 if (oldp == NULL) 79 return FALSE; 80 81 return (off < oldp->oldp_len); 82 } 83 84 /* 85 * Return the total length of the requested data. This should not be used 86 * directly except in highly unusual cases, such as particular node requests 87 * where the request semantics blatantly violate overall sysctl(2) semantics. 88 */ 89 size_t 90 mib_getoldlen(struct mib_oldp * oldp) 91 { 92 93 if (oldp == NULL) 94 return 0; 95 96 return oldp->oldp_len; 97 } 98 99 /* 100 * Copy out (partial) data to the user. The copy is automatically limited to 101 * the range of data requested by the user. Return the requested length on 102 * success (for the caller's convenience) or an error code on failure. 103 */ 104 ssize_t 105 mib_copyout(struct mib_oldp * __restrict oldp, size_t off, 106 const void * __restrict buf, size_t size) 107 { 108 size_t len; 109 int r; 110 111 len = size; 112 assert(len <= SSIZE_MAX); 113 114 if (oldp == NULL || off >= oldp->oldp_len) 115 return size; /* nothing to do */ 116 117 if (len > oldp->oldp_len - off) 118 len = oldp->oldp_len - off; 119 120 if ((r = sys_datacopy(SELF, (vir_bytes)buf, oldp->oldp_endpt, 121 oldp->oldp_addr + off, len)) != OK) 122 return r; 123 124 return size; 125 } 126 127 /* 128 * Override the oldlen value returned from the call, in situations where an 129 * error is thrown as well. 130 */ 131 void 132 mib_setoldlen(struct mib_call * call, size_t oldlen) 133 { 134 135 call->call_reslen = oldlen; 136 } 137 138 /* 139 * Return the new data length as provided by the user, or 0 if the user did not 140 * supply new data. 141 */ 142 size_t 143 mib_getnewlen(struct mib_newp * newp) 144 { 145 146 if (newp == NULL) 147 return 0; 148 149 return newp->newp_len; 150 } 151 152 /* 153 * Copy in data from the user. The given length must match exactly the length 154 * given by the user. Return OK or an error code. 155 */ 156 int 157 mib_copyin(struct mib_newp * __restrict newp, void * __restrict buf, 158 size_t len) 159 { 160 161 if (newp == NULL || len != newp->newp_len) 162 return EINVAL; 163 164 if (len == 0) 165 return OK; 166 167 return sys_datacopy(newp->newp_endpt, newp->newp_addr, SELF, 168 (vir_bytes)buf, len); 169 } 170 171 /* 172 * Copy in auxiliary data from the user, based on a user pointer obtained from 173 * data copied in earlier through mib_copyin(). 174 */ 175 int 176 mib_copyin_aux(struct mib_newp * __restrict newp, vir_bytes addr, 177 void * __restrict buf, size_t len) 178 { 179 180 assert(newp != NULL); 181 182 if (len == 0) 183 return OK; 184 185 return sys_datacopy(newp->newp_endpt, addr, SELF, (vir_bytes)buf, len); 186 } 187 188 /* 189 * Check whether the user is allowed to perform privileged operations. The 190 * function returns a nonzero value if this is the case, and zero otherwise. 191 * Authorization is performed only once per call. 192 */ 193 int 194 mib_authed(struct mib_call * call) 195 { 196 197 if ((call->call_flags & (MIB_FLAG_AUTH | MIB_FLAG_NOAUTH)) == 0) { 198 /* Ask PM if this endpoint has superuser privileges. */ 199 if (getnuid(call->call_endpt) == SUPER_USER) 200 call->call_flags |= MIB_FLAG_AUTH; 201 else 202 call->call_flags |= MIB_FLAG_NOAUTH; 203 } 204 205 return (call->call_flags & MIB_FLAG_AUTH); 206 } 207 208 /* 209 * Implement the sysctl(2) system call. 210 */ 211 static int 212 mib_sysctl(message * __restrict m_in, message * __restrict m_out) 213 { 214 vir_bytes oldaddr, newaddr; 215 size_t oldlen, newlen; 216 unsigned int namelen; 217 int s, name[CTL_MAXNAME]; 218 endpoint_t endpt; 219 struct mib_oldp oldp, *oldpp; 220 struct mib_newp newp, *newpp; 221 struct mib_call call; 222 ssize_t r; 223 224 endpt = m_in->m_source; 225 oldaddr = m_in->m_lc_mib_sysctl.oldp; 226 oldlen = m_in->m_lc_mib_sysctl.oldlen; 227 newaddr = m_in->m_lc_mib_sysctl.newp; 228 newlen = m_in->m_lc_mib_sysctl.newlen; 229 namelen = m_in->m_lc_mib_sysctl.namelen; 230 231 if (namelen == 0 || namelen > CTL_MAXNAME) 232 return EINVAL; 233 234 /* 235 * In most cases, the entire name fits in the request message, so we 236 * can avoid a kernel copy. 237 */ 238 if (namelen > CTL_SHORTNAME) { 239 if ((s = sys_datacopy(endpt, m_in->m_lc_mib_sysctl.namep, SELF, 240 (vir_bytes)&name, sizeof(name[0]) * namelen)) != OK) 241 return s; 242 } else 243 memcpy(name, m_in->m_lc_mib_sysctl.name, 244 sizeof(name[0]) * namelen); 245 246 /* 247 * Set up a structure for the old data, if any. When no old address is 248 * given, be forgiving if oldlen is not zero, as the user may simply 249 * not have initialized the variable before passing a pointer to it. 250 */ 251 if (oldaddr != 0) { 252 oldp.oldp_endpt = endpt; 253 oldp.oldp_addr = oldaddr; 254 oldp.oldp_len = oldlen; 255 oldpp = &oldp; 256 } else 257 oldpp = NULL; 258 259 /* 260 * Set up a structure for the new data, if any. If one of newaddr and 261 * newlen is zero but not the other, we (like NetBSD) disregard both. 262 */ 263 if (newaddr != 0 && newlen != 0) { 264 newp.newp_endpt = endpt; 265 newp.newp_addr = newaddr; 266 newp.newp_len = newlen; 267 newpp = &newp; 268 } else 269 newpp = NULL; 270 271 /* 272 * Set up a structure for other call parameters. Most of these should 273 * be used rarely, and we may want to add more later, so do not pass 274 * all of them around as actual function parameters all the time. 275 */ 276 call.call_endpt = endpt; 277 call.call_name = name; 278 call.call_namelen = namelen; 279 call.call_flags = 0; 280 call.call_reslen = 0; 281 282 r = mib_dispatch(&call, &mib_root, oldpp, newpp); 283 284 /* 285 * From NetBSD: we copy out as much as we can from the old data, while 286 * at the same time computing the full data length. Then, here at the 287 * end, if the entire result did not fit in the destination buffer, we 288 * return ENOMEM instead of success, thus also returning a partial 289 * result and the full data length. 290 * 291 * It is also possible that data are copied out along with a "real" 292 * error. In that case, we must report a nonzero resulting length 293 * along with that error code. This is currently the case when node 294 * creation resulted in a collision, in which case the error code is 295 * EEXIST while the existing node is copied out as well. 296 */ 297 if (r >= 0) { 298 m_out->m_mib_lc_sysctl.oldlen = (size_t)r; 299 300 if (oldaddr != 0 && oldlen < (size_t)r) 301 r = ENOMEM; 302 else 303 r = OK; 304 } else 305 m_out->m_mib_lc_sysctl.oldlen = call.call_reslen; 306 307 return r; 308 } 309 310 /* 311 * Initialize the service. 312 */ 313 static int 314 mib_init(int type __unused, sef_init_info_t * info __unused) 315 { 316 317 /* 318 * Initialize pointers and sizes of subtrees in different modules. 319 * This is needed because we cannot use sizeof on external arrays. 320 * We do initialize the node entry (including any other fields) 321 * statically through MIB_ENODE because that forces the array to be 322 * large enough to store the entry. 323 */ 324 mib_kern_init(&mib_table[CTL_KERN]); 325 mib_minix_init(&mib_table[CTL_MINIX]); 326 327 /* 328 * Now that the static tree is complete, go through the entire tree, 329 * initializing miscellaneous fields. 330 */ 331 mib_tree_init(&mib_root); 332 333 return OK; 334 } 335 336 /* 337 * Perform SEF startup. 338 */ 339 static void 340 mib_startup(void) 341 { 342 343 sef_setcb_init_fresh(mib_init); 344 /* 345 * If we restart we lose all dynamic state, which means we lose all 346 * nodes that have been created at run time. However, running with 347 * only the static node tree is still better than not running at all. 348 */ 349 sef_setcb_init_restart(mib_init); 350 351 sef_startup(); 352 } 353 354 /* 355 * The Management Information Base (MIB) service. 356 */ 357 int 358 main(void) 359 { 360 message m_in, m_out; 361 int r, ipc_status; 362 363 /* Perform initialization. */ 364 mib_startup(); 365 366 /* The main message loop. */ 367 for (;;) { 368 /* Receive a request. */ 369 if ((r = sef_receive_status(ANY, &m_in, &ipc_status)) != OK) 370 panic("sef_receive failed: %d", r); 371 372 /* Process the request. */ 373 if (is_ipc_notify(ipc_status)) { 374 /* We are not expecting any notifications. */ 375 printf("MIB: notification from %d\n", m_in.m_source); 376 377 continue; 378 } 379 380 memset(&m_out, 0, sizeof(m_out)); 381 382 switch (m_in.m_type) { 383 case MIB_SYSCTL: 384 r = mib_sysctl(&m_in, &m_out); 385 386 break; 387 388 default: 389 r = ENOSYS; 390 } 391 392 /* Send the reply. */ 393 m_out.m_type = r; 394 395 if ((r = ipc_sendnb(m_in.m_source, &m_out)) != OK) 396 printf("MIB: ipc_sendnb failed (%d)\n", r); 397 } 398 399 /* NOTREACHED */ 400 return 0; 401 } 402