1 /* $NetBSD: procfs_subr.c,v 1.67 2005/12/11 12:24:51 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Jan-Simon Pendry. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * @(#)procfs_subr.c 8.6 (Berkeley) 5/14/95 35 */ 36 37 /* 38 * Copyright (c) 1994 Christopher G. Demetriou. All rights reserved. 39 * Copyright (c) 1993 Jan-Simon Pendry 40 * 41 * This code is derived from software contributed to Berkeley by 42 * Jan-Simon Pendry. 43 * 44 * Redistribution and use in source and binary forms, with or without 45 * modification, are permitted provided that the following conditions 46 * are met: 47 * 1. Redistributions of source code must retain the above copyright 48 * notice, this list of conditions and the following disclaimer. 49 * 2. Redistributions in binary form must reproduce the above copyright 50 * notice, this list of conditions and the following disclaimer in the 51 * documentation and/or other materials provided with the distribution. 52 * 3. All advertising materials mentioning features or use of this software 53 * must display the following acknowledgement: 54 * This product includes software developed by the University of 55 * California, Berkeley and its contributors. 56 * 4. Neither the name of the University nor the names of its contributors 57 * may be used to endorse or promote products derived from this software 58 * without specific prior written permission. 59 * 60 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 61 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 62 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 63 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 64 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 65 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 66 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 67 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 68 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 69 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 70 * SUCH DAMAGE. 71 * 72 * @(#)procfs_subr.c 8.6 (Berkeley) 5/14/95 73 */ 74 75 #include <sys/cdefs.h> 76 __KERNEL_RCSID(0, "$NetBSD: procfs_subr.c,v 1.67 2005/12/11 12:24:51 christos Exp $"); 77 78 #include <sys/param.h> 79 #include <sys/systm.h> 80 #include <sys/time.h> 81 #include <sys/kernel.h> 82 #include <sys/proc.h> 83 #include <sys/vnode.h> 84 #include <sys/malloc.h> 85 #include <sys/stat.h> 86 #include <sys/file.h> 87 #include <sys/filedesc.h> 88 89 #include <miscfs/procfs/procfs.h> 90 91 void procfs_hashins(struct pfsnode *); 92 void procfs_hashrem(struct pfsnode *); 93 struct vnode *procfs_hashget(pid_t, pfstype, int, struct mount *); 94 95 LIST_HEAD(pfs_hashhead, pfsnode) *pfs_hashtbl; 96 u_long pfs_ihash; /* size of hash table - 1 */ 97 #define PFSPIDHASH(pid) ((pid) & pfs_ihash) 98 99 struct lock pfs_hashlock; 100 struct simplelock pfs_hash_slock; 101 102 #define ISSET(t, f) ((t) & (f)) 103 104 /* 105 * allocate a pfsnode/vnode pair. the vnode is 106 * referenced, and locked. 107 * 108 * the pid, pfs_type, and mount point uniquely 109 * identify a pfsnode. the mount point is needed 110 * because someone might mount this filesystem 111 * twice. 112 * 113 * all pfsnodes are maintained on a singly-linked 114 * list. new nodes are only allocated when they cannot 115 * be found on this list. entries on the list are 116 * removed when the vfs reclaim entry is called. 117 * 118 * a single lock is kept for the entire list. this is 119 * needed because the getnewvnode() function can block 120 * waiting for a vnode to become free, in which case there 121 * may be more than one process trying to get the same 122 * vnode. this lock is only taken if we are going to 123 * call getnewvnode, since the kernel itself is single-threaded. 124 * 125 * if an entry is found on the list, then call vget() to 126 * take a reference. this is done because there may be 127 * zero references to it and so it needs to removed from 128 * the vnode free list. 129 */ 130 int 131 procfs_allocvp(mp, vpp, pid, pfs_type, fd) 132 struct mount *mp; 133 struct vnode **vpp; 134 pid_t pid; 135 pfstype pfs_type; 136 int fd; 137 { 138 struct pfsnode *pfs; 139 struct vnode *vp; 140 int error; 141 142 do { 143 if ((*vpp = procfs_hashget(pid, pfs_type, fd, mp)) != NULL) 144 return (0); 145 } while (lockmgr(&pfs_hashlock, LK_EXCLUSIVE|LK_SLEEPFAIL, 0)); 146 147 if ((error = getnewvnode(VT_PROCFS, mp, procfs_vnodeop_p, &vp)) != 0) { 148 *vpp = NULL; 149 lockmgr(&pfs_hashlock, LK_RELEASE, NULL); 150 return (error); 151 } 152 153 MALLOC(pfs, void *, sizeof(struct pfsnode), M_TEMP, M_WAITOK); 154 vp->v_data = pfs; 155 156 pfs->pfs_pid = pid; 157 pfs->pfs_type = pfs_type; 158 pfs->pfs_vnode = vp; 159 pfs->pfs_flags = 0; 160 pfs->pfs_fileno = PROCFS_FILENO(pid, pfs_type, fd); 161 pfs->pfs_fd = fd; 162 163 switch (pfs_type) { 164 case PFSroot: /* /proc = dr-xr-xr-x */ 165 pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; 166 vp->v_type = VDIR; 167 vp->v_flag = VROOT; 168 break; 169 170 case PFScurproc: /* /proc/curproc = lr-xr-xr-x */ 171 case PFSself: /* /proc/self = lr-xr-xr-x */ 172 case PFScwd: /* /proc/N/cwd = lr-xr-xr-x */ 173 case PFSchroot: /* /proc/N/chroot = lr-xr-xr-x */ 174 pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; 175 vp->v_type = VLNK; 176 break; 177 178 case PFSproc: /* /proc/N = dr-xr-xr-x */ 179 case PFSfd: 180 if (fd == -1) { /* /proc/N/fd = dr-xr-xr-x */ 181 pfs->pfs_mode = S_IRUSR|S_IXUSR; 182 vp->v_type = VDIR; 183 } else { /* /proc/N/fd/M = [ps-]rw------- */ 184 struct file *fp; 185 struct vnode *vxp; 186 struct proc *pown; 187 188 /* XXX can procfs_getfp() ever fail here? */ 189 if ((error = procfs_getfp(pfs, &pown, &fp)) != 0) 190 goto bad; 191 FILE_USE(fp); 192 193 pfs->pfs_mode = S_IRUSR|S_IWUSR; 194 switch (fp->f_type) { 195 case DTYPE_VNODE: 196 vxp = (struct vnode *)fp->f_data; 197 198 /* 199 * We make symlinks for directories 200 * to avoid cycles. 201 */ 202 if (vxp->v_type == VDIR) 203 goto symlink; 204 vp->v_type = vxp->v_type; 205 break; 206 case DTYPE_PIPE: 207 vp->v_type = VFIFO; 208 break; 209 case DTYPE_SOCKET: 210 vp->v_type = VSOCK; 211 break; 212 case DTYPE_KQUEUE: 213 case DTYPE_MISC: 214 symlink: 215 pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP| 216 S_IXGRP|S_IROTH|S_IXOTH; 217 vp->v_type = VLNK; 218 break; 219 default: 220 error = EOPNOTSUPP; 221 FILE_UNUSE(fp, proc_representative_lwp(pown)); 222 goto bad; 223 } 224 FILE_UNUSE(fp, proc_representative_lwp(pown)); 225 } 226 break; 227 228 case PFSfile: /* /proc/N/file = -rw------- */ 229 case PFSmem: /* /proc/N/mem = -rw------- */ 230 case PFSregs: /* /proc/N/regs = -rw------- */ 231 case PFSfpregs: /* /proc/N/fpregs = -rw------- */ 232 pfs->pfs_mode = S_IRUSR|S_IWUSR; 233 vp->v_type = VREG; 234 break; 235 236 case PFSctl: /* /proc/N/ctl = --w------ */ 237 case PFSnote: /* /proc/N/note = --w------ */ 238 case PFSnotepg: /* /proc/N/notepg = --w------ */ 239 pfs->pfs_mode = S_IWUSR; 240 vp->v_type = VREG; 241 break; 242 243 case PFSmap: /* /proc/N/map = -r--r--r-- */ 244 case PFSmaps: /* /proc/N/maps = -r--r--r-- */ 245 case PFSstatus: /* /proc/N/status = -r--r--r-- */ 246 case PFSstat: /* /proc/N/stat = -r--r--r-- */ 247 case PFScmdline: /* /proc/N/cmdline = -r--r--r-- */ 248 case PFSmeminfo: /* /proc/meminfo = -r--r--r-- */ 249 case PFScpuinfo: /* /proc/cpuinfo = -r--r--r-- */ 250 case PFSuptime: /* /proc/uptime = -r--r--r-- */ 251 case PFSmounts: /* /proc/mounts = -r--r--r-- */ 252 pfs->pfs_mode = S_IRUSR|S_IRGRP|S_IROTH; 253 vp->v_type = VREG; 254 break; 255 256 #ifdef __HAVE_PROCFS_MACHDEP 257 PROCFS_MACHDEP_NODETYPE_CASES 258 procfs_machdep_allocvp(vp); 259 break; 260 #endif 261 262 default: 263 panic("procfs_allocvp"); 264 } 265 266 procfs_hashins(pfs); 267 uvm_vnp_setsize(vp, 0); 268 lockmgr(&pfs_hashlock, LK_RELEASE, NULL); 269 270 *vpp = vp; 271 return (0); 272 273 bad: 274 lockmgr(&pfs_hashlock, LK_RELEASE, NULL); 275 FREE(pfs, M_TEMP); 276 ungetnewvnode(vp); 277 return (error); 278 } 279 280 int 281 procfs_freevp(vp) 282 struct vnode *vp; 283 { 284 struct pfsnode *pfs = VTOPFS(vp); 285 286 procfs_hashrem(pfs); 287 288 FREE(vp->v_data, M_TEMP); 289 vp->v_data = 0; 290 return (0); 291 } 292 293 int 294 procfs_rw(v) 295 void *v; 296 { 297 struct vop_read_args *ap = v; 298 struct vnode *vp = ap->a_vp; 299 struct uio *uio = ap->a_uio; 300 struct lwp *curl; 301 struct lwp *l; 302 struct pfsnode *pfs = VTOPFS(vp); 303 struct proc *p; 304 305 if (uio->uio_offset < 0) 306 return EINVAL; 307 p = PFIND(pfs->pfs_pid); 308 if (p == 0) 309 return ESRCH; 310 /* 311 * Do not allow init to be modified while in secure mode; it 312 * could be duped into changing the security level. 313 */ 314 if (uio->uio_rw == UIO_WRITE && p == initproc && securelevel > -1) 315 return EPERM; 316 317 curl = uio->uio_lwp; 318 319 /* XXX NJWLWP 320 * The entire procfs interface needs work to be useful to 321 * a process with multiple LWPs. For the moment, we'll 322 * just kluge this and fail on others. 323 */ 324 l = proc_representative_lwp(p); 325 326 switch (pfs->pfs_type) { 327 case PFSnote: 328 case PFSnotepg: 329 return (procfs_donote(curl, p, pfs, uio)); 330 331 case PFSregs: 332 return (procfs_doregs(curl, l, pfs, uio)); 333 334 case PFSfpregs: 335 return (procfs_dofpregs(curl, l, pfs, uio)); 336 337 case PFSctl: 338 return (procfs_doctl(curl, l, pfs, uio)); 339 340 case PFSstatus: 341 return (procfs_dostatus(curl, l, pfs, uio)); 342 343 case PFSstat: 344 return (procfs_do_pid_stat(curl, l, pfs, uio)); 345 346 case PFSmap: 347 return (procfs_domap(curl, p, pfs, uio, 0)); 348 349 case PFSmaps: 350 return (procfs_domap(curl, p, pfs, uio, 1)); 351 352 case PFSmem: 353 return (procfs_domem(curl, l, pfs, uio)); 354 355 case PFScmdline: 356 return (procfs_docmdline(curl, p, pfs, uio)); 357 358 case PFSmeminfo: 359 return (procfs_domeminfo(curl, p, pfs, uio)); 360 361 case PFScpuinfo: 362 return (procfs_docpuinfo(curl, p, pfs, uio)); 363 364 case PFSfd: 365 return (procfs_dofd(curl, p, pfs, uio)); 366 367 case PFSuptime: 368 return (procfs_douptime(curl, p, pfs, uio)); 369 370 case PFSmounts: 371 return (procfs_domounts(curl, p, pfs, uio)); 372 373 #ifdef __HAVE_PROCFS_MACHDEP 374 PROCFS_MACHDEP_NODETYPE_CASES 375 return (procfs_machdep_rw(curl, l, pfs, uio)); 376 #endif 377 378 default: 379 return (EOPNOTSUPP); 380 } 381 } 382 383 /* 384 * Get a string from userland into (bf). Strip a trailing 385 * nl character (to allow easy access from the shell). 386 * The buffer should be *buflenp + 1 chars long. vfs_getuserstr 387 * will automatically add a nul char at the end. 388 * 389 * Returns 0 on success or the following errors 390 * 391 * EINVAL: file offset is non-zero. 392 * EMSGSIZE: message is longer than kernel buffer 393 * EFAULT: user i/o buffer is not addressable 394 */ 395 int 396 vfs_getuserstr(uio, bf, buflenp) 397 struct uio *uio; 398 char *bf; 399 int *buflenp; 400 { 401 int xlen; 402 int error; 403 404 if (uio->uio_offset != 0) 405 return (EINVAL); 406 407 xlen = *buflenp; 408 409 /* must be able to read the whole string in one go */ 410 if (xlen < uio->uio_resid) 411 return (EMSGSIZE); 412 xlen = uio->uio_resid; 413 414 if ((error = uiomove(bf, xlen, uio)) != 0) 415 return (error); 416 417 /* allow multiple writes without seeks */ 418 uio->uio_offset = 0; 419 420 /* cleanup string and remove trailing newline */ 421 bf[xlen] = '\0'; 422 xlen = strlen(bf); 423 if (xlen > 0 && bf[xlen-1] == '\n') 424 bf[--xlen] = '\0'; 425 *buflenp = xlen; 426 427 return (0); 428 } 429 430 const vfs_namemap_t * 431 vfs_findname(nm, bf, buflen) 432 const vfs_namemap_t *nm; 433 const char *bf; 434 int buflen; 435 { 436 437 for (; nm->nm_name; nm++) 438 if (memcmp(bf, nm->nm_name, buflen+1) == 0) 439 return (nm); 440 441 return (0); 442 } 443 444 /* 445 * Initialize pfsnode hash table. 446 */ 447 void 448 procfs_hashinit() 449 { 450 lockinit(&pfs_hashlock, PINOD, "pfs_hashlock", 0, 0); 451 pfs_hashtbl = hashinit(desiredvnodes / 4, HASH_LIST, M_UFSMNT, 452 M_WAITOK, &pfs_ihash); 453 simple_lock_init(&pfs_hash_slock); 454 } 455 456 void 457 procfs_hashreinit() 458 { 459 struct pfsnode *pp; 460 struct pfs_hashhead *oldhash, *hash; 461 u_long i, oldmask, mask, val; 462 463 hash = hashinit(desiredvnodes / 4, HASH_LIST, M_UFSMNT, M_WAITOK, 464 &mask); 465 466 simple_lock(&pfs_hash_slock); 467 oldhash = pfs_hashtbl; 468 oldmask = pfs_ihash; 469 pfs_hashtbl = hash; 470 pfs_ihash = mask; 471 for (i = 0; i <= oldmask; i++) { 472 while ((pp = LIST_FIRST(&oldhash[i])) != NULL) { 473 LIST_REMOVE(pp, pfs_hash); 474 val = PFSPIDHASH(pp->pfs_pid); 475 LIST_INSERT_HEAD(&hash[val], pp, pfs_hash); 476 } 477 } 478 simple_unlock(&pfs_hash_slock); 479 hashdone(oldhash, M_UFSMNT); 480 } 481 482 /* 483 * Free pfsnode hash table. 484 */ 485 void 486 procfs_hashdone() 487 { 488 hashdone(pfs_hashtbl, M_UFSMNT); 489 } 490 491 struct vnode * 492 procfs_hashget(pid, type, fd, mp) 493 pid_t pid; 494 pfstype type; 495 int fd; 496 struct mount *mp; 497 { 498 struct pfs_hashhead *ppp; 499 struct pfsnode *pp; 500 struct vnode *vp; 501 502 loop: 503 simple_lock(&pfs_hash_slock); 504 ppp = &pfs_hashtbl[PFSPIDHASH(pid)]; 505 LIST_FOREACH(pp, ppp, pfs_hash) { 506 vp = PFSTOV(pp); 507 if (pid == pp->pfs_pid && pp->pfs_type == type && 508 pp->pfs_fd == fd && vp->v_mount == mp) { 509 simple_lock(&vp->v_interlock); 510 simple_unlock(&pfs_hash_slock); 511 if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK)) 512 goto loop; 513 return (vp); 514 } 515 } 516 simple_unlock(&pfs_hash_slock); 517 return (NULL); 518 } 519 520 /* 521 * Insert the pfsnode into the hash table and lock it. 522 */ 523 void 524 procfs_hashins(pp) 525 struct pfsnode *pp; 526 { 527 struct pfs_hashhead *ppp; 528 529 /* lock the pfsnode, then put it on the appropriate hash list */ 530 lockmgr(&pp->pfs_vnode->v_lock, LK_EXCLUSIVE, (struct simplelock *)0); 531 532 simple_lock(&pfs_hash_slock); 533 ppp = &pfs_hashtbl[PFSPIDHASH(pp->pfs_pid)]; 534 LIST_INSERT_HEAD(ppp, pp, pfs_hash); 535 simple_unlock(&pfs_hash_slock); 536 } 537 538 /* 539 * Remove the pfsnode from the hash table. 540 */ 541 void 542 procfs_hashrem(pp) 543 struct pfsnode *pp; 544 { 545 simple_lock(&pfs_hash_slock); 546 LIST_REMOVE(pp, pfs_hash); 547 simple_unlock(&pfs_hash_slock); 548 } 549 550 void 551 procfs_revoke_vnodes(p, arg) 552 struct proc *p; 553 void *arg; 554 { 555 struct pfsnode *pfs, *pnext; 556 struct vnode *vp; 557 struct mount *mp = (struct mount *)arg; 558 struct pfs_hashhead *ppp; 559 560 if (!(p->p_flag & P_SUGID)) 561 return; 562 563 ppp = &pfs_hashtbl[PFSPIDHASH(p->p_pid)]; 564 for (pfs = LIST_FIRST(ppp); pfs; pfs = pnext) { 565 vp = PFSTOV(pfs); 566 pnext = LIST_NEXT(pfs, pfs_hash); 567 if (vp->v_usecount > 0 && pfs->pfs_pid == p->p_pid && 568 vp->v_mount == mp) 569 VOP_REVOKE(vp, REVOKEALL); 570 } 571 } 572 573 int 574 procfs_getfp(pfs, pown, fp) 575 struct pfsnode *pfs; 576 struct proc **pown; 577 struct file **fp; 578 { 579 struct proc *p = PFIND(pfs->pfs_pid); 580 581 if (p == NULL) 582 return ESRCH; 583 584 if (pfs->pfs_fd == -1) 585 return EINVAL; 586 587 if ((*fp = fd_getfile(p->p_fd, pfs->pfs_fd)) == NULL) 588 return EBADF; 589 590 *pown = p; 591 return 0; 592 } 593