1 /* $NetBSD: procfs_subr.c,v 1.94 2009/03/14 15:36:23 dsl Exp $ */
4 * Copyright (c) 2006, 2007, 2008 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
34 * The Regents of the University of California. All rights reserved.
36 * This code is derived from software contributed to Berkeley by
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
42 * 1. Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 * 3. Neither the name of the University nor the names of its contributors
48 * may be used to endorse or promote products derived from this software
49 * without specific prior written permission.
51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63 * @(#)procfs_subr.c 8.6 (Berkeley) 5/14/95
67 * Copyright (c) 1994 Christopher G. Demetriou. All rights reserved.
68 * Copyright (c) 1993 Jan-Simon Pendry
70 * This code is derived from software contributed to Berkeley by
73 * Redistribution and use in source and binary forms, with or without
74 * modification, are permitted provided that the following conditions
76 * 1. Redistributions of source code must retain the above copyright
77 * notice, this list of conditions and the following disclaimer.
78 * 2. Redistributions in binary form must reproduce the above copyright
79 * notice, this list of conditions and the following disclaimer in the
80 * documentation and/or other materials provided with the distribution.
81 * 3. All advertising materials mentioning features or use of this software
82 * must display the following acknowledgement:
83 * This product includes software developed by the University of
84 * California, Berkeley and its contributors.
85 * 4. Neither the name of the University nor the names of its contributors
86 * may be used to endorse or promote products derived from this software
87 * without specific prior written permission.
89 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
90 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
91 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
92 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
93 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
94 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
95 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
96 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
97 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
98 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
101 * @(#)procfs_subr.c 8.6 (Berkeley) 5/14/95
104 #include <sys/cdefs.h>
105 __KERNEL_RCSID(0, "$NetBSD: procfs_subr.c,v 1.94 2009/03/14 15:36:23 dsl Exp $");
107 #include <sys/param.h>
108 #include <sys/systm.h>
109 #include <sys/time.h>
110 #include <sys/kernel.h>
111 #include <sys/proc.h>
112 #include <sys/vnode.h>
113 #include <sys/malloc.h>
114 #include <sys/stat.h>
115 #include <sys/file.h>
116 #include <sys/filedesc.h>
117 #include <sys/kauth.h>
119 #include <miscfs/procfs/procfs.h>
121 void procfs_hashins(struct pfsnode
*);
122 void procfs_hashrem(struct pfsnode
*);
123 struct vnode
*procfs_hashget(pid_t
, pfstype
, int, struct mount
*, int);
125 LIST_HEAD(pfs_hashhead
, pfsnode
) *pfs_hashtbl
;
126 u_long pfs_ihash
; /* size of hash table - 1 */
127 #define PFSPIDHASH(pid) ((pid) & pfs_ihash)
129 kmutex_t pfs_hashlock
;
130 kmutex_t pfs_ihash_lock
;
132 #define ISSET(t, f) ((t) & (f))
135 * allocate a pfsnode/vnode pair. the vnode is
136 * referenced, and locked.
138 * the pid, pfs_type, and mount point uniquely
139 * identify a pfsnode. the mount point is needed
140 * because someone might mount this filesystem
143 * all pfsnodes are maintained on a singly-linked
144 * list. new nodes are only allocated when they cannot
145 * be found on this list. entries on the list are
146 * removed when the vfs reclaim entry is called.
148 * a single lock is kept for the entire list. this is
149 * needed because the getnewvnode() function can block
150 * waiting for a vnode to become free, in which case there
151 * may be more than one process trying to get the same
152 * vnode. this lock is only taken if we are going to
153 * call getnewvnode, since the kernel itself is single-threaded.
155 * if an entry is found on the list, then call vget() to
156 * take a reference. this is done because there may be
157 * zero references to it and so it needs to removed from
158 * the vnode free list.
161 procfs_allocvp(struct mount
*mp
, struct vnode
**vpp
, pid_t pid
, pfstype pfs_type
, int fd
, struct proc
*p
)
168 *vpp
= procfs_hashget(pid
, pfs_type
, fd
, mp
, LK_EXCLUSIVE
);
172 if ((error
= getnewvnode(VT_PROCFS
, mp
, procfs_vnodeop_p
, &vp
)) != 0) {
176 pfs
= malloc(sizeof(struct pfsnode
), M_TEMP
, M_WAITOK
);
178 mutex_enter(&pfs_hashlock
);
179 if ((*vpp
= procfs_hashget(pid
, pfs_type
, fd
, mp
, 0)) != NULL
) {
180 mutex_exit(&pfs_hashlock
);
188 pfs
->pfs_type
= pfs_type
;
191 pfs
->pfs_fileno
= PROCFS_FILENO(pid
, pfs_type
, fd
);
195 case PFSroot
: /* /proc = dr-xr-xr-x */
196 vp
->v_vflag
|= VV_ROOT
;
198 case PFSproc
: /* /proc/N = dr-xr-xr-x */
199 pfs
->pfs_mode
= S_IRUSR
|S_IXUSR
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
;
203 case PFScurproc
: /* /proc/curproc = lr-xr-xr-x */
204 case PFSself
: /* /proc/self = lr-xr-xr-x */
205 case PFScwd
: /* /proc/N/cwd = lr-xr-xr-x */
206 case PFSchroot
: /* /proc/N/chroot = lr-xr-xr-x */
207 case PFSexe
: /* /proc/N/exe = lr-xr-xr-x */
208 pfs
->pfs_mode
= S_IRUSR
|S_IXUSR
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
;
213 if (fd
== -1) { /* /proc/N/fd = dr-xr-xr-x */
214 pfs
->pfs_mode
= S_IRUSR
|S_IXUSR
;
216 } else { /* /proc/N/fd/M = [ps-]rw------- */
220 if ((fp
= fd_getfile2(p
, pfs
->pfs_fd
)) == NULL
) {
225 pfs
->pfs_mode
= S_IRUSR
|S_IWUSR
;
226 switch (fp
->f_type
) {
231 * We make symlinks for directories
234 if (vxp
->v_type
== VDIR
)
236 vp
->v_type
= vxp
->v_type
;
247 pfs
->pfs_mode
= S_IRUSR
|S_IXUSR
|S_IRGRP
|
248 S_IXGRP
|S_IROTH
|S_IXOTH
;
260 case PFSfile
: /* /proc/N/file = -rw------- */
261 case PFSmem
: /* /proc/N/mem = -rw------- */
262 case PFSregs
: /* /proc/N/regs = -rw------- */
263 case PFSfpregs
: /* /proc/N/fpregs = -rw------- */
264 pfs
->pfs_mode
= S_IRUSR
|S_IWUSR
;
268 case PFSctl
: /* /proc/N/ctl = --w------ */
269 case PFSnote
: /* /proc/N/note = --w------ */
270 case PFSnotepg
: /* /proc/N/notepg = --w------ */
271 pfs
->pfs_mode
= S_IWUSR
;
275 case PFSmap
: /* /proc/N/map = -r--r--r-- */
276 case PFSmaps
: /* /proc/N/maps = -r--r--r-- */
277 case PFSstatus
: /* /proc/N/status = -r--r--r-- */
278 case PFSstat
: /* /proc/N/stat = -r--r--r-- */
279 case PFScmdline
: /* /proc/N/cmdline = -r--r--r-- */
280 case PFSemul
: /* /proc/N/emul = -r--r--r-- */
281 case PFSmeminfo
: /* /proc/meminfo = -r--r--r-- */
282 case PFScpustat
: /* /proc/stat = -r--r--r-- */
283 case PFSdevices
: /* /proc/devices = -r--r--r-- */
284 case PFScpuinfo
: /* /proc/cpuinfo = -r--r--r-- */
285 case PFSuptime
: /* /proc/uptime = -r--r--r-- */
286 case PFSmounts
: /* /proc/mounts = -r--r--r-- */
287 case PFSloadavg
: /* /proc/loadavg = -r--r--r-- */
288 case PFSstatm
: /* /proc/N/statm = -r--r--r-- */
289 pfs
->pfs_mode
= S_IRUSR
|S_IRGRP
|S_IROTH
;
293 #ifdef __HAVE_PROCFS_MACHDEP
294 PROCFS_MACHDEP_NODETYPE_CASES
295 procfs_machdep_allocvp(vp
);
300 panic("procfs_allocvp");
304 uvm_vnp_setsize(vp
, 0);
305 mutex_exit(&pfs_hashlock
);
311 mutex_exit(&pfs_hashlock
);
319 procfs_freevp(struct vnode
*vp
)
321 struct pfsnode
*pfs
= VTOPFS(vp
);
325 free(vp
->v_data
, M_TEMP
);
333 struct vop_read_args
*ap
= v
;
334 struct vnode
*vp
= ap
->a_vp
;
335 struct uio
*uio
= ap
->a_uio
;
338 struct pfsnode
*pfs
= VTOPFS(vp
);
342 if (uio
->uio_offset
< 0)
345 if ((error
= procfs_proc_lock(pfs
->pfs_pid
, &p
, ESRCH
)) != 0)
351 * Do not allow init to be modified while in secure mode; it
352 * could be duped into changing the security level.
354 #define M2K(m) ((m) == UIO_READ ? KAUTH_REQ_PROCESS_PROCFS_READ : \
355 KAUTH_REQ_PROCESS_PROCFS_WRITE)
356 mutex_enter(p
->p_lock
);
357 error
= kauth_authorize_process(curl
->l_cred
, KAUTH_PROCESS_PROCFS
,
358 p
, pfs
, KAUTH_ARG(M2K(uio
->uio_rw
)), NULL
);
359 mutex_exit(p
->p_lock
);
361 procfs_proc_unlock(p
);
366 mutex_enter(p
->p_lock
);
367 LIST_FOREACH(l
, &p
->p_lwps
, l_sibling
) {
368 if (l
->l_stat
!= LSZOMB
)
371 /* Process is exiting if no-LWPS or all LWPs are LSZOMB */
373 mutex_exit(p
->p_lock
);
374 procfs_proc_unlock(p
);
379 mutex_exit(p
->p_lock
);
381 switch (pfs
->pfs_type
) {
384 error
= procfs_donote(curl
, p
, pfs
, uio
);
388 error
= procfs_doregs(curl
, l
, pfs
, uio
);
392 error
= procfs_dofpregs(curl
, l
, pfs
, uio
);
396 error
= procfs_doctl(curl
, l
, pfs
, uio
);
400 error
= procfs_dostatus(curl
, l
, pfs
, uio
);
404 error
= procfs_do_pid_stat(curl
, l
, pfs
, uio
);
408 error
= procfs_domap(curl
, p
, pfs
, uio
, 0);
412 error
= procfs_domap(curl
, p
, pfs
, uio
, 1);
416 error
= procfs_domem(curl
, l
, pfs
, uio
);
420 error
= procfs_docmdline(curl
, p
, pfs
, uio
);
424 error
= procfs_domeminfo(curl
, p
, pfs
, uio
);
428 error
= procfs_dodevices(curl
, p
, pfs
, uio
);
432 error
= procfs_docpuinfo(curl
, p
, pfs
, uio
);
436 error
= procfs_docpustat(curl
, p
, pfs
, uio
);
440 error
= procfs_doloadavg(curl
, p
, pfs
, uio
);
444 error
= procfs_do_pid_statm(curl
, l
, pfs
, uio
);
448 error
= procfs_dofd(curl
, p
, pfs
, uio
);
452 error
= procfs_douptime(curl
, p
, pfs
, uio
);
456 error
= procfs_domounts(curl
, p
, pfs
, uio
);
460 error
= procfs_doemul(curl
, p
, pfs
, uio
);
463 #ifdef __HAVE_PROCFS_MACHDEP
464 PROCFS_MACHDEP_NODETYPE_CASES
465 error
= procfs_machdep_rw(curl
, l
, pfs
, uio
);
475 * Release the references that we acquired earlier.
478 procfs_proc_unlock(p
);
484 * Get a string from userland into (bf). Strip a trailing
485 * nl character (to allow easy access from the shell).
486 * The buffer should be *buflenp + 1 chars long. vfs_getuserstr
487 * will automatically add a nul char at the end.
489 * Returns 0 on success or the following errors
491 * EINVAL: file offset is non-zero.
492 * EMSGSIZE: message is longer than kernel buffer
493 * EFAULT: user i/o buffer is not addressable
496 vfs_getuserstr(struct uio
*uio
, char *bf
, int *buflenp
)
501 if (uio
->uio_offset
!= 0)
506 /* must be able to read the whole string in one go */
507 if (xlen
< uio
->uio_resid
)
509 xlen
= uio
->uio_resid
;
511 if ((error
= uiomove(bf
, xlen
, uio
)) != 0)
514 /* allow multiple writes without seeks */
517 /* cleanup string and remove trailing newline */
520 if (xlen
> 0 && bf
[xlen
-1] == '\n')
527 const vfs_namemap_t
*
528 vfs_findname(const vfs_namemap_t
*nm
, const char *bf
, int buflen
)
531 for (; nm
->nm_name
; nm
++)
532 if (memcmp(bf
, nm
->nm_name
, buflen
+1) == 0)
539 * Initialize pfsnode hash table.
542 procfs_hashinit(void)
544 mutex_init(&pfs_hashlock
, MUTEX_DEFAULT
, IPL_NONE
);
545 mutex_init(&pfs_ihash_lock
, MUTEX_DEFAULT
, IPL_NONE
);
546 pfs_hashtbl
= hashinit(desiredvnodes
/ 4, HASH_LIST
, true, &pfs_ihash
);
550 procfs_hashreinit(void)
553 struct pfs_hashhead
*oldhash
, *hash
;
554 u_long i
, oldmask
, mask
, val
;
556 hash
= hashinit(desiredvnodes
/ 4, HASH_LIST
, true, &mask
);
558 mutex_enter(&pfs_ihash_lock
);
559 oldhash
= pfs_hashtbl
;
563 for (i
= 0; i
<= oldmask
; i
++) {
564 while ((pp
= LIST_FIRST(&oldhash
[i
])) != NULL
) {
565 LIST_REMOVE(pp
, pfs_hash
);
566 val
= PFSPIDHASH(pp
->pfs_pid
);
567 LIST_INSERT_HEAD(&hash
[val
], pp
, pfs_hash
);
570 mutex_exit(&pfs_ihash_lock
);
571 hashdone(oldhash
, HASH_LIST
, oldmask
);
575 * Free pfsnode hash table.
578 procfs_hashdone(void)
580 hashdone(pfs_hashtbl
, HASH_LIST
, pfs_ihash
);
581 mutex_destroy(&pfs_hashlock
);
582 mutex_destroy(&pfs_ihash_lock
);
586 procfs_hashget(pid_t pid
, pfstype type
, int fd
, struct mount
*mp
, int flags
)
588 struct pfs_hashhead
*ppp
;
593 mutex_enter(&pfs_ihash_lock
);
594 ppp
= &pfs_hashtbl
[PFSPIDHASH(pid
)];
595 LIST_FOREACH(pp
, ppp
, pfs_hash
) {
597 if (pid
== pp
->pfs_pid
&& pp
->pfs_type
== type
&&
598 pp
->pfs_fd
== fd
&& vp
->v_mount
== mp
) {
600 mutex_exit(&pfs_ihash_lock
);
602 mutex_enter(&vp
->v_interlock
);
603 mutex_exit(&pfs_ihash_lock
);
604 if (vget(vp
, flags
| LK_INTERLOCK
))
610 mutex_exit(&pfs_ihash_lock
);
615 * Insert the pfsnode into the hash table and lock it.
618 procfs_hashins(struct pfsnode
*pp
)
620 struct pfs_hashhead
*ppp
;
622 /* lock the pfsnode, then put it on the appropriate hash list */
623 vlockmgr(&pp
->pfs_vnode
->v_lock
, LK_EXCLUSIVE
);
625 mutex_enter(&pfs_ihash_lock
);
626 ppp
= &pfs_hashtbl
[PFSPIDHASH(pp
->pfs_pid
)];
627 LIST_INSERT_HEAD(ppp
, pp
, pfs_hash
);
628 mutex_exit(&pfs_ihash_lock
);
632 * Remove the pfsnode from the hash table.
635 procfs_hashrem(struct pfsnode
*pp
)
637 mutex_enter(&pfs_ihash_lock
);
638 LIST_REMOVE(pp
, pfs_hash
);
639 mutex_exit(&pfs_ihash_lock
);
643 procfs_revoke_vnodes(struct proc
*p
, void *arg
)
645 struct pfsnode
*pfs
, *pnext
;
647 struct mount
*mp
= (struct mount
*)arg
;
648 struct pfs_hashhead
*ppp
;
650 if (!(p
->p_flag
& PK_SUGID
))
653 mutex_enter(&pfs_ihash_lock
);
654 ppp
= &pfs_hashtbl
[PFSPIDHASH(p
->p_pid
)];
655 for (pfs
= LIST_FIRST(ppp
); pfs
; pfs
= pnext
) {
657 pnext
= LIST_NEXT(pfs
, pfs_hash
);
658 mutex_enter(&vp
->v_interlock
);
659 if (vp
->v_usecount
> 0 && pfs
->pfs_pid
== p
->p_pid
&&
662 mutex_exit(&vp
->v_interlock
);
663 mutex_exit(&pfs_ihash_lock
);
664 VOP_REVOKE(vp
, REVOKEALL
);
666 mutex_enter(&pfs_ihash_lock
);
668 mutex_exit(&vp
->v_interlock
);
671 mutex_exit(&pfs_ihash_lock
);
675 procfs_proc_lock(int pid
, struct proc
**bunghole
, int notfound
)
680 mutex_enter(proc_lock
);
684 else if ((tp
= p_find(pid
, PFIND_LOCKED
)) == NULL
)
686 if (tp
!= NULL
&& !rw_tryenter(&tp
->p_reflock
, RW_READER
))
689 mutex_exit(proc_lock
);
696 procfs_proc_unlock(struct proc
*p
)
699 rw_exit(&p
->p_reflock
);
703 procfs_doemul(struct lwp
*curl
, struct proc
*p
,
704 struct pfsnode
*pfs
, struct uio
*uio
)
706 const char *ename
= p
->p_emul
->e_name
;
707 return uiomove_frombuf(__UNCONST(ename
), strlen(ename
), uio
);