1 /* $OpenBSD: procfs_subr.c,v 1.27 2007/06/22 09:38:53 jasper Exp $ */
2 /* $NetBSD: procfs_subr.c,v 1.15 1996/02/12 15:01:42 christos Exp $ */
3
4 /*
5 * Copyright (c) 1993 Jan-Simon Pendry
6 * Copyright (c) 1993
7 * The Regents of the University of California. All rights reserved.
8 *
9 * This code is derived from software contributed to Berkeley by
10 * Jan-Simon Pendry.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * @(#)procfs_subr.c 8.5 (Berkeley) 6/15/94
37 */
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/time.h>
42 #include <sys/kernel.h>
43 #include <sys/proc.h>
44 #include <sys/vnode.h>
45 #include <sys/malloc.h>
46 #include <sys/stat.h>
47 #include <sys/ptrace.h>
48
49 #include <miscfs/procfs/procfs.h>
50
51 static TAILQ_HEAD(, pfsnode) pfshead;
52 struct lock pfs_vlock;
53
54 /*ARGSUSED*/
55 int
56 procfs_init(struct vfsconf *vfsp)
57 {
58 lockinit(&pfs_vlock, PVFS, "procfsl", 0, 0);
59 TAILQ_INIT(&pfshead);
60 return (0);
61 }
62
63 /*
64 * allocate a pfsnode/vnode pair. the vnode is
65 * referenced, but not locked.
66 *
67 * the pid, pfs_type, and mount point uniquely
68 * identify a pfsnode. the mount point is needed
69 * because someone might mount this filesystem
70 * twice.
71 *
72 * all pfsnodes are maintained on a singly-linked
73 * list. new nodes are only allocated when they cannot
74 * be found on this list. entries on the list are
75 * removed when the vfs reclaim entry is called.
76 *
77 * a single lock is kept for the entire list. this is
78 * needed because the getnewvnode() function can block
79 * waiting for a vnode to become free, in which case there
80 * may be more than one process trying to get the same
81 * vnode. this lock is only taken if we are going to
82 * call getnewvnode, since the kernel itself is single-threaded.
83 *
84 * if an entry is found on the list, then call vget() to
85 * take a reference. this is done because there may be
86 * zero references to it and so it needs to removed from
87 * the vnode free list.
88 */
89 int
90 procfs_allocvp(struct mount *mp, struct vnode **vpp, pid_t pid, pfstype pfs_type)
91 {
92 struct proc *p = curproc;
93 struct pfsnode *pfs;
94 struct vnode *vp;
95 int error;
96
97 /*
98 * Lock the vp list, getnewvnode can sleep.
99 */
100 error = lockmgr(&pfs_vlock, LK_EXCLUSIVE, NULL);
101 if (error)
102 return (error);
103 loop:
104 TAILQ_FOREACH(pfs, &pfshead, list) {
105 vp = PFSTOV(pfs);
106 if (pfs->pfs_pid == pid &&
107 pfs->pfs_type == pfs_type &&
108 vp->v_mount == mp) {
109 if (vget(vp, 0, p))
110 goto loop;
111 *vpp = vp;
112 goto out;
113 }
114 }
115
116 if ((error = getnewvnode(VT_PROCFS, mp, procfs_vnodeop_p, vpp)) != 0)
117 goto out;
118 vp = *vpp;
119
120 MALLOC(pfs, void *, sizeof(struct pfsnode), M_TEMP, M_WAITOK);
121 vp->v_data = pfs;
122
123 pfs->pfs_pid = pid;
124 pfs->pfs_type = pfs_type;
125 pfs->pfs_vnode = vp;
126 pfs->pfs_flags = 0;
127 pfs->pfs_fileno = PROCFS_FILENO(pid, pfs_type);
128
129 switch (pfs_type) {
130 case Proot: /* /proc = dr-xr-xr-x */
131 pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
132 vp->v_type = VDIR;
133 vp->v_flag = VROOT;
134 break;
135
136 case Pcurproc: /* /proc/curproc = lr--r--r-- */
137 case Pself: /* /proc/self = lr--r--r-- */
138 pfs->pfs_mode = S_IRUSR|S_IRGRP|S_IROTH;
139 vp->v_type = VLNK;
140 break;
141
142 case Pproc: /* /proc/N = dr-xr-xr-x */
143 pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
144 vp->v_type = VDIR;
145 break;
146
147 case Pfile: /* /proc/N/file = -rw------- */
148 case Pmem: /* /proc/N/mem = -rw------- */
149 case Pregs: /* /proc/N/regs = -rw------- */
150 case Pfpregs: /* /proc/N/fpregs = -rw------- */
151 pfs->pfs_mode = S_IRUSR|S_IWUSR;
152 vp->v_type = VREG;
153 break;
154
155 case Pctl: /* /proc/N/ctl = --w------ */
156 case Pnote: /* /proc/N/note = --w------ */
157 case Pnotepg: /* /proc/N/notepg = --w------ */
158 pfs->pfs_mode = S_IWUSR;
159 vp->v_type = VREG;
160 break;
161
162 case Pstatus: /* /proc/N/status = -r--r--r-- */
163 case Pcmdline: /* /proc/N/cmdline = -r--r--r-- */
164 case Pmeminfo: /* /proc/meminfo = -r--r--r-- */
165 case Pcpuinfo: /* /proc/cpuinfo = -r--r--r-- */
166 pfs->pfs_mode = S_IRUSR|S_IRGRP|S_IROTH;
167 vp->v_type = VREG;
168 break;
169
170 default:
171 panic("procfs_allocvp");
172 }
173
174 /* add to procfs vnode list */
175 TAILQ_INSERT_TAIL(&pfshead, pfs, list);
176 uvm_vnp_setsize(vp, 0);
177 out:
178 lockmgr(&pfs_vlock, LK_RELEASE, NULL);
179
180 return (error);
181 }
182
183 int
184 procfs_freevp(struct vnode *vp)
185 {
186 struct pfsnode *pfs = VTOPFS(vp);
187
188 TAILQ_REMOVE(&pfshead, pfs, list);
189 FREE(vp->v_data, M_TEMP);
190 vp->v_data = 0;
191 return (0);
192 }
193
194 int
195 procfs_rw(void *v)
196 {
197 struct vop_read_args *ap = v;
198 struct vnode *vp = ap->a_vp;
199 struct uio *uio = ap->a_uio;
200 struct proc *curp = uio->uio_procp;
201 struct pfsnode *pfs = VTOPFS(vp);
202 struct proc *p;
203
204 p = pfind(pfs->pfs_pid);
205 if (p == 0)
206 return (EINVAL);
207 /* Do not permit games to be played with init(8) */
208 if (p->p_pid == 1 && securelevel > 0 && uio->uio_rw == UIO_WRITE)
209 return (EPERM);
210 if (uio->uio_offset < 0)
211 return (EINVAL);
212
213 switch (pfs->pfs_type) {
214 case Pnote:
215 case Pnotepg:
216 return (procfs_donote(curp, p, pfs, uio));
217
218 case Pctl:
219 return (procfs_doctl(curp, p, pfs, uio));
220
221 case Pstatus:
222 return (procfs_dostatus(curp, p, pfs, uio));
223
224 case Pmem:
225 return (process_domem(curp, p, uio, PT_WRITE_I));
226
227 case Pcmdline:
228 return (procfs_docmdline(curp, p, pfs, uio));
229
230 case Pmeminfo:
231 return (procfs_domeminfo(curp, p, pfs, uio));
232
233 case Pcpuinfo:
234 return (procfs_docpuinfo(curp, p, pfs, uio));
235
236 default:
237 return (EOPNOTSUPP);
238 }
239 }
240
241 /*
242 * Get a string from userland into (buf). Strip a trailing
243 * nl character (to allow easy access from the shell).
244 * The buffer should be *buflenp + 1 chars long. vfs_getuserstr
245 * will automatically add a nul char at the end.
246 *
247 * Returns 0 on success or the following errors
248 *
249 * EINVAL: file offset is non-zero.
250 * EMSGSIZE: message is longer than kernel buffer
251 * EFAULT: user i/o buffer is not addressable
252 */
253 int
254 vfs_getuserstr(struct uio *uio, char *buf, int *buflenp)
255 {
256 int xlen;
257 int error;
258
259 if (uio->uio_offset != 0)
260 return (EINVAL);
261
262 xlen = *buflenp;
263
264 /* must be able to read the whole string in one go */
265 if (xlen < uio->uio_resid)
266 return (EMSGSIZE);
267 xlen = uio->uio_resid;
268
269 if ((error = uiomove(buf, xlen, uio)) != 0)
270 return (error);
271
272 /* allow multiple writes without seeks */
273 uio->uio_offset = 0;
274
275 /* cleanup string and remove trailing newline */
276 buf[xlen] = '\0';
277 xlen = strlen(buf);
278 if (xlen > 0 && buf[xlen-1] == '\n')
279 buf[--xlen] = '\0';
280 *buflenp = xlen;
281
282 return (0);
283 }
284
285 const vfs_namemap_t *
286 vfs_findname(const vfs_namemap_t *nm, char *buf, int buflen)
287 {
288 for (; nm->nm_name; nm++)
289 if (bcmp(buf, nm->nm_name, buflen + 1) == 0)
290 return (nm);
291
292 return (0);
293 }