1 /* $NetBSD: procfs_subr.c,v 1.59 2004/05/14 16:35:24 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.59 2004/05/14 16:35:24 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 __P((struct pfsnode *)); 92 void procfs_hashrem __P((struct pfsnode *)); 93 struct vnode *procfs_hashget __P((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 pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; 173 vp->v_type = VLNK; 174 break; 175 176 case PFSproc: /* /proc/N = dr-xr-xr-x */ 177 case PFSfd: 178 if (fd == -1) { /* /proc/N/fd = dr-xr-xr-x */ 179 pfs->pfs_mode = S_IRUSR|S_IXUSR; 180 vp->v_type = VDIR; 181 } else { /* /proc/N/fd/M = [ps-]rw------- */ 182 struct file *fp; 183 struct vnode *vxp; 184 struct proc *pown; 185 186 /* XXX can procfs_getfp() ever fail here? */ 187 if ((error = procfs_getfp(pfs, &pown, &fp)) != 0) 188 goto bad; 189 FILE_USE(fp); 190 191 pfs->pfs_mode = S_IRUSR|S_IWUSR; 192 switch (fp->f_type) { 193 case DTYPE_VNODE: 194 vxp = (struct vnode *)fp->f_data; 195 196 /* 197 * We make symlinks for directories 198 * to avoid cycles. 199 */ 200 if (vxp->v_type == VDIR) 201 goto symlink; 202 vp->v_type = vxp->v_type; 203 break; 204 case DTYPE_PIPE: 205 vp->v_type = VFIFO; 206 break; 207 case DTYPE_SOCKET: 208 vp->v_type = VSOCK; 209 break; 210 case DTYPE_KQUEUE: 211 case DTYPE_MISC: 212 symlink: 213 pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP| 214 S_IXGRP|S_IROTH|S_IXOTH; 215 vp->v_type = VLNK; 216 break; 217 default: 218 error = EOPNOTSUPP; 219 FILE_UNUSE(fp, pown); 220 goto bad; 221 } 222 FILE_UNUSE(fp, pown); 223 } 224 break; 225 226 case PFSfile: /* /proc/N/file = -rw------- */ 227 case PFSmem: /* /proc/N/mem = -rw------- */ 228 case PFSregs: /* /proc/N/regs = -rw------- */ 229 case PFSfpregs: /* /proc/N/fpregs = -rw------- */ 230 pfs->pfs_mode = S_IRUSR|S_IWUSR; 231 vp->v_type = VREG; 232 break; 233 234 case PFSctl: /* /proc/N/ctl = --w------ */ 235 case PFSnote: /* /proc/N/note = --w------ */ 236 case PFSnotepg: /* /proc/N/notepg = --w------ */ 237 pfs->pfs_mode = S_IWUSR; 238 vp->v_type = VREG; 239 break; 240 241 case PFSmap: /* /proc/N/map = -r--r--r-- */ 242 case PFSmaps: /* /proc/N/maps = -r--r--r-- */ 243 case PFSstatus: /* /proc/N/status = -r--r--r-- */ 244 case PFSstat: /* /proc/N/stat = -r--r--r-- */ 245 case PFScmdline: /* /proc/N/cmdline = -r--r--r-- */ 246 case PFSmeminfo: /* /proc/meminfo = -r--r--r-- */ 247 case PFScpuinfo: /* /proc/cpuinfo = -r--r--r-- */ 248 case PFSuptime: /* /proc/uptime = -r--r--r-- */ 249 pfs->pfs_mode = S_IRUSR|S_IRGRP|S_IROTH; 250 vp->v_type = VREG; 251 break; 252 253 #ifdef __HAVE_PROCFS_MACHDEP 254 PROCFS_MACHDEP_NODETYPE_CASES 255 procfs_machdep_allocvp(vp); 256 break; 257 #endif 258 259 default: 260 panic("procfs_allocvp"); 261 } 262 263 procfs_hashins(pfs); 264 uvm_vnp_setsize(vp, 0); 265 lockmgr(&pfs_hashlock, LK_RELEASE, NULL); 266 267 *vpp = vp; 268 return (0); 269 270 bad: 271 lockmgr(&pfs_hashlock, LK_RELEASE, NULL); 272 FREE(pfs, M_TEMP); 273 ungetnewvnode(vp); 274 return (error); 275 } 276 277 int 278 procfs_freevp(vp) 279 struct vnode *vp; 280 { 281 struct pfsnode *pfs = VTOPFS(vp); 282 283 procfs_hashrem(pfs); 284 285 FREE(vp->v_data, M_TEMP); 286 vp->v_data = 0; 287 return (0); 288 } 289 290 int 291 procfs_rw(v) 292 void *v; 293 { 294 struct vop_read_args *ap = v; 295 struct vnode *vp = ap->a_vp; 296 struct uio *uio = ap->a_uio; 297 struct proc *curp = uio->uio_procp; 298 struct pfsnode *pfs = VTOPFS(vp); 299 struct lwp *l; 300 struct proc *p; 301 302 if (uio->uio_offset < 0) 303 return EINVAL; 304 p = PFIND(pfs->pfs_pid); 305 if (p == 0) 306 return ESRCH; 307 /* 308 * Do not allow init to be modified while in secure mode; it 309 * could be duped into changing the security level. 310 */ 311 if (uio->uio_rw == UIO_WRITE && p == initproc && securelevel > -1) 312 return EPERM; 313 314 /* XXX NJWLWP 315 * The entire procfs interface needs work to be useful to 316 * a process with multiple LWPs. For the moment, we'll 317 * just kluge this and fail on others. 318 */ 319 l = proc_representative_lwp(p); 320 321 switch (pfs->pfs_type) { 322 case PFSnote: 323 case PFSnotepg: 324 return (procfs_donote(curp, p, pfs, uio)); 325 326 case PFSregs: 327 return (procfs_doregs(curp, l, pfs, uio)); 328 329 case PFSfpregs: 330 return (procfs_dofpregs(curp, l, pfs, uio)); 331 332 case PFSctl: 333 return (procfs_doctl(curp, l, pfs, uio)); 334 335 case PFSstatus: 336 return (procfs_dostatus(curp, l, pfs, uio)); 337 338 case PFSstat: 339 return (procfs_do_pid_stat(curp, l, pfs, uio)); 340 341 case PFSmap: 342 return (procfs_domap(curp, p, pfs, uio, 0)); 343 344 case PFSmaps: 345 return (procfs_domap(curp, p, pfs, uio, 1)); 346 347 case PFSmem: 348 return (procfs_domem(curp, p, pfs, uio)); 349 350 case PFScmdline: 351 return (procfs_docmdline(curp, p, pfs, uio)); 352 353 case PFSmeminfo: 354 return (procfs_domeminfo(curp, p, pfs, uio)); 355 356 case PFScpuinfo: 357 return (procfs_docpuinfo(curp, p, pfs, uio)); 358 359 case PFSfd: 360 return (procfs_dofd(curp, p, pfs, uio)); 361 362 case PFSuptime: 363 return (procfs_douptime(curp, p, pfs, uio)); 364 365 #ifdef __HAVE_PROCFS_MACHDEP 366 PROCFS_MACHDEP_NODETYPE_CASES 367 return (procfs_machdep_rw(curp, l, pfs, uio)); 368 #endif 369 370 default: 371 return (EOPNOTSUPP); 372 } 373 } 374 375 /* 376 * Get a string from userland into (buf). Strip a trailing 377 * nl character (to allow easy access from the shell). 378 * The buffer should be *buflenp + 1 chars long. vfs_getuserstr 379 * will automatically add a nul char at the end. 380 * 381 * Returns 0 on success or the following errors 382 * 383 * EINVAL: file offset is non-zero. 384 * EMSGSIZE: message is longer than kernel buffer 385 * EFAULT: user i/o buffer is not addressable 386 */ 387 int 388 vfs_getuserstr(uio, buf, buflenp) 389 struct uio *uio; 390 char *buf; 391 int *buflenp; 392 { 393 int xlen; 394 int error; 395 396 if (uio->uio_offset != 0) 397 return (EINVAL); 398 399 xlen = *buflenp; 400 401 /* must be able to read the whole string in one go */ 402 if (xlen < uio->uio_resid) 403 return (EMSGSIZE); 404 xlen = uio->uio_resid; 405 406 if ((error = uiomove(buf, xlen, uio)) != 0) 407 return (error); 408 409 /* allow multiple writes without seeks */ 410 uio->uio_offset = 0; 411 412 /* cleanup string and remove trailing newline */ 413 buf[xlen] = '\0'; 414 xlen = strlen(buf); 415 if (xlen > 0 && buf[xlen-1] == '\n') 416 buf[--xlen] = '\0'; 417 *buflenp = xlen; 418 419 return (0); 420 } 421 422 const vfs_namemap_t * 423 vfs_findname(nm, buf, buflen) 424 const vfs_namemap_t *nm; 425 const char *buf; 426 int buflen; 427 { 428 429 for (; nm->nm_name; nm++) 430 if (memcmp(buf, nm->nm_name, buflen+1) == 0) 431 return (nm); 432 433 return (0); 434 } 435 436 /* 437 * Initialize pfsnode hash table. 438 */ 439 void 440 procfs_hashinit() 441 { 442 lockinit(&pfs_hashlock, PINOD, "pfs_hashlock", 0, 0); 443 pfs_hashtbl = hashinit(desiredvnodes / 4, HASH_LIST, M_UFSMNT, 444 M_WAITOK, &pfs_ihash); 445 simple_lock_init(&pfs_hash_slock); 446 } 447 448 void 449 procfs_hashreinit() 450 { 451 struct pfsnode *pp; 452 struct pfs_hashhead *oldhash, *hash; 453 u_long i, oldmask, mask, val; 454 455 hash = hashinit(desiredvnodes / 4, HASH_LIST, M_UFSMNT, M_WAITOK, 456 &mask); 457 458 simple_lock(&pfs_hash_slock); 459 oldhash = pfs_hashtbl; 460 oldmask = pfs_ihash; 461 pfs_hashtbl = hash; 462 pfs_ihash = mask; 463 for (i = 0; i <= oldmask; i++) { 464 while ((pp = LIST_FIRST(&oldhash[i])) != NULL) { 465 LIST_REMOVE(pp, pfs_hash); 466 val = PFSPIDHASH(pp->pfs_pid); 467 LIST_INSERT_HEAD(&hash[val], pp, pfs_hash); 468 } 469 } 470 simple_unlock(&pfs_hash_slock); 471 hashdone(oldhash, M_UFSMNT); 472 } 473 474 /* 475 * Free pfsnode hash table. 476 */ 477 void 478 procfs_hashdone() 479 { 480 hashdone(pfs_hashtbl, M_UFSMNT); 481 } 482 483 struct vnode * 484 procfs_hashget(pid, type, fd, mp) 485 pid_t pid; 486 pfstype type; 487 int fd; 488 struct mount *mp; 489 { 490 struct pfs_hashhead *ppp; 491 struct pfsnode *pp; 492 struct vnode *vp; 493 494 loop: 495 simple_lock(&pfs_hash_slock); 496 ppp = &pfs_hashtbl[PFSPIDHASH(pid)]; 497 LIST_FOREACH(pp, ppp, pfs_hash) { 498 vp = PFSTOV(pp); 499 if (pid == pp->pfs_pid && pp->pfs_type == type && 500 pp->pfs_fd == fd && vp->v_mount == mp) { 501 simple_lock(&vp->v_interlock); 502 simple_unlock(&pfs_hash_slock); 503 if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK)) 504 goto loop; 505 return (vp); 506 } 507 } 508 simple_unlock(&pfs_hash_slock); 509 return (NULL); 510 } 511 512 /* 513 * Insert the pfsnode into the hash table and lock it. 514 */ 515 void 516 procfs_hashins(pp) 517 struct pfsnode *pp; 518 { 519 struct pfs_hashhead *ppp; 520 521 /* lock the pfsnode, then put it on the appropriate hash list */ 522 lockmgr(&pp->pfs_vnode->v_lock, LK_EXCLUSIVE, (struct simplelock *)0); 523 524 simple_lock(&pfs_hash_slock); 525 ppp = &pfs_hashtbl[PFSPIDHASH(pp->pfs_pid)]; 526 LIST_INSERT_HEAD(ppp, pp, pfs_hash); 527 simple_unlock(&pfs_hash_slock); 528 } 529 530 /* 531 * Remove the pfsnode from the hash table. 532 */ 533 void 534 procfs_hashrem(pp) 535 struct pfsnode *pp; 536 { 537 simple_lock(&pfs_hash_slock); 538 LIST_REMOVE(pp, pfs_hash); 539 simple_unlock(&pfs_hash_slock); 540 } 541 542 void 543 procfs_revoke_vnodes(p, arg) 544 struct proc *p; 545 void *arg; 546 { 547 struct pfsnode *pfs, *pnext; 548 struct vnode *vp; 549 struct mount *mp = (struct mount *)arg; 550 struct pfs_hashhead *ppp; 551 552 if (!(p->p_flag & P_SUGID)) 553 return; 554 555 ppp = &pfs_hashtbl[PFSPIDHASH(p->p_pid)]; 556 for (pfs = LIST_FIRST(ppp); pfs; pfs = pnext) { 557 vp = PFSTOV(pfs); 558 pnext = LIST_NEXT(pfs, pfs_hash); 559 if (vp->v_usecount > 0 && pfs->pfs_pid == p->p_pid && 560 vp->v_mount == mp) 561 VOP_REVOKE(vp, REVOKEALL); 562 } 563 } 564 565 int 566 procfs_getfp(pfs, pown, fp) 567 struct pfsnode *pfs; 568 struct proc **pown; 569 struct file **fp; 570 { 571 struct proc *p = PFIND(pfs->pfs_pid); 572 573 if (p == NULL) 574 return ESRCH; 575 576 if (pfs->pfs_fd == -1) 577 return EINVAL; 578 579 if ((*fp = fd_getfile(p->p_fd, pfs->pfs_fd)) == NULL) 580 return EBADF; 581 582 *pown = p; 583 return 0; 584 } 585