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