kernel/linprocfs: Fix accessing files in /proc (such as /proc/meminfo).
[dragonfly.git] / sys / emulation / linux / i386 / linprocfs / linprocfs_vnops.c
CommitLineData
984263bc
MD
1/*
2 * Copyright (c) 2000 Dag-Erling Coïdan Smørgrav
3 * Copyright (c) 1999 Pierre Beyssac
4 * Copyright (c) 1993, 1995 Jan-Simon Pendry
5 * Copyright (c) 1993, 1995
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
39 * @(#)procfs_vnops.c 8.18 (Berkeley) 5/21/95
40 *
41 * $FreeBSD: src/sys/i386/linux/linprocfs/linprocfs_vnops.c,v 1.3.2.5 2001/08/12 14:29:19 rwatson Exp $
42 */
43
44/*
45 * procfs vnode interface
46 */
47
48#include <sys/param.h>
49#include <sys/systm.h>
50#include <sys/time.h>
51#include <sys/kernel.h>
52#include <sys/lock.h>
53#include <sys/fcntl.h>
54#include <sys/proc.h>
895c1f85 55#include <sys/priv.h>
984263bc
MD
56#include <sys/signalvar.h>
57#include <sys/vnode.h>
58#include <sys/mount.h>
59#include <sys/namei.h>
60#include <sys/dirent.h>
61#include <sys/malloc.h>
527fddf7 62#include <sys/reg.h>
a1f82243 63#include <sys/jail.h>
984263bc 64#include <vm/vm_zone.h>
1f2de5d4 65#include "linprocfs.h"
984263bc 66#include <sys/pioctl.h>
c55c2701 67#include <sys/spinlock2.h>
984263bc 68
9c274f28
JS
69#include <machine/limits.h>
70
b1efe3b4 71extern struct vnode *procfs_findtextvp (struct proc *);
984263bc 72
b1efe3b4 73static int linprocfs_access (struct vop_access_args *);
66a1ddf5 74static int linprocfs_badop (struct vop_generic_args *);
b1efe3b4
RG
75static int linprocfs_bmap (struct vop_bmap_args *);
76static int linprocfs_close (struct vop_close_args *);
77static int linprocfs_getattr (struct vop_getattr_args *);
78static int linprocfs_inactive (struct vop_inactive_args *);
79static int linprocfs_ioctl (struct vop_ioctl_args *);
e62afb5f 80static int linprocfs_lookup (struct vop_old_lookup_args *);
b1efe3b4
RG
81static int linprocfs_open (struct vop_open_args *);
82static int linprocfs_print (struct vop_print_args *);
83static int linprocfs_readdir (struct vop_readdir_args *);
84static int linprocfs_readlink (struct vop_readlink_args *);
85static int linprocfs_reclaim (struct vop_reclaim_args *);
86static int linprocfs_setattr (struct vop_setattr_args *);
984263bc 87
9c274f28
JS
88static int linprocfs_readdir_proc(struct vop_readdir_args *);
89static int linprocfs_readdir_root(struct vop_readdir_args *);
a1f82243
AH
90static int linprocfs_readdir_net(struct vop_readdir_args *ap);
91static int linprocfs_readdir_sys(struct vop_readdir_args *ap);
92static int linprocfs_readdir_syskernel(struct vop_readdir_args *ap);
9c274f28 93
984263bc 94/*
66a1ddf5
MD
95 * procfs vnode operations.
96 */
97struct vop_ops linprocfs_vnode_vops = {
98 .vop_default = vop_defaultop,
99 .vop_access = linprocfs_access,
100 .vop_advlock = (void *)linprocfs_badop,
101 .vop_bmap = linprocfs_bmap,
102 .vop_close = linprocfs_close,
103 .vop_old_create = (void *)linprocfs_badop,
104 .vop_getattr = linprocfs_getattr,
105 .vop_inactive = linprocfs_inactive,
106 .vop_old_link = (void *)linprocfs_badop,
107 .vop_old_lookup = linprocfs_lookup,
108 .vop_old_mkdir = (void *)linprocfs_badop,
109 .vop_old_mknod = (void *)linprocfs_badop,
110 .vop_open = linprocfs_open,
111 .vop_pathconf = vop_stdpathconf,
112 .vop_print = linprocfs_print,
113 .vop_read = (void *)linprocfs_rw,
114 .vop_readdir = linprocfs_readdir,
115 .vop_readlink = linprocfs_readlink,
116 .vop_reclaim = linprocfs_reclaim,
117 .vop_old_remove = (void *)linprocfs_badop,
118 .vop_old_rename = (void *)linprocfs_badop,
119 .vop_old_rmdir = (void *)linprocfs_badop,
120 .vop_setattr = linprocfs_setattr,
121 .vop_old_symlink = (void *)linprocfs_badop,
122 .vop_write = (void *)linprocfs_rw,
123 .vop_ioctl = linprocfs_ioctl
124};
125
126/*
984263bc
MD
127 * This is a list of the valid names in the
128 * process-specific sub-directories. It is
129 * used in linprocfs_lookup and linprocfs_readdir
130 */
131static struct proc_target {
132 u_char pt_type;
133 u_char pt_namlen;
134 char *pt_name;
135 pfstype pt_pfstype;
b1efe3b4 136 int (*pt_valid) (struct proc *p);
984263bc
MD
137} proc_targets[] = {
138#define N(s) sizeof(s)-1, s
139 /* name type validp */
140 { DT_DIR, N("."), Pproc, NULL },
141 { DT_DIR, N(".."), Proot, NULL },
142 { DT_REG, N("mem"), Pmem, NULL },
a1f82243 143
984263bc 144 { DT_LNK, N("exe"), Pexe, NULL },
a1f82243
AH
145 { DT_LNK, N("cwd"), Pcwd, NULL },
146 { DT_LNK, N("root"), Pprocroot, NULL },
147 { DT_LNK, N("fd"), Pfd, NULL },
148
984263bc
MD
149 { DT_REG, N("stat"), Pprocstat, NULL },
150 { DT_REG, N("status"), Pprocstatus, NULL },
a1f82243
AH
151 { DT_REG, N("maps"), Pmaps, NULL },
152 { DT_REG, N("statm"), Pstatm, NULL },
153#if 0
154 { DT_REG, N("cmdline"), Pcmdline, NULL },
155 { DT_REG, N("environ"), Penviron, NULL },
156#endif
984263bc
MD
157#undef N
158};
c157ff7a 159static const int nproc_targets = NELEM(proc_targets);
984263bc 160
b1efe3b4 161static pid_t atopid (const char *, u_int);
984263bc
MD
162
163/*
164 * set things up for doing i/o on
165 * the pfsnode (vp). (vp) is locked
166 * on entry, and should be left locked
167 * on exit.
168 *
169 * for procfs we don't need to do anything
170 * in particular for i/o. all that is done
171 * is to support exclusive open on process
172 * memory images.
173 */
174static int
2da2a8af 175linprocfs_open(struct vop_open_args *ap)
984263bc
MD
176{
177 struct pfsnode *pfs = VTOPFS(ap->a_vp);
41c20dac 178 struct proc *p2;
984263bc 179
96a686f9 180 p2 = linprocfs_pfind(pfs->pfs_pid);
984263bc
MD
181 if (p2 == NULL)
182 return (ENOENT);
41c20dac 183 if (pfs->pfs_pid && !PRISON_CHECK(ap->a_cred, p2->p_ucred))
984263bc
MD
184 return (ENOENT);
185
186 switch (pfs->pfs_type) {
187 case Pmem:
188 if (((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL)) ||
189 ((pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE)))
190 return (EBUSY);
191
41c20dac 192 if (p_trespass(ap->a_cred, p2->p_ucred))
984263bc
MD
193 return (EPERM);
194
195 if (ap->a_mode & FWRITE)
196 pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL);
197
8ddc6004 198 break;
984263bc
MD
199 default:
200 break;
201 }
202
8ddc6004 203 return (vop_stdopen(ap));
984263bc
MD
204}
205
206/*
207 * close the pfsnode (vp) after doing i/o.
208 * (vp) is not locked on entry or exit.
209 *
210 * nothing to do for procfs other than undo
211 * any exclusive open flag (see _open above).
212 */
213static int
2da2a8af 214linprocfs_close(struct vop_close_args *ap)
984263bc
MD
215{
216 struct pfsnode *pfs = VTOPFS(ap->a_vp);
217 struct proc *p;
218
219 switch (pfs->pfs_type) {
220 case Pmem:
221 if ((ap->a_fflag & FWRITE) && (pfs->pfs_flags & O_EXCL))
222 pfs->pfs_flags &= ~(FWRITE|O_EXCL);
223 /*
224 * If this is the last close, then it checks to see if
225 * the target process has PF_LINGER set in p_pfsflags,
226 * if this is *not* the case, then the process' stop flags
227 * are cleared, and the process is woken up. This is
228 * to help prevent the case where a process has been
229 * told to stop on an event, but then the requesting process
230 * has gone away or forgotten about it.
231 */
58c2553a 232 p = NULL;
3c37c940 233 if ((ap->a_vp->v_opencount < 2)
96a686f9 234 && (p = linprocfs_pfind(pfs->pfs_pid))
984263bc
MD
235 && !(p->p_pfsflags & PF_LINGER)) {
236 p->p_stops = 0;
237 p->p_step = 0;
b2a6ad87 238 wakeup(&p->p_stype);
984263bc 239 }
58c2553a
MD
240 if (p)
241 PRELE(p);
984263bc
MD
242 break;
243 default:
244 break;
245 }
8ddc6004 246 return (vop_stdclose(ap));
984263bc
MD
247}
248
249/*
250 * do an ioctl operation on a pfsnode (vp).
251 * (vp) is not locked on entry or exit.
252 */
253static int
2da2a8af 254linprocfs_ioctl(struct vop_ioctl_args *ap)
984263bc
MD
255{
256 struct pfsnode *pfs = VTOPFS(ap->a_vp);
dadab5e9 257 struct proc *procp;
984263bc
MD
258 int error;
259 int signo;
260 struct procfs_status *psp;
261 unsigned char flags;
262
96a686f9 263 procp = linprocfs_pfind(pfs->pfs_pid);
984263bc
MD
264 if (procp == NULL) {
265 return ENOTTY;
266 }
267
58c2553a
MD
268 if (p_trespass(ap->a_cred, procp->p_ucred)) {
269 error = EPERM;
270 goto done;
271 }
984263bc
MD
272
273 switch (ap->a_command) {
274 case PIOCBIS:
275 procp->p_stops |= *(unsigned int*)ap->a_data;
276 break;
277 case PIOCBIC:
278 procp->p_stops &= ~*(unsigned int*)ap->a_data;
279 break;
280 case PIOCSFL:
281 /*
282 * NFLAGS is "non-suser_xxx flags" -- currently, only
283 * PFS_ISUGID ("ignore set u/g id");
284 */
285#define NFLAGS (PF_ISUGID)
286 flags = (unsigned char)*(unsigned int*)ap->a_data;
895c1f85 287 if (flags & NFLAGS && (error = priv_check_cred(ap->a_cred, PRIV_ROOT, 0)))
58c2553a 288 goto done;
984263bc
MD
289 procp->p_pfsflags = flags;
290 break;
291 case PIOCGFL:
292 *(unsigned int*)ap->a_data = (unsigned int)procp->p_pfsflags;
293 case PIOCSTATUS:
294 psp = (struct procfs_status *)ap->a_data;
295 psp->state = (procp->p_step == 0);
296 psp->flags = procp->p_pfsflags;
297 psp->events = procp->p_stops;
298 if (procp->p_step) {
299 psp->why = procp->p_stype;
300 psp->val = procp->p_xstat;
301 } else {
302 psp->why = psp->val = 0; /* Not defined values */
303 }
304 break;
305 case PIOCWAIT:
306 psp = (struct procfs_status *)ap->a_data;
b2a6ad87 307 spin_lock(&procp->p_spin);
984263bc 308 if (procp->p_step == 0) {
b2a6ad87
MD
309 spin_unlock(&procp->p_spin);
310 tsleep_interlock(&procp->p_stype, PCATCH);
311 if (procp->p_stops == 0) {
312 error = EINVAL;
313 goto done;
314 }
315 if (procp->p_flags & P_POSTEXIT) {
316 error = EINVAL;
317 goto done;
318 }
319 if (procp->p_flags & P_INEXEC) {
320 error = EAGAIN;
321 goto done;
322 }
323 error = tsleep(&procp->p_stype, PCATCH | PINTERLOCKED,
324 "piocwait", 0);
984263bc 325 if (error)
58c2553a 326 goto done;
b2a6ad87
MD
327 } else {
328 spin_unlock(&procp->p_spin);
984263bc
MD
329 }
330 psp->state = 1; /* It stopped */
331 psp->flags = procp->p_pfsflags;
332 psp->events = procp->p_stops;
333 psp->why = procp->p_stype; /* why it stopped */
334 psp->val = procp->p_xstat; /* any extra info */
335 break;
336 case PIOCCONT: /* Restart a proc */
58c2553a
MD
337 if (procp->p_step == 0) {
338 error = EINVAL; /* Can only start a stopped process */
339 goto done;
340 }
984263bc 341 if ((signo = *(int*)ap->a_data) != 0) {
58c2553a
MD
342 if (signo >= NSIG || signo <= 0) {
343 error = EINVAL;
344 goto done;
345 }
84204577 346 ksignal(procp, signo);
984263bc
MD
347 }
348 procp->p_step = 0;
349 wakeup(&procp->p_step);
350 break;
351 default:
58c2553a
MD
352 error = ENOTTY;
353 goto done;
984263bc 354 }
58c2553a
MD
355 error = 0;
356done:
357 if (procp)
358 PRELE(procp);
359 return error;
984263bc
MD
360}
361
362/*
363 * do block mapping for pfsnode (vp).
364 * since we don't use the buffer cache
365 * for procfs this function should never
366 * be called. in any case, it's not clear
367 * what part of the kernel ever makes use
368 * of this function. for sanity, this is the
369 * usual no-op bmap, although returning
370 * (EIO) would be a reasonable alternative.
371 */
372static int
2da2a8af 373linprocfs_bmap(struct vop_bmap_args *ap)
984263bc 374{
54078292
MD
375 if (ap->a_doffsetp != NULL)
376 *ap->a_doffsetp = ap->a_loffset;
984263bc
MD
377 if (ap->a_runp != NULL)
378 *ap->a_runp = 0;
54078292
MD
379 if (ap->a_runb != NULL)
380 *ap->a_runb = 0;
984263bc
MD
381 return (0);
382}
383
384/*
385 * linprocfs_inactive is called when the pfsnode
5fd012e0
MD
386 * is vrele'd and the reference count is about
387 * to go to zero. (vp) will be on the vnode free
984263bc
MD
388 * list, so to get it back vget() must be
389 * used.
390 *
5fd012e0
MD
391 * (vp) is locked on entry and must remain locked
392 * on exit.
984263bc
MD
393 */
394static int
2da2a8af 395linprocfs_inactive(struct vop_inactive_args *ap)
984263bc 396{
5fd012e0 397 /*struct vnode *vp = ap->a_vp;*/
984263bc
MD
398
399 return (0);
400}
401
402/*
403 * _reclaim is called when getnewvnode()
404 * wants to make use of an entry on the vnode
405 * free list. at this time the filesystem needs
406 * to free any private data and remove the node
407 * from any private lists.
408 */
409static int
2da2a8af 410linprocfs_reclaim(struct vop_reclaim_args *ap)
984263bc 411{
984263bc
MD
412 return (linprocfs_freevp(ap->a_vp));
413}
414
415/*
416 * _print is used for debugging.
417 * just print a readable description
418 * of (vp).
419 */
420static int
2da2a8af 421linprocfs_print(struct vop_print_args *ap)
984263bc
MD
422{
423 struct pfsnode *pfs = VTOPFS(ap->a_vp);
424
26be20a0 425 kprintf("tag VT_PROCFS, type %d, pid %ld, mode %x, flags %lx\n",
984263bc
MD
426 pfs->pfs_type, (long)pfs->pfs_pid, pfs->pfs_mode, pfs->pfs_flags);
427 return (0);
428}
429
430/*
431 * generic entry point for unsupported operations
432 */
433static int
66a1ddf5 434linprocfs_badop(struct vop_generic_args *ap __unused)
984263bc
MD
435{
436
437 return (EIO);
438}
439
440/*
441 * Invent attributes for pfsnode (vp) and store
442 * them in (vap).
443 * Directories lengths are returned as zero since
444 * any real length would require the genuine size
445 * to be computed, and nothing cares anyway.
446 *
447 * this is relatively minimal for procfs.
448 */
449static int
2da2a8af 450linprocfs_getattr(struct vop_getattr_args *ap)
984263bc
MD
451{
452 struct pfsnode *pfs = VTOPFS(ap->a_vp);
453 struct vattr *vap = ap->a_vap;
454 struct proc *procp;
455 int error;
456
457 /*
458 * First make sure that the process and its credentials
459 * still exist.
460 */
461 switch (pfs->pfs_type) {
462 case Proot:
463 case Pself:
58c2553a 464 procp = NULL;
984263bc
MD
465 break;
466
467 default:
96a686f9 468 procp = linprocfs_pfind(pfs->pfs_pid);
4090d6ff 469 if (procp == NULL || procp->p_ucred == NULL)
984263bc
MD
470 return (ENOENT);
471 }
472
473 error = 0;
474
475 /* start by zeroing out the attributes */
476 VATTR_NULL(vap);
477
478 /* next do all the common fields */
479 vap->va_type = ap->a_vp->v_type;
480 vap->va_mode = pfs->pfs_mode;
481 vap->va_fileid = pfs->pfs_fileno;
482 vap->va_flags = 0;
483 vap->va_blocksize = PAGE_SIZE;
484 vap->va_bytes = vap->va_size = 0;
485 vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
486
487 /*
488 * Make all times be current TOD.
489 * It would be possible to get the process start
490 * time from the p_stat structure, but there's
491 * no "file creation" time stamp anyway, and the
492 * p_stat structure is not addressible if u. gets
493 * swapped out for that process.
494 */
495 nanotime(&vap->va_ctime);
496 vap->va_atime = vap->va_mtime = vap->va_ctime;
497
498 /*
499 * now do the object specific fields
500 *
501 * The size could be set from struct reg, but it's hardly
502 * worth the trouble, and it puts some (potentially) machine
503 * dependent data into this machine-independent code. If it
504 * becomes important then this function should break out into
505 * a per-file stat function in the corresponding .c file.
506 */
507
508 vap->va_nlink = 1;
509 if (procp) {
510 vap->va_uid = procp->p_ucred->cr_uid;
511 vap->va_gid = procp->p_ucred->cr_gid;
512 }
513
514 switch (pfs->pfs_type) {
515 case Proot:
a1f82243
AH
516 case Pnet:
517 case Psys:
518 case Psyskernel:
984263bc
MD
519 /*
520 * Set nlink to 1 to tell fts(3) we don't actually know.
521 */
522 vap->va_nlink = 1;
523 vap->va_uid = 0;
524 vap->va_gid = 0;
525 vap->va_size = vap->va_bytes = DEV_BSIZE;
526 break;
527
528 case Pself: {
529 char buf[16]; /* should be enough */
530 vap->va_uid = 0;
531 vap->va_gid = 0;
532 vap->va_size = vap->va_bytes =
f8c7a42d 533 ksnprintf(buf, sizeof(buf), "%ld", (long)curproc->p_pid);
984263bc
MD
534 break;
535 }
536
537 case Pproc:
538 vap->va_nlink = nproc_targets;
539 vap->va_size = vap->va_bytes = DEV_BSIZE;
540 break;
541
542 case Pexe: {
543 char *fullpath, *freepath;
5b4cfb7e 544 error = cache_fullpath(procp, &procp->p_textnch, &fullpath, &freepath, 0);
a1f82243 545 /* error = vn_fullpath(procp, NULL, &fullpath, &freepath); */
984263bc
MD
546 if (error == 0) {
547 vap->va_size = strlen(fullpath);
efda3bd0 548 kfree(freepath, M_TEMP);
984263bc
MD
549 } else {
550 vap->va_size = sizeof("unknown") - 1;
551 error = 0;
552 }
553 vap->va_bytes = vap->va_size;
554 break;
555 }
a1f82243
AH
556 case Pcwd: {
557 char *fullpath, *freepath;
5b4cfb7e 558 error = cache_fullpath(procp, &procp->p_fd->fd_ncdir, &fullpath, &freepath, 0);
a1f82243
AH
559 if (error == 0) {
560 vap->va_size = strlen(fullpath);
561 kfree(freepath, M_TEMP);
562 } else {
563 vap->va_size = sizeof("unknown") - 1;
564 error = 0;
565 }
566 vap->va_bytes = vap->va_size;
567 break;
568 }
569 case Pprocroot: {
570 struct nchandle *nchp;
571 char *fullpath, *freepath;
572 nchp = jailed(procp->p_ucred) ? &procp->p_fd->fd_njdir : &procp->p_fd->fd_nrdir;
5b4cfb7e 573 error = cache_fullpath(procp, nchp, &fullpath, &freepath, 0);
a1f82243
AH
574 if (error == 0) {
575 vap->va_size = strlen(fullpath);
576 kfree(freepath, M_TEMP);
577 } else {
578 vap->va_size = sizeof("unknown") - 1;
579 error = 0;
580 }
581 vap->va_bytes = vap->va_size;
582 break;
583 }
584 case Pfd: {
585 if (procp == curproc) {
586 vap->va_size = sizeof("/dev/fd") - 1;
587 error = 0;
588 } else {
589 vap->va_size = sizeof("unknown") - 1;
590 error = 0;
591 }
592 vap->va_bytes = vap->va_size;
593 break;
594 }
984263bc
MD
595
596 case Pmeminfo:
597 case Pcpuinfo:
a3c5067f 598 case Pmounts:
984263bc
MD
599 case Pstat:
600 case Puptime:
601 case Pversion:
602 case Ploadavg:
a1f82243
AH
603 case Pnetdev:
604 case Pdevices:
605 case Posrelease:
606 case Postype:
607 case Ppidmax:
984263bc
MD
608 vap->va_bytes = vap->va_size = 0;
609 vap->va_uid = 0;
610 vap->va_gid = 0;
611 break;
612
613 case Pmem:
614 /*
615 * If we denied owner access earlier, then we have to
616 * change the owner to root - otherwise 'ps' and friends
617 * will break even though they are setgid kmem. *SIGH*
618 */
4643740a 619 if (procp->p_flags & P_SUGID)
984263bc
MD
620 vap->va_uid = 0;
621 else
622 vap->va_uid = procp->p_ucred->cr_uid;
623 break;
624
625 case Pprocstat:
626 case Pprocstatus:
a1f82243
AH
627 case Pcmdline:
628 case Penviron:
629 case Pmaps:
630 case Pstatm:
984263bc
MD
631 vap->va_bytes = vap->va_size = 0;
632 /* uid, gid are already set */
633 break;
634
635 default:
636 panic("linprocfs_getattr");
637 }
638
639 return (error);
640}
641
642static int
2da2a8af 643linprocfs_setattr(struct vop_setattr_args *ap)
984263bc
MD
644{
645
646 if (ap->a_vap->va_flags != VNOVAL)
647 return (EOPNOTSUPP);
648
649 /*
650 * just fake out attribute setting
651 * it's not good to generate an error
652 * return, otherwise things like creat()
653 * will fail when they try to set the
654 * file length to 0. worse, this means
655 * that echo $note > /proc/$pid/note will fail.
656 */
657
658 return (0);
659}
660
661/*
662 * implement access checking.
663 *
664 * something very similar to this code is duplicated
665 * throughout the 4bsd kernel and should be moved
666 * into kern/vfs_subr.c sometime.
667 *
668 * actually, the check for super-user is slightly
669 * broken since it will allow read access to write-only
670 * objects. this doesn't cause any particular trouble
671 * but does mean that the i/o entry points need to check
672 * that the operation really does make sense.
673 */
674static int
2da2a8af 675linprocfs_access(struct vop_access_args *ap)
984263bc
MD
676{
677 struct vattr *vap;
678 struct vattr vattr;
679 int error;
680
681 /*
682 * If you're the super-user,
683 * you always get access.
684 */
685 if (ap->a_cred->cr_uid == 0)
686 return (0);
687
688 vap = &vattr;
87de5057 689 error = VOP_GETATTR(ap->a_vp, vap);
984263bc
MD
690 if (error)
691 return (error);
692
693 /*
694 * Access check is based on only one of owner, group, public.
695 * If not owner, then check group. If not a member of the
696 * group, then check public access.
697 */
698 if (ap->a_cred->cr_uid != vap->va_uid) {
699 gid_t *gp;
700 int i;
701
702 ap->a_mode >>= 3;
703 gp = ap->a_cred->cr_groups;
704 for (i = 0; i < ap->a_cred->cr_ngroups; i++, gp++)
705 if (vap->va_gid == *gp)
706 goto found;
707 ap->a_mode >>= 3;
708found:
709 ;
710 }
711
712 if ((vap->va_mode & ap->a_mode) == ap->a_mode)
713 return (0);
714
715 return (EACCES);
716}
717
718/*
ed5d0d4a
MD
719 * lookup. this is incredibly complicated in the general case, however
720 * for most pseudo-filesystems very little needs to be done.
984263bc
MD
721 */
722static int
2da2a8af 723linprocfs_lookup(struct vop_old_lookup_args *ap)
984263bc
MD
724{
725 struct componentname *cnp = ap->a_cnp;
726 struct vnode **vpp = ap->a_vpp;
727 struct vnode *dvp = ap->a_dvp;
728 char *pname = cnp->cn_nameptr;
729 struct proc_target *pt;
730 pid_t pid;
731 struct pfsnode *pfs;
732 struct proc *p;
733 int i;
ed5d0d4a 734 int error;
984263bc
MD
735
736 *vpp = NULL;
737
2b69e610
MD
738 if (cnp->cn_nameiop == NAMEI_DELETE ||
739 cnp->cn_nameiop == NAMEI_RENAME ||
740 cnp->cn_nameiop == NAMEI_CREATE) {
984263bc 741 return (EROFS);
2b69e610 742 }
984263bc 743
ed5d0d4a
MD
744 error = 0;
745
984263bc
MD
746 if (cnp->cn_namelen == 1 && *pname == '.') {
747 *vpp = dvp;
ed5d0d4a
MD
748 vref(*vpp);
749 goto out;
984263bc
MD
750 }
751
752 pfs = VTOPFS(dvp);
753 switch (pfs->pfs_type) {
a1f82243
AH
754 case Psys:
755 if (cnp->cn_flags & CNP_ISDOTDOT) {
756 error = linprocfs_root(dvp->v_mount, vpp);
757 goto out;
758 }
759 if (CNEQ(cnp, "kernel", 6)) {
760 error = linprocfs_allocvp(dvp->v_mount, vpp, 0, Psyskernel);
761 goto out;
762 }
763 break;
764 case Pnet:
765 if (cnp->cn_flags & CNP_ISDOTDOT) {
766 error = linprocfs_root(dvp->v_mount, vpp);
767 goto out;
768 }
769 if (CNEQ(cnp, "dev", 3)) {
770 error = linprocfs_allocvp(dvp->v_mount, vpp, 0, Pnetdev);
771 goto out;
772 }
773 break;
774 case Psyskernel:
775 if (cnp->cn_flags & CNP_ISDOTDOT) {
776 /* XXX: this is wrong, wrong, wrong. */
777 error = linprocfs_root(dvp->v_mount, vpp);
778 goto out;
779 }
780 if (CNEQ(cnp, "osrelease", 9)) {
781 error = linprocfs_allocvp(dvp->v_mount, vpp, 0, Posrelease);
782 goto out;
783 }
784 if (CNEQ(cnp, "ostype", 6)) {
785 error = linprocfs_allocvp(dvp->v_mount, vpp, 0, Postype);
786 goto out;
787 }
788 if (CNEQ(cnp, "pid_max", 7)) {
789 error = linprocfs_allocvp(dvp->v_mount, vpp, 0, Ppidmax);
790 goto out;
791 }
792 if (CNEQ(cnp, "version", 7)) {
793 error = linprocfs_allocvp(dvp->v_mount, vpp, 0, Pversion);
794 goto out;
795 }
796 break;
797
984263bc 798 case Proot:
2b69e610 799 if (cnp->cn_flags & CNP_ISDOTDOT)
984263bc
MD
800 return (EIO);
801
ed5d0d4a
MD
802 if (CNEQ(cnp, "self", 4)) {
803 error = linprocfs_allocvp(dvp->v_mount, vpp, 0, Pself);
804 goto out;
805 }
806 if (CNEQ(cnp, "meminfo", 7)) {
807 error = linprocfs_allocvp(dvp->v_mount, vpp, 0, Pmeminfo);
808 goto out;
809 }
810 if (CNEQ(cnp, "cpuinfo", 7)) {
811 error = linprocfs_allocvp(dvp->v_mount, vpp, 0, Pcpuinfo);
812 goto out;
813 }
a3c5067f
AH
814 if (CNEQ(cnp, "mounts", 6)) {
815 error = linprocfs_allocvp(dvp->v_mount, vpp, 0, Pmounts);
816 goto out;
817 }
ed5d0d4a
MD
818 if (CNEQ(cnp, "stat", 4)) {
819 error = linprocfs_allocvp(dvp->v_mount, vpp, 0, Pstat);
820 goto out;
821 }
822 if (CNEQ(cnp, "uptime", 6)) {
823 error = linprocfs_allocvp(dvp->v_mount, vpp, 0, Puptime);
824 goto out;
825 }
826 if (CNEQ(cnp, "version", 7)) {
827 error = linprocfs_allocvp(dvp->v_mount, vpp, 0, Pversion);
828 goto out;
829 }
830 if (CNEQ(cnp, "loadavg", 7)) {
831 error = linprocfs_allocvp(dvp->v_mount, vpp, 0, Ploadavg);
832 goto out;
833 }
a1f82243
AH
834 if (CNEQ(cnp, "net", 3)) {
835 error = linprocfs_allocvp(dvp->v_mount, vpp, 0, Pnet);
836 goto out;
837 }
838 if (CNEQ(cnp, "sys", 3)) {
839 error = linprocfs_allocvp(dvp->v_mount, vpp, 0, Psys);
840 goto out;
841 }
984263bc
MD
842
843 pid = atopid(pname, cnp->cn_namelen);
844 if (pid == NO_PID)
845 break;
846
96a686f9 847 p = linprocfs_pfind(pid);
4090d6ff 848 if (p == NULL)
984263bc
MD
849 break;
850
570ace91
JS
851 if (!PRISON_CHECK(ap->a_cnp->cn_cred, p->p_ucred))
852 break;
853
854 if (ps_showallprocs == 0 && ap->a_cnp->cn_cred->cr_uid != 0 &&
855 ap->a_cnp->cn_cred->cr_uid != p->p_ucred->cr_uid)
856 break;
857
ed5d0d4a
MD
858 error = linprocfs_allocvp(dvp->v_mount, vpp, pid, Pproc);
859 goto out;
984263bc
MD
860
861 case Pproc:
ed5d0d4a
MD
862 if (cnp->cn_flags & CNP_ISDOTDOT) {
863 error = linprocfs_root(dvp->v_mount, vpp);
864 goto out;
865 }
984263bc 866
96a686f9 867 p = linprocfs_pfind(pfs->pfs_pid);
4090d6ff 868 if (p == NULL)
984263bc
MD
869 break;
870
570ace91
JS
871 if (!PRISON_CHECK(ap->a_cnp->cn_cred, p->p_ucred))
872 break;
873
874 if (ps_showallprocs == 0 && ap->a_cnp->cn_cred->cr_uid != 0 &&
875 ap->a_cnp->cn_cred->cr_uid != p->p_ucred->cr_uid)
876 break;
877
984263bc
MD
878 for (pt = proc_targets, i = 0; i < nproc_targets; pt++, i++) {
879 if (cnp->cn_namelen == pt->pt_namlen &&
880 bcmp(pt->pt_name, pname, cnp->cn_namelen) == 0 &&
881 (pt->pt_valid == NULL || (*pt->pt_valid)(p)))
882 goto found;
883 }
884 break;
885
886 found:
ed5d0d4a
MD
887 error = linprocfs_allocvp(dvp->v_mount, vpp, pfs->pfs_pid,
888 pt->pt_pfstype);
889 goto out;
984263bc
MD
890
891 default:
ed5d0d4a
MD
892 error = ENOTDIR;
893 goto out;
984263bc
MD
894 }
895
ed5d0d4a
MD
896 if (cnp->cn_nameiop == NAMEI_LOOKUP)
897 error = ENOENT;
898 else
899 error = EROFS;
900
901 /*
902 * If no error occured *vpp will hold a referenced locked vnode.
903 * dvp was passed to us locked and *vpp must be returned locked
904 * so if dvp != *vpp and CNP_LOCKPARENT is not set, unlock dvp.
905 */
906out:
907 if (error == 0) {
908 if (*vpp != dvp && (cnp->cn_flags & CNP_LOCKPARENT) == 0) {
909 cnp->cn_flags |= CNP_PDIRUNLOCK;
a11aaa81 910 vn_unlock(dvp);
ed5d0d4a
MD
911 }
912 }
913 return (error);
984263bc
MD
914}
915
916/*
917 * Does this process have a text file?
918 */
919int
2da2a8af 920linprocfs_validfile(struct proc *p)
984263bc
MD
921{
922
923 return (procfs_findtextvp(p) != NULLVP);
924}
925
926/*
927 * readdir() returns directory entries from pfsnode (vp).
928 *
929 * We generate just one directory entry at a time, as it would probably
930 * not pay off to buffer several entries locally to save uiomove calls.
9c274f28
JS
931 *
932 * linprocfs_readdir(struct vnode *a_vp, struct uio *a_uio,
933 * struct ucred *a_cred, int *a_eofflag,
84009d92 934 * int *a_ncookies, off_t **a_cookies)
984263bc
MD
935 */
936static int
9c274f28 937linprocfs_readdir(struct vop_readdir_args *ap)
984263bc 938{
984263bc 939 struct pfsnode *pfs;
9c274f28 940 int error;
984263bc 941
570ace91 942 if (ap->a_uio->uio_offset < 0 || ap->a_uio->uio_offset > INT_MAX)
984263bc
MD
943 return (EINVAL);
944
9c274f28 945 pfs = VTOPFS(ap->a_vp);
885ecb13
MD
946 if ((error = vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY)) != 0)
947 return (error);
984263bc
MD
948
949 switch (pfs->pfs_type) {
9c274f28 950 case Pproc:
885ecb13
MD
951 /*
952 * This is for the process-specific sub-directories.
953 * all that is needed to is copy out all the entries
954 * from the procent[] table (top of this file).
955 */
9c274f28
JS
956 error = linprocfs_readdir_proc(ap);
957 break;
9c274f28 958 case Proot:
885ecb13
MD
959 /*
960 * This is for the root of the procfs filesystem
961 * what is needed is a special entry for "self"
962 * followed by an entry for each process on allproc
963 */
9c274f28 964 error = linprocfs_readdir_root(ap);
984263bc 965 break;
a1f82243
AH
966 case Pnet:
967 error = linprocfs_readdir_net(ap);
968 break;
969 case Psys:
970 error = linprocfs_readdir_sys(ap);
971 break;
972 case Psyskernel:
973 error = linprocfs_readdir_syskernel(ap);
974 break;
984263bc
MD
975 default:
976 error = ENOTDIR;
977 break;
978 }
885ecb13 979 vn_unlock(ap->a_vp);
984263bc 980
984263bc
MD
981 return (error);
982}
983
9c274f28
JS
984static int
985linprocfs_readdir_proc(struct vop_readdir_args *ap)
986{
987 struct pfsnode *pfs;
988 int error, i, retval;
989 struct proc *p;
990 struct proc_target *pt;
991 struct uio *uio = ap->a_uio;
992
9c274f28 993 pfs = VTOPFS(ap->a_vp);
96a686f9 994 p = linprocfs_pfind(pfs->pfs_pid);
9c274f28
JS
995 if (p == NULL)
996 return(0);
997 if (!PRISON_CHECK(ap->a_cred, p->p_ucred))
998 return(0);
999
1000 error = 0;
1001 i = uio->uio_offset;
1002
1003 for (pt = &proc_targets[i];
1004 !error && uio->uio_resid > 0 && i < nproc_targets; pt++, i++) {
1005 if (pt->pt_valid && (*pt->pt_valid)(p) == 0)
1006 continue;
1007
1008 retval = vop_write_dirent(&error, uio,
1009 PROCFS_FILENO(pfs->pfs_pid, pt->pt_pfstype), pt->pt_type,
1010 pt->pt_namlen, pt->pt_name);
1011 if (retval)
1012 break;
1013 }
1014
1015 uio->uio_offset = i;
1016
1017 return(error);
1018}
1019
5bf0d9b5
MD
1020struct linprocfs_readdir_root_info {
1021 int error;
1022 int pcnt;
1023 int i;
1024 struct uio *uio;
1025 struct ucred *cred;
1026};
1027
1028/*
1029 * Scan the root directory by scanning all process
1030 */
1031static int linprocfs_readdir_root_callback(struct proc *p, void *data);
1032
9c274f28
JS
1033static int
1034linprocfs_readdir_root(struct vop_readdir_args *ap)
1035{
5bf0d9b5 1036 struct linprocfs_readdir_root_info info;
9c274f28 1037 struct uio *uio = ap->a_uio;
a1f82243 1038 int res = 0;
5bf0d9b5
MD
1039
1040 info.error = 0;
1041 info.i = uio->uio_offset;
1042 info.pcnt = 0;
1043 info.uio = uio;
1044 info.cred = ap->a_cred;
1045
a3c5067f 1046 while (info.pcnt < 12) {
5bf0d9b5
MD
1047 res = linprocfs_readdir_root_callback(NULL, &info);
1048 if (res < 0)
1049 break;
1050 }
1051 if (res >= 0)
1052 allproc_scan(linprocfs_readdir_root_callback, &info);
1053
1054 uio->uio_offset = info.i;
1055 return(info.error);
1056}
1057
1058static int
1059linprocfs_readdir_root_callback(struct proc *p, void *data)
1060{
1061 struct linprocfs_readdir_root_info *info = data;
1062 int retval;
1063 struct uio *uio = info->uio;
9c274f28
JS
1064 ino_t d_ino;
1065 const char *d_name;
1066 char d_name_pid[20];
1067 size_t d_namlen;
1068 uint8_t d_type;
1069
5bf0d9b5
MD
1070 switch (info->pcnt) {
1071 case 0: /* `.' */
1072 d_ino = PROCFS_FILENO(0, Proot);
1073 d_name = ".";
1074 d_namlen = 1;
1075 d_type = DT_DIR;
1076 break;
1077 case 1: /* `..' */
1078 d_ino = PROCFS_FILENO(0, Proot);
1079 d_name = "..";
1080 d_namlen = 2;
1081 d_type = DT_DIR;
1082 break;
9c274f28 1083
5bf0d9b5
MD
1084 case 2:
1085 d_ino = PROCFS_FILENO(0, Proot);
1086 d_namlen = 4;
1087 d_name = "self";
1088 d_type = DT_LNK;
1089 break;
9c274f28 1090
5bf0d9b5
MD
1091 case 3:
1092 d_ino = PROCFS_FILENO(0, Pmeminfo);
1093 d_namlen = 7;
1094 d_name = "meminfo";
1095 d_type = DT_REG;
1096 break;
9c274f28 1097
5bf0d9b5
MD
1098 case 4:
1099 d_ino = PROCFS_FILENO(0, Pcpuinfo);
1100 d_namlen = 7;
1101 d_name = "cpuinfo";
1102 d_type = DT_REG;
1103 break;
9c274f28 1104
5bf0d9b5
MD
1105 case 5:
1106 d_ino = PROCFS_FILENO(0, Pstat);
1107 d_namlen = 4;
1108 d_name = "stat";
1109 d_type = DT_REG;
1110 break;
1111
1112 case 6:
1113 d_ino = PROCFS_FILENO(0, Puptime);
1114 d_namlen = 6;
1115 d_name = "uptime";
1116 d_type = DT_REG;
1117 break;
9c274f28 1118
5bf0d9b5
MD
1119 case 7:
1120 d_ino = PROCFS_FILENO(0, Pversion);
1121 d_namlen = 7;
1122 d_name = "version";
1123 d_type = DT_REG;
1124 break;
9c274f28 1125
5bf0d9b5
MD
1126 case 8:
1127 d_ino = PROCFS_FILENO(0, Ploadavg);
1128 d_namlen = 7;
1129 d_name = "loadavg";
1130 d_type = DT_REG;
1131 break;
a1f82243
AH
1132 case 9:
1133 d_ino = PROCFS_FILENO(0, Pnet);
1134 d_namlen = 3;
1135 d_name = "net";
1136 d_type = DT_DIR;
1137 break;
1138 case 10:
1139 d_ino = PROCFS_FILENO(0, Psys);
1140 d_namlen = 3;
1141 d_name = "sys";
1142 d_type = DT_DIR;
1143 break;
a3c5067f
AH
1144 case 11:
1145 d_ino = PROCFS_FILENO(0, Pmounts);
1146 d_namlen = 6;
1147 d_name = "mounts";
1148 d_type = DT_DIR;
1149 break;
a1f82243
AH
1150#if 0
1151 case 11:
1152 d_ino = PROCFS_FILENO(0, Pdevices);
1153 d_namlen = 7;
1154 d_name = "devices";
1155 d_type = DT_REG;
1156 break;
1157#endif
5bf0d9b5
MD
1158 default:
1159 /*
1160 * Ignore processes that aren't in our prison
1161 */
1162 if (PRISON_CHECK(info->cred, p->p_ucred) == 0)
1163 return(0);
1164
1165 /*
1166 * Ignore processes that we do not want to be visible.
1167 */
1168 if (ps_showallprocs == 0 &&
1169 info->cred->cr_uid != 0 &&
1170 info->cred->cr_uid != p->p_ucred->cr_uid) {
1171 return(0);
9c274f28
JS
1172 }
1173
5bf0d9b5
MD
1174 /*
1175 * Skip processes we have already read (optimization)
1176 */
1177 if (info->pcnt < info->i) {
1178 ++info->pcnt;
1179 return(0);
1180 }
1181 d_ino = PROCFS_FILENO(p->p_pid, Pproc);
f8c7a42d 1182 d_namlen = ksnprintf(d_name_pid, sizeof(d_name_pid),
5bf0d9b5
MD
1183 "%ld", (long)p->p_pid);
1184 d_name = d_name_pid;
1185 d_type = DT_DIR;
1186 break;
1187 }
1188
1189 /*
1190 * Skip processes we have already read
1191 */
1192 if (info->pcnt < info->i) {
1193 ++info->pcnt;
1194 return(0);
1195 }
1196 retval = vop_write_dirent(&info->error, info->uio,
1197 d_ino, d_type, d_namlen, d_name);
1198 if (retval == 0) {
1199 ++info->pcnt; /* iterate proc candidates scanned */
1200 ++info->i; /* iterate entries written */
1201 }
1202 if (retval || info->error || uio->uio_resid <= 0)
1203 return(-1);
1204 return(0);
9c274f28
JS
1205}
1206
984263bc 1207/*
a1f82243
AH
1208 * Scan the root directory by scanning all process
1209 */
1210static int linprocfs_readdir_net_callback(struct proc *p, void *data);
1211
1212static int
1213linprocfs_readdir_net(struct vop_readdir_args *ap)
1214{
1215 struct linprocfs_readdir_root_info info;
1216 struct uio *uio = ap->a_uio;
1217 int res;
1218
1219 info.error = 0;
1220 info.i = uio->uio_offset;
1221 info.pcnt = 0;
1222 info.uio = uio;
1223 info.cred = ap->a_cred;
1224
1225 while (info.pcnt < 3) {
1226 res = linprocfs_readdir_net_callback(NULL, &info);
1227 if (res < 0)
1228 break;
1229 }
1230
1231 uio->uio_offset = info.i;
1232 return(info.error);
1233}
1234
1235static int
1236linprocfs_readdir_net_callback(struct proc *p, void *data)
1237{
1238 struct linprocfs_readdir_root_info *info = data;
1239 int retval;
1240 struct uio *uio = info->uio;
1241 ino_t d_ino;
1242 const char *d_name;
1243 size_t d_namlen;
1244 uint8_t d_type;
1245
1246 switch (info->pcnt) {
1247 case 0: /* `.' */
1248 d_ino = PROCFS_FILENO(0, Pnet);
1249 d_name = ".";
1250 d_namlen = 1;
1251 d_type = DT_DIR;
1252 break;
1253 case 1: /* `..' */
1254 d_ino = PROCFS_FILENO(0, Proot);
1255 d_name = "..";
1256 d_namlen = 2;
1257 d_type = DT_DIR;
1258 break;
1259
1260 case 2:
1261 d_ino = PROCFS_FILENO(0, Pnet);
1262 d_namlen = 3;
1263 d_name = "dev";
1264 d_type = DT_REG;
1265 break;
1266 default:
1267 d_ino = 0;
1268 d_namlen = 0;
1269 d_name = NULL;
1270 d_type = DT_REG;
1271 break;
1272 }
1273
1274 /*
1275 * Skip processes we have already read
1276 */
1277 if (info->pcnt < info->i) {
1278 ++info->pcnt;
1279 return(0);
1280 }
1281 retval = vop_write_dirent(&info->error, info->uio,
1282 d_ino, d_type, d_namlen, d_name);
1283 if (retval == 0) {
1284 ++info->pcnt; /* iterate proc candidates scanned */
1285 ++info->i; /* iterate entries written */
1286 }
1287 if (retval || info->error || uio->uio_resid <= 0)
1288 return(-1);
1289 return(0);
1290}
1291
1292
1293
1294
1295
1296
1297
1298/*
1299 * Scan the root directory by scanning all process
1300 */
1301static int linprocfs_readdir_sys_callback(struct proc *p, void *data);
1302
1303static int
1304linprocfs_readdir_sys(struct vop_readdir_args *ap)
1305{
1306 struct linprocfs_readdir_root_info info;
1307 struct uio *uio = ap->a_uio;
1308 int res;
1309
1310 info.error = 0;
1311 info.i = uio->uio_offset;
1312 info.pcnt = 0;
1313 info.uio = uio;
1314 info.cred = ap->a_cred;
1315
1316 while (info.pcnt < 3) {
1317 res = linprocfs_readdir_sys_callback(NULL, &info);
1318 if (res < 0)
1319 break;
1320 }
1321
1322 uio->uio_offset = info.i;
1323 return(info.error);
1324}
1325
1326static int
1327linprocfs_readdir_sys_callback(struct proc *p, void *data)
1328{
1329 struct linprocfs_readdir_root_info *info = data;
1330 int retval;
1331 struct uio *uio = info->uio;
1332 ino_t d_ino;
1333 const char *d_name;
1334 size_t d_namlen;
1335 uint8_t d_type;
1336
1337 switch (info->pcnt) {
1338 case 0: /* `.' */
1339 d_ino = PROCFS_FILENO(0, Psys);
1340 d_name = ".";
1341 d_namlen = 1;
1342 d_type = DT_DIR;
1343 break;
1344 case 1: /* `..' */
1345 d_ino = PROCFS_FILENO(0, Proot);
1346 d_name = "..";
1347 d_namlen = 2;
1348 d_type = DT_DIR;
1349 break;
1350
1351 case 2:
1352 d_ino = PROCFS_FILENO(0, Psyskernel);
1353 d_namlen = 6;
1354 d_name = "kernel";
1355 d_type = DT_DIR;
1356 break;
1357 default:
1358 d_ino = 0;
1359 d_namlen = 0;
1360 d_name = NULL;
1361 d_type = DT_REG;
1362 break;
1363 }
1364
1365 /*
1366 * Skip processes we have already read
1367 */
1368 if (info->pcnt < info->i) {
1369 ++info->pcnt;
1370 return(0);
1371 }
1372 retval = vop_write_dirent(&info->error, info->uio,
1373 d_ino, d_type, d_namlen, d_name);
1374 if (retval == 0) {
1375 ++info->pcnt; /* iterate proc candidates scanned */
1376 ++info->i; /* iterate entries written */
1377 }
1378 if (retval || info->error || uio->uio_resid <= 0)
1379 return(-1);
1380 return(0);
1381}
1382
1383
1384
1385
1386
1387/*
1388 * Scan the root directory by scanning all process
1389 */
1390static int linprocfs_readdir_syskernel_callback(struct proc *p, void *data);
1391
1392static int
1393linprocfs_readdir_syskernel(struct vop_readdir_args *ap)
1394{
1395 struct linprocfs_readdir_root_info info;
1396 struct uio *uio = ap->a_uio;
1397 int res;
1398
1399 info.error = 0;
1400 info.i = uio->uio_offset;
1401 info.pcnt = 0;
1402 info.uio = uio;
1403 info.cred = ap->a_cred;
1404
1405 while (info.pcnt < 6) {
1406 res = linprocfs_readdir_syskernel_callback(NULL, &info);
1407 if (res < 0)
1408 break;
1409 }
1410
1411 uio->uio_offset = info.i;
1412 return(info.error);
1413}
1414
1415static int
1416linprocfs_readdir_syskernel_callback(struct proc *p, void *data)
1417{
1418 struct linprocfs_readdir_root_info *info = data;
1419 int retval;
1420 struct uio *uio = info->uio;
1421 ino_t d_ino;
1422 const char *d_name;
1423 size_t d_namlen;
1424 uint8_t d_type;
1425
1426 switch (info->pcnt) {
1427 case 0: /* `.' */
1428 d_ino = PROCFS_FILENO(0, Psyskernel);
1429 d_name = ".";
1430 d_namlen = 1;
1431 d_type = DT_DIR;
1432 break;
1433 case 1: /* `..' */
1434 d_ino = PROCFS_FILENO(0, Psys);
1435 d_name = "..";
1436 d_namlen = 2;
1437 d_type = DT_DIR;
1438 break;
1439
1440 case 2:
1441 d_ino = PROCFS_FILENO(0, Posrelease);
1442 d_namlen = 9;
1443 d_name = "osrelease";
1444 d_type = DT_REG;
1445 break;
1446
1447 case 3:
1448 d_ino = PROCFS_FILENO(0, Postype);
1449 d_namlen = 4;
1450 d_name = "ostype";
1451 d_type = DT_REG;
1452 break;
1453
1454 case 4:
1455 d_ino = PROCFS_FILENO(0, Pversion);
1456 d_namlen = 7;
1457 d_name = "version";
1458 d_type = DT_REG;
1459 break;
1460
1461 case 5:
1462 d_ino = PROCFS_FILENO(0, Ppidmax);
1463 d_namlen = 7;
1464 d_name = "pid_max";
1465 d_type = DT_REG;
1466 break;
1467 default:
1468 d_ino = 0;
1469 d_namlen = 0;
1470 d_name = NULL;
1471 d_type = DT_REG;
1472 break;
1473 }
1474
1475 /*
1476 * Skip processes we have already read
1477 */
1478 if (info->pcnt < info->i) {
1479 ++info->pcnt;
1480 return(0);
1481 }
1482 retval = vop_write_dirent(&info->error, info->uio,
1483 d_ino, d_type, d_namlen, d_name);
1484 if (retval == 0) {
1485 ++info->pcnt; /* iterate proc candidates scanned */
1486 ++info->i; /* iterate entries written */
1487 }
1488 if (retval || info->error || uio->uio_resid <= 0)
1489 return(-1);
1490 return(0);
1491}
1492
1493/*
984263bc
MD
1494 * readlink reads the link of `self' or `exe'
1495 */
1496static int
2da2a8af 1497linprocfs_readlink(struct vop_readlink_args *ap)
984263bc
MD
1498{
1499 char buf[16]; /* should be enough */
1500 struct proc *procp;
1501 struct vnode *vp = ap->a_vp;
a1f82243 1502 struct nchandle *nchp;
984263bc
MD
1503 struct pfsnode *pfs = VTOPFS(vp);
1504 char *fullpath, *freepath;
1505 int error, len;
1506
1507 switch (pfs->pfs_type) {
1508 case Pself:
1509 if (pfs->pfs_fileno != PROCFS_FILENO(0, Pself))
1510 return (EINVAL);
1511
f8c7a42d 1512 len = ksnprintf(buf, sizeof(buf), "%ld", (long)curproc->p_pid);
984263bc
MD
1513
1514 return (uiomove(buf, len, ap->a_uio));
1515 /*
1516 * There _should_ be no way for an entire process to disappear
1517 * from under us...
1518 */
1519 case Pexe:
96a686f9 1520 procp = linprocfs_pfind(pfs->pfs_pid);
41c20dac 1521 if (procp == NULL || procp->p_ucred == NULL) {
26be20a0 1522 kprintf("linprocfs_readlink: pid %d disappeared\n",
984263bc
MD
1523 pfs->pfs_pid);
1524 return (uiomove("unknown", sizeof("unknown") - 1,
1525 ap->a_uio));
1526 }
5b4cfb7e 1527 error = cache_fullpath(procp, &procp->p_textnch, &fullpath, &freepath, 0);
a1f82243
AH
1528 if (error != 0)
1529 return (uiomove("unknown", sizeof("unknown") - 1,
1530 ap->a_uio));
1531 error = uiomove(fullpath, strlen(fullpath), ap->a_uio);
1532 kfree(freepath, M_TEMP);
1533 return (error);
1534 case Pcwd:
96a686f9 1535 procp = linprocfs_pfind(pfs->pfs_pid);
a1f82243
AH
1536 if (procp == NULL || procp->p_ucred == NULL) {
1537 kprintf("linprocfs_readlink: pid %d disappeared\n",
1538 pfs->pfs_pid);
1539 return (uiomove("unknown", sizeof("unknown") - 1,
1540 ap->a_uio));
1541 }
5b4cfb7e 1542 error = cache_fullpath(procp, &procp->p_fd->fd_ncdir, &fullpath, &freepath, 0);
a1f82243
AH
1543 if (error != 0)
1544 return (uiomove("unknown", sizeof("unknown") - 1,
1545 ap->a_uio));
1546 error = uiomove(fullpath, strlen(fullpath), ap->a_uio);
1547 kfree(freepath, M_TEMP);
1548 return (error);
1549 case Pprocroot:
96a686f9 1550 procp = linprocfs_pfind(pfs->pfs_pid);
a1f82243
AH
1551 if (procp == NULL || procp->p_ucred == NULL) {
1552 kprintf("linprocfs_readlink: pid %d disappeared\n",
1553 pfs->pfs_pid);
1554 return (uiomove("unknown", sizeof("unknown") - 1,
1555 ap->a_uio));
1556 }
1557 nchp = jailed(procp->p_ucred) ? &procp->p_fd->fd_njdir : &procp->p_fd->fd_nrdir;
5b4cfb7e 1558 error = cache_fullpath(procp, nchp, &fullpath, &freepath, 0);
984263bc
MD
1559 if (error != 0)
1560 return (uiomove("unknown", sizeof("unknown") - 1,
1561 ap->a_uio));
1562 error = uiomove(fullpath, strlen(fullpath), ap->a_uio);
efda3bd0 1563 kfree(freepath, M_TEMP);
984263bc 1564 return (error);
a1f82243 1565 case Pfd:
96a686f9 1566 procp = linprocfs_pfind(pfs->pfs_pid);
a1f82243
AH
1567 if (procp == NULL || procp->p_ucred == NULL) {
1568 kprintf("linprocfs_readlink: pid %d disappeared\n",
1569 pfs->pfs_pid);
1570 return (uiomove("unknown", sizeof("unknown") - 1,
1571 ap->a_uio));
1572 }
1573 if (procp == curproc) {
1574 return (uiomove("/dev/fd", sizeof("/dev/fd") - 1,
1575 ap->a_uio));
1576 } else {
1577 return (uiomove("unknown", sizeof("unknown") - 1,
1578 ap->a_uio));
1579 }
1580 /* notreached */
1581 break;
984263bc
MD
1582 default:
1583 return (EINVAL);
1584 }
1585}
1586
1587/*
1588 * convert decimal ascii to pid_t
1589 */
1590static pid_t
2da2a8af 1591atopid(const char *b, u_int len)
984263bc
MD
1592{
1593 pid_t p = 0;
1594
1595 while (len--) {
1596 char c = *b++;
1597 if (c < '0' || c > '9')
1598 return (NO_PID);
1599 p = 10 * p + (c - '0');
1600 if (p > PID_MAX)
1601 return (NO_PID);
1602 }
1603
1604 return (p);
1605}
1606