xref: /netbsd-src/sys/miscfs/procfs/procfs_subr.c (revision ae1bfcddc410612bc8c58b807e1830becb69a24c)
1 /*
2  * Copyright (c) 1993 The Regents of the University of California.
3  * Copyright (c) 1993 Jan-Simon Pendry
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * Jan-Simon Pendry.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by the University of
20  *	California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  * From:
38  *	Id: procfs_subr.c,v 4.1 1993/12/17 10:47:45 jsp Rel
39  *
40  *	$Id: procfs_subr.c,v 1.10 1994/04/25 03:50:01 cgd Exp $
41  */
42 
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/time.h>
46 #include <sys/kernel.h>
47 #include <sys/proc.h>
48 #include <sys/vnode.h>
49 #include <miscfs/procfs/procfs.h>
50 
51 static struct pfsnode *pfshead;
52 static int pfsvplock;
53 
54 /*
55  * allocate a pfsnode/vnode pair.  the vnode is
56  * referenced, but not locked.
57  *
58  * the pid, pfs_type, and mount point uniquely
59  * identify a pfsnode.  the mount point is needed
60  * because someone might mount this filesystem
61  * twice.
62  *
63  * all pfsnodes are maintained on a singly-linked
64  * list.  new nodes are only allocated when they cannot
65  * be found on this list.  entries on the list are
66  * removed when the vfs reclaim entry is called.
67  *
68  * a single lock is kept for the entire list.  this is
69  * needed because the getnewvnode() function can block
70  * waiting for a vnode to become free, in which case there
71  * may be more than one process trying to get the same
72  * vnode.  this lock is only taken if we are going to
73  * call getnewvnode, since the kernel itself is single-threaded.
74  *
75  * if an entry is found on the list, then call vget() to
76  * take a reference.  this is done because there may be
77  * zero references to it and so it needs to removed from
78  * the vnode free list.
79  */
80 procfs_allocvp(mp, vpp, pid, pfs_type)
81 	struct mount *mp;
82 	struct vnode **vpp;
83 	long pid;
84 	pfstype pfs_type;
85 {
86 	int error;
87 	struct pfsnode *pfs;
88 	struct pfsnode **pp;
89 	struct vnode *vp;
90 
91 loop:
92 	for (pfs = pfshead; pfs != 0; pfs = pfs->pfs_next) {
93 		if (pfs->pfs_pid == pid &&
94 		    pfs->pfs_type == pfs_type &&
95 		    PFSTOV(pfs)->v_mount == mp) {
96 			if (vget(pfs->pfs_vnode, 1))
97 				goto loop;
98 			VOP_UNLOCK(pfs->pfs_vnode);
99 			*vpp = pfs->pfs_vnode;
100 			return (0);
101 		}
102 	}
103 
104 	/*
105 	 * otherwise lock the vp list while we call getnewvnode
106 	 * since that can block.
107 	 */
108 	if (pfsvplock & PROCFS_LOCKED) {
109 		pfsvplock |= PROCFS_WANT;
110 		sleep((caddr_t) &pfsvplock, PINOD);
111 		goto loop;
112 	}
113 	pfsvplock |= PROCFS_LOCKED;
114 
115 	error = getnewvnode(VT_PROCFS, mp, &procfs_vnodeops, vpp);
116 	if (error)
117 		goto out;
118 
119 	/* 4.4: at this point, need to allocate a pfsnode */
120 
121 	pfs = VTOPFS(*vpp);
122 	pfs->pfs_next = 0;
123 	pfs->pfs_pid = (pid_t) pid;
124 	pfs->pfs_type = pfs_type;
125 	pfs->pfs_vnode = vp = *vpp;
126 	pfs->pfs_flags = 0;
127 	pfs->pfs_fileno = PROCFS_FILENO(pid, pfs_type);
128 
129 	switch (pfs_type) {
130 	case Proot:
131 		switch ((int)pid) {
132 		case 0:		/* /proc = dr-xr-xr-x */
133 			pfs->pfs_mode = (VREAD|VEXEC) |
134 					(VREAD|VEXEC) >> 3 |
135 					(VREAD|VEXEC) >> 6;
136 			vp->v_type = VDIR;
137 			vp->v_flag = VROOT;
138 			break;
139 		case 1:		/* /proc/curproc = lr--r--r-- */
140 			pfs->pfs_mode = VREAD |
141 					VREAD >> 3 |
142 					VREAD >> 6;
143 			vp->v_type = VLNK;
144 			break;
145 		default:
146 			panic("procfs_allocvp root");
147 		}
148 		break;
149 
150 	case Pproc:
151 		pfs->pfs_mode = (VREAD|VEXEC) |
152 				(VREAD|VEXEC) >> 3 |
153 				(VREAD|VEXEC) >> 6;
154 		vp->v_type = VDIR;
155 		break;
156 
157 	case Pmem:
158 		pfs->pfs_mode = (VREAD|VWRITE);
159 		vp->v_type = VREG;
160 		break;
161 
162 	case Pregs:
163 		pfs->pfs_mode = (VREAD|VWRITE);
164 		vp->v_type = VREG;
165 		break;
166 
167 	case Pfpregs:
168 		pfs->pfs_mode = (VREAD|VWRITE);
169 		vp->v_type = VREG;
170 		break;
171 
172 	case Pctl:
173 		pfs->pfs_mode = (VREAD|VWRITE);
174 		vp->v_type = VREG;
175 		break;
176 
177 	case Pstatus:
178 		pfs->pfs_mode = (VREAD) |
179 				(VREAD >> 3) |
180 				(VREAD >> 6);
181 		vp->v_type = VREG;
182 		break;
183 
184 	case Pnote:
185 		pfs->pfs_mode = (VREAD|VWRITE);
186 		vp->v_type = VREG;
187 		break;
188 
189 	case Pnotepg:
190 		pfs->pfs_mode = (VWRITE);
191 		vp->v_type = VREG;
192 		break;
193 
194 	default:
195 		panic("procfs_allocvp type");
196 	}
197 
198 	/* add to procfs vnode list */
199 	for (pp = &pfshead; *pp; pp = &(*pp)->pfs_next)
200 		continue;
201 	*pp = pfs;
202 
203 out:
204 	pfsvplock &= ~PROCFS_LOCKED;
205 
206 	if (pfsvplock & PROCFS_WANT) {
207 		pfsvplock &= ~PROCFS_WANT;
208 		wakeup((caddr_t) &pfsvplock);
209 	}
210 
211 	return (error);
212 }
213 
214 procfs_freevp(vp)
215 	struct vnode *vp;
216 {
217 	struct pfsnode **pfspp;
218 	struct pfsnode *pfs = VTOPFS(vp);
219 
220 	/* 4.4: at this point, need to deallocate the pfsnode */
221 
222 	for (pfspp = &pfshead; *pfspp != 0; pfspp = &(*pfspp)->pfs_next) {
223 		if (*pfspp == pfs) {
224 			*pfspp = pfs->pfs_next;
225 			break;
226 		}
227 	}
228 
229 	return (0);
230 }
231 
232 procfs_rw(vp, uio, ioflag, cred)
233 	struct vnode *vp;
234 	struct uio *uio;
235 	int ioflag;
236 	struct ucred *cred;
237 {
238 	struct proc *curp = uio->uio_procp;
239 	struct pfsnode *pfs = VTOPFS(vp);
240 	struct proc *p;
241 
242 	p = PFIND(pfs->pfs_pid);
243 	if (p == 0)
244 		return (EINVAL);
245 
246 	switch (pfs->pfs_type) {
247 	case Pnote:
248 	case Pnotepg:
249 		return (pfs_donote(curp, p, pfs, uio));
250 
251 	case Pregs:
252 		return (pfs_doregs(curp, p, pfs, uio));
253 
254 	case Pfpregs:
255 		return (pfs_dofpregs(curp, p, pfs, uio));
256 
257 	case Pctl:
258 		return (pfs_doctl(curp, p, pfs, uio));
259 
260 	case Pstatus:
261 		return (pfs_dostatus(curp, p, pfs, uio));
262 
263 	case Pmem:
264 		return (pfs_domem(curp, p, pfs, uio));
265 
266 	default:
267 		return (EOPNOTSUPP);
268 	}
269 }
270 
271 /*
272  * Get a string from userland into (buf).  Strip a trailing
273  * nl character (to allow easy access from the shell).
274  * The buffer should be *buflenp + 1 chars long.  procfs_getuserstr
275  * will automatically add a nul char at the end.
276  *
277  * Returns 0 on success or the following errors
278  *
279  * EINVAL:    file offset is non-zero.
280  * EMSGSIZE:  message is longer than kernel buffer
281  * EFAULT:    user i/o buffer is not addressable
282  */
283 procfs_getuserstr(uio, buf, buflenp)
284 	struct uio *uio;
285 	char *buf;
286 	int *buflenp;
287 {
288 	int xlen;
289 	int error;
290 
291 	xlen = *buflenp;
292 
293 	/* must be able to read the whole string in one go */
294 	if (xlen < uio->uio_resid)
295 		return (EMSGSIZE);
296 	xlen = uio->uio_resid;
297 
298 	error = uiomove(buf, xlen, uio);
299 	if (error)
300 		return (error);
301 
302 	/* cleanup string and remove trailing newline */
303 	buf[xlen] = '\0';
304 	xlen = strlen(buf);
305 	if (xlen > 0 && buf[xlen-1] == '\n')
306 		buf[--xlen] = '\0';
307 	*buflenp = xlen;
308 
309 	return (0);
310 }
311 
312 procfs_namemap_t *
313 procfs_findname(nm, buf, buflen)
314 	procfs_namemap_t *nm;
315 	char *buf;
316 	int buflen;
317 {
318 	for (; nm->nm_name; nm++)
319 		if (bcmp(buf, (char *) nm->nm_name, buflen+1) == 0)
320 			return (nm);
321 
322 	return (0);
323 }
324