diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 2009-06-07 03:07:20 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 2009-06-07 03:07:20 +0000 |
commit | 35f0486b79701fae79f4e1da115887021cca6abf (patch) | |
tree | d5729efcae898a700d4d656f0ce61d5ab19a2798 | |
parent | af1d7788f946103f1f71ffcbc3b4887428e05017 (diff) |
Add KERN_FILE2 sysctl analogous to KERN_PROC2 but for file structures,
along with vnode type-specific info to make it more useful for fstat(1).
OK deraadt@
-rw-r--r-- | lib/libc/gen/sysctl.3 | 14 | ||||
-rw-r--r-- | sbin/sysctl/sysctl.c | 9 | ||||
-rw-r--r-- | sys/kern/kern_sysctl.c | 299 | ||||
-rw-r--r-- | sys/sys/sysctl.h | 99 |
4 files changed, 412 insertions, 9 deletions
diff --git a/lib/libc/gen/sysctl.3 b/lib/libc/gen/sysctl.3 index 51840ae3878..89f568b4d95 100644 --- a/lib/libc/gen/sysctl.3 +++ b/lib/libc/gen/sysctl.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sysctl.3,v 1.185 2008/12/23 09:57:55 jmc Exp $ +.\" $OpenBSD: sysctl.3,v 1.186 2009/06/07 03:07:19 millert Exp $ .\" .\" Copyright (c) 1993 .\" The Regents of the University of California. All rights reserved. @@ -27,7 +27,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd $Mdocdate: December 23 2008 $ +.Dd $Mdocdate: June 7 2009 $ .Dt SYSCTL 3 .Os .Sh NAME @@ -412,6 +412,7 @@ information. .It Dv KERN_DOMAINNAME No " string yes" .It Dv KERN_EMUL No " node not applicable" .It Dv KERN_FILE No " struct file no" +.It Dv KERN_FILE2 No " struct kinfo_file2 no" .It Dv KERN_FORKSTAT No " struct forkstat no" .It Dv KERN_FSCALE No " integer no" .It Dv KERN_FSYNC No " integer no" @@ -547,6 +548,15 @@ The returned data consists of a single followed by an array of .Li struct file , whose size depends on the current number of such objects in the system. +.It Dv KERN_FILE2 +Like +.Dv KERN_FILE +but an array of +.Li struct kinfo_file2 +structures is returned. +The fifth level name is the size of the +.Li struct kinfo_file2 +and the sixth level name is the number of structures to return. .It Dv KERN_FORKSTAT A .Li struct forkstat diff --git a/sbin/sysctl/sysctl.c b/sbin/sysctl/sysctl.c index e3315e0dcf1..efd52c4311e 100644 --- a/sbin/sysctl/sysctl.c +++ b/sbin/sysctl/sysctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sysctl.c,v 1.160 2008/08/04 04:26:42 miod Exp $ */ +/* $OpenBSD: sysctl.c,v 1.161 2009/06/07 03:07:19 millert Exp $ */ /* $NetBSD: sysctl.c,v 1.9 1995/09/30 07:12:50 thorpej Exp $ */ /* @@ -40,7 +40,7 @@ static const char copyright[] = #if 0 static const char sccsid[] = "@(#)sysctl.c 8.5 (Berkeley) 5/9/95"; #else -static const char rcsid[] = "$OpenBSD: sysctl.c,v 1.160 2008/08/04 04:26:42 miod Exp $"; +static const char rcsid[] = "$OpenBSD: sysctl.c,v 1.161 2009/06/07 03:07:19 millert Exp $"; #endif #endif /* not lint */ @@ -451,6 +451,11 @@ parse(char *string, int flags) case KERN_EMUL: sysctl_emul(string, newval, flags); return; + case KERN_FILE2: + if (flags == 0) + return; + warnx("use fstat to view %s information", string); + return; } break; diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c index c1a0d54f5a1..4496273eca2 100644 --- a/sys/kern/kern_sysctl.c +++ b/sys/kern/kern_sysctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_sysctl.c,v 1.171 2009/06/05 04:29:14 beck Exp $ */ +/* $OpenBSD: kern_sysctl.c,v 1.172 2009/06/07 03:07:19 millert Exp $ */ /* $NetBSD: kern_sysctl.c,v 1.17 1996/05/20 17:49:05 mrg Exp $ */ /*- @@ -46,6 +46,7 @@ #include <sys/proc.h> #include <sys/resourcevar.h> #include <sys/file.h> +#include <sys/filedesc.h> #include <sys/vnode.h> #include <sys/unistd.h> #include <sys/buf.h> @@ -62,14 +63,29 @@ #include <sys/exec.h> #include <sys/mbuf.h> #include <sys/sensors.h> +#include <sys/pipe.h> +#include <sys/eventvar.h> +#include <sys/socketvar.h> +#include <sys/domain.h> +#include <sys/protosw.h> #ifdef __HAVE_TIMECOUNTER #include <sys/timetc.h> #endif #include <sys/evcount.h> +#include <sys/unpcb.h> #include <sys/mount.h> #include <sys/syscallargs.h> #include <dev/rndvar.h> +#include <dev/systrace.h> + +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/in_pcb.h> +#include <netinet/ip6.h> +#include <netinet6/ip6_var.h> #ifdef DDB #include <ddb/db_var.h> @@ -271,6 +287,7 @@ kern_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, case KERN_TIMECOUNTER: #endif case KERN_CPTIME2: + case KERN_FILE2: break; default: return (ENOTDIR); /* overloaded */ @@ -345,6 +362,8 @@ kern_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, case KERN_PROC_ARGS: return (sysctl_proc_args(name + 1, namelen - 1, oldp, oldlenp, p)); + case KERN_FILE2: + return (sysctl_file2(name + 1, namelen - 1, oldp, oldlenp, p)); #endif case KERN_FILE: return (sysctl_file(oldp, oldlenp, p)); @@ -953,9 +972,10 @@ sysctl_file(char *where, size_t *sizep, struct proc *p) buflen = *sizep; if (where == NULL) { /* - * overestimate by 10 files + * overestimate by KERN_FILESLOP files */ - *sizep = sizeof(filehead) + (nfiles + 10) * sizeof(struct file); + *sizep = sizeof(filehead) + + (nfiles + KERN_FILESLOP) * sizeof(struct file); return (0); } @@ -1001,6 +1021,279 @@ sysctl_file(char *where, size_t *sizep, struct proc *p) } #ifndef SMALL_KERNEL +void +fill_file2(struct kinfo_file2 *kf, struct file *fp, struct filedesc *fdp, + int fd, struct vnode *vp, struct proc *pp, struct proc *p) +{ + struct vattr va; + + memset(kf, 0, sizeof(*kf)); + + kf->fd_fd = fd; /* might not really be an fd */ + + if (fp != NULL) { + kf->f_fileaddr = PTRTOINT64(fp); + kf->f_flag = fp->f_flag; + kf->f_iflags = fp->f_iflags; + kf->f_type = fp->f_type; + kf->f_count = fp->f_count; + kf->f_msgcount = fp->f_msgcount; + kf->f_ucred = PTRTOINT64(fp->f_cred); + kf->f_uid = fp->f_cred->cr_uid; + kf->f_gid = fp->f_cred->cr_gid; + kf->f_ops = PTRTOINT64(fp->f_ops); + kf->f_offset = fp->f_offset; + kf->f_data = PTRTOINT64(fp->f_data); + kf->f_usecount = fp->f_usecount; + + if (suser(p, 0) == 0 || p->p_ucred->cr_uid == fp->f_cred->cr_uid) { + kf->f_rxfer = fp->f_rxfer; + kf->f_rwfer = fp->f_wxfer; + kf->f_seek = fp->f_seek; + kf->f_rbytes = fp->f_rbytes; + kf->f_wbytes = fp->f_rbytes; + } + } else if (vp != NULL) { + /* fake it */ + kf->f_type = DTYPE_VNODE; + kf->f_flag = FREAD; + if (fd == KERN_FILE_TRACE) + kf->f_flag |= FWRITE; + } + + /* information about the object associated with this file */ + switch (kf->f_type) { + case DTYPE_VNODE: + if (fp != NULL) + vp = (struct vnode *)fp->f_data; + + kf->v_un = PTRTOINT64(vp->v_un.vu_socket); + kf->v_type = vp->v_type; + kf->v_tag = vp->v_tag; + kf->v_flag = vp->v_flag; + kf->v_data = PTRTOINT64(vp->v_data); + kf->v_mount = PTRTOINT64(vp->v_mount); + strlcpy(kf->f_mntonname, vp->v_mount->mnt_stat.f_mntonname, + sizeof(kf->f_mntonname)); + + if (VOP_GETATTR(vp, &va, p->p_ucred, p) == 0) { + kf->va_fileid = va.va_fileid; + kf->va_mode = MAKEIMODE(va.va_type, va.va_mode); + kf->va_size = va.va_size; + kf->va_rdev = va.va_rdev; + kf->va_fsid = va.va_fsid & 0xffffffff; + } + break; + + case DTYPE_SOCKET: { + struct socket *so = (struct socket *)fp->f_data; + + kf->so_type = so->so_type; + kf->so_state = so->so_state; + kf->so_pcb = PTRTOINT64(so->so_pcb); + kf->so_protocol = so->so_proto->pr_protocol; + kf->so_family = so->so_proto->pr_domain->dom_family; + switch (kf->so_family) { + case AF_INET: { + struct inpcb *inpcb = so->so_pcb; + + kf->inp_ppcb = PTRTOINT64(inpcb->inp_ppcb); + kf->inp_lport = inpcb->inp_lport; + kf->inp_laddru[0] = inpcb->inp_laddr.s_addr; + kf->inp_fport = inpcb->inp_fport; + kf->inp_faddru[0] = inpcb->inp_faddr.s_addr; + break; + } + case AF_INET6: { + struct inpcb *inpcb = so->so_pcb; + + kf->inp_ppcb = PTRTOINT64(inpcb->inp_ppcb); + kf->inp_lport = inpcb->inp_lport; + kf->inp_laddru[0] = inpcb->inp_laddr6.s6_addr32[0]; + kf->inp_laddru[1] = inpcb->inp_laddr6.s6_addr32[1]; + kf->inp_laddru[2] = inpcb->inp_laddr6.s6_addr32[2]; + kf->inp_laddru[3] = inpcb->inp_laddr6.s6_addr32[3]; + kf->inp_fport = inpcb->inp_fport; + kf->inp_faddru[0] = inpcb->inp_laddr6.s6_addr32[0]; + kf->inp_faddru[1] = inpcb->inp_faddr6.s6_addr32[1]; + kf->inp_faddru[2] = inpcb->inp_faddr6.s6_addr32[2]; + kf->inp_faddru[3] = inpcb->inp_faddr6.s6_addr32[3]; + break; + } + case AF_UNIX: { + struct unpcb *unpcb = so->so_pcb; + + kf->unp_conn = PTRTOINT64(unpcb->unp_conn); + break; + } + } + break; + } + + case DTYPE_PIPE: { + struct pipe *pipe = (struct pipe *)fp->f_data; + + kf->pipe_peer = PTRTOINT64(pipe->pipe_peer); + kf->pipe_state = pipe->pipe_state; + break; + } + + case DTYPE_KQUEUE: { + struct kqueue *kqi = (struct kqueue *)fp->f_data; + + kf->kq_count = kqi->kq_count; + kf->kq_state = kqi->kq_state; + break; + } + case DTYPE_SYSTRACE: { + struct fsystrace *f = (struct fsystrace *)fp->f_data; + + kf->str_npolicies = f->npolicies; + break; + } + } + + /* per-process information for KERN_FILE_BY[PU]ID */ + if (pp != NULL) { + kf->p_pid = pp->p_pid; + kf->p_uid = pp->p_ucred->cr_uid; + kf->p_gid = pp->p_ucred->cr_gid; + strlcpy(kf->p_comm, pp->p_comm, sizeof(kf->p_comm)); + } + if (fdp != NULL) + kf->fd_ofileflags = fdp->fd_ofileflags[fd]; +} + +/* + * Get file structures. + */ +int +sysctl_file2(int *name, u_int namelen, char *where, size_t *sizep, + struct proc *p) +{ + struct kinfo_file2 *kf; + struct filedesc *fdp; + struct file *fp; + struct proc *pp; + size_t buflen, elem_size, elem_count, outsize; + char *dp = where; + int arg, i, error = 0, needed = 0; + u_int op; + + if (namelen > 4) + return (ENOTDIR); + if (namelen < 4) + return (EINVAL); + + buflen = where != NULL ? *sizep : 0; + op = name[0]; + arg = name[1]; + elem_size = name[2]; + elem_count = name[3]; + outsize = MIN(sizeof(*kf), elem_size); + + if (elem_size < 1 || elem_count < 0) + return (EINVAL); + + kf = malloc(sizeof(*kf), M_TEMP, M_WAITOK); + +#define FILLIT(fp, fdp, i, vp, pp) do { \ + if (buflen >= elem_size && elem_count > 0) { \ + fill_file2(kf, fp, fdp, i, vp, pp, p); \ + error = copyout(kf, dp, outsize); \ + if (error) \ + break; \ + dp += elem_size; \ + buflen -= elem_size; \ + elem_count--; \ + } \ + needed += elem_size; \ +} while (0) + + switch (op) { + case KERN_FILE_BYFILE: + if (arg != 0) { + /* no arg in file mode */ + error = EINVAL; + break; + } + LIST_FOREACH(fp, &filehead, f_list) { + if (fp->f_count == 0) + continue; + FILLIT(fp, NULL, 0, NULL, NULL); + } + break; + case KERN_FILE_BYPID: + /* A arg of -1 indicates all processes */ + if (arg < -1) { + error = EINVAL; + break; + } + LIST_FOREACH(pp, &allproc, p_list) { + /* skip system, embryonic and undead processes */ + if ((pp->p_flag & P_SYSTEM) || + pp->p_stat == SIDL || pp->p_stat == SZOMB) + continue; + if (arg > 0 && pp->p_pid != (pid_t)arg) { + /* not the pid we are looking for */ + continue; + } + fdp = pp->p_fd; + if (fdp->fd_cdir) + FILLIT(NULL, NULL, KERN_FILE_CDIR, fdp->fd_cdir, pp); + if (fdp->fd_rdir) + FILLIT(NULL, NULL, KERN_FILE_RDIR, fdp->fd_rdir, pp); + if (pp->p_tracep) + FILLIT(NULL, NULL, KERN_FILE_TRACE, pp->p_tracep, pp); + for (i = 0; i < fdp->fd_nfiles; i++) { + if ((fp = fdp->fd_ofiles[i]) == NULL) + continue; + if (!FILE_IS_USABLE(fp)) + continue; + FILLIT(fp, fdp, i, NULL, pp); + } + } + break; + case KERN_FILE_BYUID: + LIST_FOREACH(pp, &allproc, p_list) { + /* skip system, embryonic and undead processes */ + if ((pp->p_flag & P_SYSTEM) || + pp->p_stat == SIDL || pp->p_stat == SZOMB) + continue; + if (arg > 0 && pp->p_ucred->cr_uid != (uid_t)arg) { + /* not the uid we are looking for */ + continue; + } + fdp = pp->p_fd; + if (fdp->fd_cdir) + FILLIT(NULL, NULL, KERN_FILE_CDIR, fdp->fd_cdir, pp); + if (fdp->fd_rdir) + FILLIT(NULL, NULL, KERN_FILE_RDIR, fdp->fd_rdir, pp); + if (pp->p_tracep) + FILLIT(NULL, NULL, KERN_FILE_TRACE, pp->p_tracep, pp); + for (i = 0; i < fdp->fd_nfiles; i++) { + if ((fp = fdp->fd_ofiles[i]) == NULL) + continue; + if (!FILE_IS_USABLE(fp)) + continue; + FILLIT(fp, fdp, i, NULL, pp); + } + } + break; + default: + error = EINVAL; + break; + } + free(kf, M_TEMP); + + if (!error) { + if (where == NULL) + needed += KERN_FILESLOP * elem_size; + *sizep = needed; + } + + return (error); +} /* * try over estimating by 5 procs diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h index 21dde2bbf58..93fc0798768 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sysctl.h,v 1.97 2009/06/03 21:30:20 beck Exp $ */ +/* $OpenBSD: sysctl.h,v 1.98 2009/06/07 03:07:19 millert Exp $ */ /* $NetBSD: sysctl.h,v 1.16 1996/04/09 20:55:36 cgd Exp $ */ /* @@ -185,7 +185,8 @@ struct ctlname { #define KERN_MAXLOCKSPERUID 70 /* int: locks per uid */ #define KERN_CPTIME2 71 /* array: cp_time2 */ #define KERN_CACHEPCT 72 /* buffer cache % of physmem */ -#define KERN_MAXID 73 /* number of valid kern ids */ +#define KERN_FILE2 73 /* struct: file entries */ +#define KERN_MAXID 74 /* number of valid kern ids */ #define CTL_KERN_NAMES { \ { 0, 0 }, \ @@ -261,6 +262,7 @@ struct ctlname { { "maxlocksperuid", CTLTYPE_INT }, \ { "cp_time2", CTLTYPE_STRUCT }, \ { "bufcachepercent", CTLTYPE_INT }, \ + { "file2", CTLTYPE_STRUCT }, \ } /* @@ -466,6 +468,94 @@ struct kinfo_proc2 { }; /* + * kern.file2 returns an array of these structures, which are designed + * both to be immune to be immune to 32/64 bit emulation issues and to + * provide backwards compatibility. The order differs slightly from + * that of the real struct file, and some fields are taken from other + * structures (struct vnode, struct proc) in order to make the file + * information more useful. + */ +#define KERN_FILE_BYFILE 1 +#define KERN_FILE_BYPID 2 +#define KERN_FILE_BYUID 3 +#define KERN_FILESLOP 10 + +#define KERN_FILE_TEXT -1 +#define KERN_FILE_CDIR -2 +#define KERN_FILE_RDIR -3 +#define KERN_FILE_TRACE -4 + +#define KI_MNAMELEN 96 /* rounded up from 90 */ + +struct kinfo_file2 { + uint64_t f_fileaddr; /* PTR: address of struct file */ + uint32_t f_flag; /* SHORT: flags (see fcntl.h) */ + uint32_t f_iflags; /* INT: internal flags */ + uint32_t f_type; /* INT: descriptor type */ + uint32_t f_count; /* UINT: reference count */ + uint32_t f_msgcount; /* UINT: references from msg queue */ + uint32_t f_usecount; /* INT: number active users */ + uint64_t f_ucred; /* PTR: creds for descriptor */ + uint32_t f_uid; /* UID_T: descriptor credentials */ + uint32_t f_gid; /* GID_T: descriptor credentials */ + uint64_t f_ops; /* PTR: address of fileops */ + uint64_t f_offset; /* OFF_T: offset */ + uint64_t f_data; /* PTR: descriptor data */ + uint64_t f_rxfer; /* UINT64: number of read xfers */ + uint64_t f_rwfer; /* UINT64: number of write xfers */ + uint64_t f_seek; /* UINT64: number of seek operations */ + uint64_t f_rbytes; /* UINT64: total bytes read */ + uint64_t f_wbytes; /* UINT64: total bytes written */ + + /* information about the vnode associated with this file */ + uint64_t v_un; /* PTR: socket, specinfo, etc */ + uint32_t v_type; /* ENUM: vnode type */ + uint32_t v_tag; /* ENUM: type of underlying data */ + uint32_t v_flag; /* UINT: vnode flags */ + uint32_t va_rdev; /* DEV_T: raw device */ + uint64_t v_data; /* PTR: private data for fs */ + uint64_t v_mount; /* PTR: mount info for fs */ + uint64_t va_fileid; /* LONG: file id */ + uint64_t va_size; /* UINT64_T: file size in bytes */ + uint32_t va_mode; /* MODE_T: file access mode and type */ + uint32_t va_fsid; /* DEV_T: filesystem device */ + char f_mntonname[KI_MNAMELEN]; + + /* socket information */ + uint32_t so_type; /* SHORT: socket type */ + uint32_t so_state; /* SHORT: socket state */ + uint64_t so_pcb; /* PTR: socket pcb */ + uint32_t so_protocol; /* SHORT: socket protocol type */ + uint32_t so_family; /* INT: socket domain family */ + uint64_t inp_ppcb; /* PTR: pointer to per-protocol pcb */ + uint32_t inp_lport; /* SHORT: local inet port */ + uint32_t inp_laddru[4]; /* STRUCT: local inet addr */ + uint32_t inp_fport; /* SHORT: foreign inet port */ + uint32_t inp_faddru[4]; /* STRUCT: foreign inet addr */ + uint64_t unp_conn; /* PTR: connected socket cntrl block */ + + /* pipe information */ + uint64_t pipe_peer; /* PTR: link with other direction */ + uint32_t pipe_state; /* UINT: pipe status info */ + + /* kqueue information */ + uint32_t kq_count; /* INT: number of pending events */ + uint32_t kq_state; /* INT: kqueue status information */ + + /* systrace information */ + uint32_t str_npolicies; /* INT: number systrace policies */ + + /* process information when retrieved via KERN_FILE_BY[PU]ID */ + uint32_t p_pid; /* PID_T: process id */ + int32_t fd_fd; /* INT: descriptor number */ + uint32_t fd_ofileflags; /* CHAR: open file flags */ + uint32_t p_uid; /* UID_T: process credentials */ + uint32_t p_gid; /* GID_T: process credentials */ + uint32_t __spare; /* padding */ + char p_comm[KI_MAXCOMLEN]; +}; + +/* * KERN_INTRCNT */ #define KERN_INTRCNT_NUM 1 /* int: # intrcnt */ @@ -691,6 +781,7 @@ int sysctl_rdstring(void *, size_t *, void *, const char *); int sysctl_rdstruct(void *, size_t *, void *, const void *, int); int sysctl_struct(void *, size_t *, void *, size_t, void *, int); int sysctl_file(char *, size_t *, struct proc *); +int sysctl_file2(int *, u_int, char *, size_t *, struct proc *); int sysctl_doproc(int *, u_int, char *, size_t *); struct radix_node; struct walkarg; @@ -705,6 +796,10 @@ int sysctl_doprof(int *, u_int, void *, size_t *, void *, size_t); int sysctl_dopool(int *, u_int, char *, size_t *); void fill_eproc(struct proc *, struct eproc *); + +void fill_file2(struct kinfo_file2 *, struct file *, struct filedesc *, + int, struct vnode *, struct proc *, struct proc *); + void fill_kproc2(struct proc *, struct kinfo_proc2 *); int kern_sysctl(int *, u_int, void *, size_t *, void *, size_t, |