diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile index 4860292c1196..8ef8018ef113 100644 --- a/lib/libutil/Makefile +++ b/lib/libutil/Makefile @@ -9,7 +9,8 @@ LIB= util SHLIB_MAJOR= 7 SRCS= _secure_path.c auth.c gr_util.c expand_number.c flopen.c fparseln.c \ - humanize_number.c kld.c login.c login_auth.c login_cap.c login_class.c \ + humanize_number.c kinfo_getfile.c kinfo_getvmmap.c kld.c \ + login.c login_auth.c login_cap.c login_class.c \ login_crypt.c login_ok.c login_times.c login_tty.c logout.c \ logwtmp.c pidfile.c property.c pty.c pw_util.c realhostname.c \ stub.c trimdomain.c uucplock.c diff --git a/lib/libutil/kinfo_getfile.c b/lib/libutil/kinfo_getfile.c new file mode 100644 index 000000000000..ca0f832f1829 --- /dev/null +++ b/lib/libutil/kinfo_getfile.c @@ -0,0 +1,72 @@ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include "libutil.h" + +struct kinfo_file * +kinfo_getfile(pid_t pid, int *cntp) +{ + int mib[4]; + int error; + int cnt; + size_t len; + char *buf, *bp, *eb; + struct kinfo_file *kif, *kp, *kf; + + len = 0; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_FILEDESC; + mib[3] = pid; + + error = sysctl(mib, 4, NULL, &len, NULL, 0); + if (error) + return (0); + len = len * 4 / 3; + buf = malloc(len); + if (buf == NULL) + return (0); + error = sysctl(mib, 4, buf, &len, NULL, 0); + if (error) { + free(buf); + return (0); + } + /* Pass 1: count items */ + cnt = 0; + bp = buf; + eb = buf + len; + while (bp < eb) { + kf = (struct kinfo_file *)(uintptr_t)bp; + bp += kf->kf_structsize; + cnt++; + } + + kif = calloc(cnt, sizeof(*kif)); + if (kif == NULL) { + free(buf); + return (0); + } + bp = buf; + eb = buf + len; + kp = kif; + /* Pass 2: unpack */ + while (bp < eb) { + kf = (struct kinfo_file *)(uintptr_t)bp; + /* Copy/expand into pre-zeroed buffer */ + memcpy(kp, kf, kf->kf_structsize); + /* Advance to next packed record */ + bp += kf->kf_structsize; + /* Set field size to fixed length, advance */ + kp->kf_structsize = sizeof(*kp); + kp++; + } + free(buf); + *cntp = cnt; + return (kif); /* Caller must free() return value */ +} diff --git a/lib/libutil/kinfo_getvmmap.c b/lib/libutil/kinfo_getvmmap.c new file mode 100644 index 000000000000..54361089a599 --- /dev/null +++ b/lib/libutil/kinfo_getvmmap.c @@ -0,0 +1,72 @@ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include "libutil.h" + +struct kinfo_vmentry * +kinfo_getvmmap(pid_t pid, int *cntp) +{ + int mib[4]; + int error; + int cnt; + size_t len; + char *buf, *bp, *eb; + struct kinfo_vmentry *kiv, *kp, *kv; + + len = 0; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_VMMAP; + mib[3] = pid; + + error = sysctl(mib, 4, NULL, &len, NULL, 0); + if (error) + return (0); + len = len * 4 / 3; + buf = malloc(len); + if (buf == NULL) + return (0); + error = sysctl(mib, 4, buf, &len, NULL, 0); + if (error) { + free(buf); + return (0); + } + /* Pass 1: count items */ + cnt = 0; + bp = buf; + eb = buf + len; + while (bp < eb) { + kv = (struct kinfo_vmentry *)(uintptr_t)bp; + bp += kv->kve_structsize; + cnt++; + } + + kiv = calloc(cnt, sizeof(*kiv)); + if (kiv == NULL) { + free(buf); + return (0); + } + bp = buf; + eb = buf + len; + kp = kiv; + /* Pass 2: unpack */ + while (bp < eb) { + kv = (struct kinfo_vmentry *)(uintptr_t)bp; + /* Copy/expand into pre-zeroed buffer */ + memcpy(kp, kv, kv->kve_structsize); + /* Advance to next packed record */ + bp += kv->kve_structsize; + /* Set field size to fixed length, advance */ + kp->kve_structsize = sizeof(*kp); + kp++; + } + free(buf); + *cntp = cnt; + return (kiv); /* Caller must free() return value */ +} diff --git a/lib/libutil/libutil.h b/lib/libutil/libutil.h index 5fcbdb39580f..1061fad7b2c0 100644 --- a/lib/libutil/libutil.h +++ b/lib/libutil/libutil.h @@ -64,6 +64,8 @@ struct termios; struct winsize; struct utmp; struct in_addr; +struct kinfo_file; +struct kinfo_vmentry; __BEGIN_DECLS void clean_environment(const char * const *_white, @@ -99,6 +101,10 @@ int realhostname_sa(char *host, size_t hsize, struct sockaddr *addr, int kld_isloaded(const char *name); int kld_load(const char *name); +struct kinfo_file * + kinfo_getfile(pid_t _pid, int *_cntp); +struct kinfo_vmentry * + kinfo_getvmmap(pid_t _pid, int *_cntp); #ifdef _STDIO_H_ /* avoid adding new includes */ char *fparseln(FILE *, size_t *, size_t *, const char[3], int); diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index 35345865dc56..29af809ce220 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -2570,9 +2570,14 @@ sysctl_kern_file(SYSCTL_HANDLER_ARGS) SYSCTL_PROC(_kern, KERN_FILE, file, CTLTYPE_OPAQUE|CTLFLAG_RD, 0, 0, sysctl_kern_file, "S,xfile", "Entire file table"); +#ifdef KINFO_OFILE_SIZE +CTASSERT(sizeof(struct kinfo_ofile) == KINFO_OFILE_SIZE); +#endif + +/* Compatability with early 7-stable */ static int -export_vnode_for_sysctl(struct vnode *vp, int type, - struct kinfo_file *kif, struct filedesc *fdp, struct sysctl_req *req) +export_vnode_for_osysctl(struct vnode *vp, int type, + struct kinfo_ofile *kif, struct filedesc *fdp, struct sysctl_req *req) { int error; char *fullpath, *freepath; @@ -2585,7 +2590,7 @@ export_vnode_for_sysctl(struct vnode *vp, int type, kif->kf_fd = type; kif->kf_type = KF_TYPE_VNODE; /* This function only handles directories. */ - KASSERT(vp->v_type == VDIR, ("export_vnode_for_sysctl: vnode not directory")); + KASSERT(vp->v_type == VDIR, ("export_vnode_for_osysctl: vnode not directory")); kif->kf_vnode_type = KF_VTYPE_VDIR; /* @@ -2615,10 +2620,10 @@ export_vnode_for_sysctl(struct vnode *vp, int type, * Get per-process file descriptors for use by procstat(1), et al. */ static int -sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) +sysctl_kern_proc_ofiledesc(SYSCTL_HANDLER_ARGS) { char *fullpath, *freepath; - struct kinfo_file *kif; + struct kinfo_ofile *kif; struct filedesc *fdp; int error, i, *name; struct socket *so; @@ -2641,13 +2646,13 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) kif = malloc(sizeof(*kif), M_TEMP, M_WAITOK); FILEDESC_SLOCK(fdp); if (fdp->fd_cdir != NULL) - export_vnode_for_sysctl(fdp->fd_cdir, KF_FD_TYPE_CWD, kif, + export_vnode_for_osysctl(fdp->fd_cdir, KF_FD_TYPE_CWD, kif, fdp, req); if (fdp->fd_rdir != NULL) - export_vnode_for_sysctl(fdp->fd_rdir, KF_FD_TYPE_ROOT, kif, + export_vnode_for_osysctl(fdp->fd_rdir, KF_FD_TYPE_ROOT, kif, fdp, req); if (fdp->fd_jdir != NULL) - export_vnode_for_sysctl(fdp->fd_jdir, KF_FD_TYPE_JAIL, kif, + export_vnode_for_osysctl(fdp->fd_jdir, KF_FD_TYPE_JAIL, kif, fdp, req); for (i = 0; i < fdp->fd_nfiles; i++) { if ((fp = fdp->fd_ofiles[i]) == NULL) @@ -2795,6 +2800,245 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) return (0); } +static SYSCTL_NODE(_kern_proc, KERN_PROC_OFILEDESC, ofiledesc, CTLFLAG_RD, + sysctl_kern_proc_ofiledesc, "Process ofiledesc entries"); + +#ifdef KINFO_FILE_SIZE +CTASSERT(sizeof(struct kinfo_file) == KINFO_FILE_SIZE); +#endif + +static int +export_vnode_for_sysctl(struct vnode *vp, int type, + struct kinfo_file *kif, struct filedesc *fdp, struct sysctl_req *req) +{ + int error; + char *fullpath, *freepath; + int vfslocked; + + bzero(kif, sizeof(*kif)); + + vref(vp); + kif->kf_fd = type; + kif->kf_type = KF_TYPE_VNODE; + /* This function only handles directories. */ + KASSERT(vp->v_type == VDIR, ("export_vnode_for_sysctl: vnode not directory")); + kif->kf_vnode_type = KF_VTYPE_VDIR; + + /* + * This is not a true file descriptor, so we set a bogus refcount + * and offset to indicate these fields should be ignored. + */ + kif->kf_ref_count = -1; + kif->kf_offset = -1; + + freepath = NULL; + fullpath = "-"; + FILEDESC_SUNLOCK(fdp); + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, curthread); + vn_fullpath(curthread, vp, &fullpath, &freepath); + vput(vp); + VFS_UNLOCK_GIANT(vfslocked); + strlcpy(kif->kf_path, fullpath, sizeof(kif->kf_path)); + if (freepath != NULL) + free(freepath, M_TEMP); + /* Pack record size down */ + kif->kf_structsize = offsetof(struct kinfo_file, kf_path) + + strlen(kif->kf_path) + 1; + kif->kf_structsize = roundup(kif->kf_structsize, sizeof(uint64_t)); + error = SYSCTL_OUT(req, kif, kif->kf_structsize); + FILEDESC_SLOCK(fdp); + return (error); +} + +/* + * Get per-process file descriptors for use by procstat(1), et al. + */ +static int +sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) +{ + char *fullpath, *freepath; + struct kinfo_file *kif; + struct filedesc *fdp; + int error, i, *name; + struct socket *so; + struct vnode *vp; + struct file *fp; + struct proc *p; + int vfslocked; + + name = (int *)arg1; + if ((p = pfind((pid_t)name[0])) == NULL) + return (ESRCH); + if ((error = p_candebug(curthread, p))) { + PROC_UNLOCK(p); + return (error); + } + fdp = fdhold(p); + PROC_UNLOCK(p); + if (fdp == NULL) + return (ENOENT); + kif = malloc(sizeof(*kif), M_TEMP, M_WAITOK); + FILEDESC_SLOCK(fdp); + if (fdp->fd_cdir != NULL) + export_vnode_for_sysctl(fdp->fd_cdir, KF_FD_TYPE_CWD, kif, + fdp, req); + if (fdp->fd_rdir != NULL) + export_vnode_for_sysctl(fdp->fd_rdir, KF_FD_TYPE_ROOT, kif, + fdp, req); + if (fdp->fd_jdir != NULL) + export_vnode_for_sysctl(fdp->fd_jdir, KF_FD_TYPE_JAIL, kif, + fdp, req); + for (i = 0; i < fdp->fd_nfiles; i++) { + if ((fp = fdp->fd_ofiles[i]) == NULL) + continue; + bzero(kif, sizeof(*kif)); + FILE_LOCK(fp); + vp = NULL; + so = NULL; + kif->kf_fd = i; + switch (fp->f_type) { + case DTYPE_VNODE: + kif->kf_type = KF_TYPE_VNODE; + vp = fp->f_vnode; + vref(vp); + break; + + case DTYPE_SOCKET: + kif->kf_type = KF_TYPE_SOCKET; + so = fp->f_data; + break; + + case DTYPE_PIPE: + kif->kf_type = KF_TYPE_PIPE; + break; + + case DTYPE_FIFO: + kif->kf_type = KF_TYPE_FIFO; + vp = fp->f_vnode; + vref(vp); + break; + + case DTYPE_KQUEUE: + kif->kf_type = KF_TYPE_KQUEUE; + break; + + case DTYPE_CRYPTO: + kif->kf_type = KF_TYPE_CRYPTO; + break; + + case DTYPE_MQUEUE: + kif->kf_type = KF_TYPE_MQUEUE; + break; + + default: + kif->kf_type = KF_TYPE_UNKNOWN; + break; + } + kif->kf_ref_count = fp->f_count; + if (fp->f_flag & FREAD) + kif->kf_flags |= KF_FLAG_READ; + if (fp->f_flag & FWRITE) + kif->kf_flags |= KF_FLAG_WRITE; + if (fp->f_flag & FAPPEND) + kif->kf_flags |= KF_FLAG_APPEND; + if (fp->f_flag & FASYNC) + kif->kf_flags |= KF_FLAG_ASYNC; + if (fp->f_flag & FFSYNC) + kif->kf_flags |= KF_FLAG_FSYNC; + if (fp->f_flag & FNONBLOCK) + kif->kf_flags |= KF_FLAG_NONBLOCK; + if (fp->f_flag & O_DIRECT) + kif->kf_flags |= KF_FLAG_DIRECT; + if (fp->f_flag & FHASLOCK) + kif->kf_flags |= KF_FLAG_HASLOCK; + kif->kf_offset = fp->f_offset; + FILE_UNLOCK(fp); + if (vp != NULL) { + switch (vp->v_type) { + case VNON: + kif->kf_vnode_type = KF_VTYPE_VNON; + break; + case VREG: + kif->kf_vnode_type = KF_VTYPE_VREG; + break; + case VDIR: + kif->kf_vnode_type = KF_VTYPE_VDIR; + break; + case VBLK: + kif->kf_vnode_type = KF_VTYPE_VBLK; + break; + case VCHR: + kif->kf_vnode_type = KF_VTYPE_VCHR; + break; + case VLNK: + kif->kf_vnode_type = KF_VTYPE_VLNK; + break; + case VSOCK: + kif->kf_vnode_type = KF_VTYPE_VSOCK; + break; + case VFIFO: + kif->kf_vnode_type = KF_VTYPE_VFIFO; + break; + case VBAD: + kif->kf_vnode_type = KF_VTYPE_VBAD; + break; + default: + kif->kf_vnode_type = KF_VTYPE_UNKNOWN; + break; + } + /* + * It is OK to drop the filedesc lock here as we will + * re-validate and re-evaluate its properties when + * the loop continues. + */ + freepath = NULL; + fullpath = "-"; + FILEDESC_SUNLOCK(fdp); + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, curthread); + vn_fullpath(curthread, vp, &fullpath, &freepath); + vput(vp); + VFS_UNLOCK_GIANT(vfslocked); + strlcpy(kif->kf_path, fullpath, + sizeof(kif->kf_path)); + if (freepath != NULL) + free(freepath, M_TEMP); + FILEDESC_SLOCK(fdp); + } + if (so != NULL) { + struct sockaddr *sa; + + if (so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa) + == 0 && sa->sa_len <= sizeof(kif->kf_sa_local)) { + bcopy(sa, &kif->kf_sa_local, sa->sa_len); + free(sa, M_SONAME); + } + if (so->so_proto->pr_usrreqs->pru_peeraddr(so, &sa) + == 00 && sa->sa_len <= sizeof(kif->kf_sa_peer)) { + bcopy(sa, &kif->kf_sa_peer, sa->sa_len); + free(sa, M_SONAME); + } + kif->kf_sock_domain = + so->so_proto->pr_domain->dom_family; + kif->kf_sock_type = so->so_type; + kif->kf_sock_protocol = so->so_proto->pr_protocol; + } + /* Pack record size down */ + kif->kf_structsize = offsetof(struct kinfo_file, kf_path) + + strlen(kif->kf_path) + 1; + kif->kf_structsize = roundup(kif->kf_structsize, + sizeof(uint64_t)); + error = SYSCTL_OUT(req, kif, kif->kf_structsize); + if (error) + break; + } + FILEDESC_SUNLOCK(fdp); + fddrop(fdp); + free(kif, M_TEMP); + return (0); +} + static SYSCTL_NODE(_kern_proc, KERN_PROC_FILEDESC, filedesc, CTLFLAG_RD, sysctl_kern_proc_filedesc, "Process filedesc entries"); diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index f9419604b49b..0997cb269561 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -32,6 +32,7 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_compat.h" #include "opt_ddb.h" #include "opt_kdtrace.h" #include "opt_ktrace.h" @@ -1339,13 +1340,18 @@ sysctl_kern_proc_sv_name(SYSCTL_HANDLER_ARGS) return (sysctl_handle_string(oidp, sv_name, 0, req)); } +#ifdef KINFO_OVMENTRY_SIZE +CTASSERT(sizeof(struct kinfo_ovmentry) == KINFO_OVMENTRY_SIZE); +#endif + +/* Compatability with early 7-stable */ static int -sysctl_kern_proc_vmmap(SYSCTL_HANDLER_ARGS) +sysctl_kern_proc_ovmmap(SYSCTL_HANDLER_ARGS) { vm_map_entry_t entry, tmp_entry; unsigned int last_timestamp; char *fullpath, *freepath; - struct kinfo_vmentry *kve; + struct kinfo_ovmentry *kve; struct vattr va; struct ucred *cred; int error, *name; @@ -1501,6 +1507,176 @@ sysctl_kern_proc_vmmap(SYSCTL_HANDLER_ARGS) return (error); } +#ifdef KINFO_VMENTRY_SIZE +CTASSERT(sizeof(struct kinfo_vmentry) == KINFO_VMENTRY_SIZE); +#endif + +static int +sysctl_kern_proc_vmmap(SYSCTL_HANDLER_ARGS) +{ + vm_map_entry_t entry, tmp_entry; + unsigned int last_timestamp; + char *fullpath, *freepath; + struct kinfo_vmentry *kve; + struct vattr va; + struct ucred *cred; + int error, *name; + struct vnode *vp; + struct proc *p; + vm_map_t map; + + name = (int *)arg1; + if ((p = pfind((pid_t)name[0])) == NULL) + return (ESRCH); + if (p->p_flag & P_WEXIT) { + PROC_UNLOCK(p); + return (ESRCH); + } + if ((error = p_candebug(curthread, p))) { + PROC_UNLOCK(p); + return (error); + } + _PHOLD(p); + PROC_UNLOCK(p); + + kve = malloc(sizeof(*kve), M_TEMP, M_WAITOK); + + map = &p->p_vmspace->vm_map; /* XXXRW: More locking required? */ + vm_map_lock_read(map); + for (entry = map->header.next; entry != &map->header; + entry = entry->next) { + vm_object_t obj, tobj, lobj; + vm_offset_t addr; + int vfslocked; + + if (entry->eflags & MAP_ENTRY_IS_SUB_MAP) + continue; + + bzero(kve, sizeof(*kve)); + + kve->kve_private_resident = 0; + obj = entry->object.vm_object; + if (obj != NULL) { + VM_OBJECT_LOCK(obj); + if (obj->shadow_count == 1) + kve->kve_private_resident = + obj->resident_page_count; + } + kve->kve_resident = 0; + addr = entry->start; + while (addr < entry->end) { + if (pmap_extract(map->pmap, addr)) + kve->kve_resident++; + addr += PAGE_SIZE; + } + + for (lobj = tobj = obj; tobj; tobj = tobj->backing_object) { + if (tobj != obj) + VM_OBJECT_LOCK(tobj); + if (lobj != obj) + VM_OBJECT_UNLOCK(lobj); + lobj = tobj; + } + + kve->kve_fileid = 0; + kve->kve_fsid = 0; + freepath = NULL; + fullpath = ""; + if (lobj) { + vp = NULL; + switch(lobj->type) { + case OBJT_DEFAULT: + kve->kve_type = KVME_TYPE_DEFAULT; + break; + case OBJT_VNODE: + kve->kve_type = KVME_TYPE_VNODE; + vp = lobj->handle; + vref(vp); + break; + case OBJT_SWAP: + kve->kve_type = KVME_TYPE_SWAP; + break; + case OBJT_DEVICE: + kve->kve_type = KVME_TYPE_DEVICE; + break; + case OBJT_PHYS: + kve->kve_type = KVME_TYPE_PHYS; + break; + case OBJT_DEAD: + kve->kve_type = KVME_TYPE_DEAD; + break; + default: + kve->kve_type = KVME_TYPE_UNKNOWN; + break; + } + if (lobj != obj) + VM_OBJECT_UNLOCK(lobj); + + kve->kve_ref_count = obj->ref_count; + kve->kve_shadow_count = obj->shadow_count; + VM_OBJECT_UNLOCK(obj); + if (vp != NULL) { + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, + curthread); + vn_fullpath(curthread, vp, &fullpath, + &freepath); + cred = curthread->td_ucred; + if (VOP_GETATTR(vp, &va, cred, curthread) == 0) { + kve->kve_fileid = va.va_fileid; + kve->kve_fsid = va.va_fsid; + } + vput(vp); + VFS_UNLOCK_GIANT(vfslocked); + } + } else { + kve->kve_type = KVME_TYPE_NONE; + kve->kve_ref_count = 0; + kve->kve_shadow_count = 0; + } + + kve->kve_start = entry->start; + kve->kve_end = entry->end; + kve->kve_offset = entry->offset; + + if (entry->protection & VM_PROT_READ) + kve->kve_protection |= KVME_PROT_READ; + if (entry->protection & VM_PROT_WRITE) + kve->kve_protection |= KVME_PROT_WRITE; + if (entry->protection & VM_PROT_EXECUTE) + kve->kve_protection |= KVME_PROT_EXEC; + + if (entry->eflags & MAP_ENTRY_COW) + kve->kve_flags |= KVME_FLAG_COW; + if (entry->eflags & MAP_ENTRY_NEEDS_COPY) + kve->kve_flags |= KVME_FLAG_NEEDS_COPY; + + strlcpy(kve->kve_path, fullpath, sizeof(kve->kve_path)); + if (freepath != NULL) + free(freepath, M_TEMP); + + last_timestamp = map->timestamp; + vm_map_unlock_read(map); + /* Pack record size down */ + kve->kve_structsize = offsetof(struct kinfo_vmentry, kve_path) + + strlen(kve->kve_path) + 1; + kve->kve_structsize = roundup(kve->kve_structsize, + sizeof(uint64_t)); + error = SYSCTL_OUT(req, kve, kve->kve_structsize); + vm_map_lock_read(map); + if (error) + break; + if (last_timestamp + 1 != map->timestamp) { + vm_map_lookup_entry(map, addr - 1, &tmp_entry); + entry = tmp_entry; + } + } + vm_map_unlock_read(map); + PRELE(p); + free(kve, M_TEMP); + return (error); +} + #if defined(STACK) || defined(DDB) static int sysctl_kern_proc_kstack(SYSCTL_HANDLER_ARGS) @@ -1674,6 +1850,9 @@ static SYSCTL_NODE(_kern_proc, (KERN_PROC_PID | KERN_PROC_INC_THREAD), pid_td, static SYSCTL_NODE(_kern_proc, (KERN_PROC_PROC | KERN_PROC_INC_THREAD), proc_td, CTLFLAG_RD, sysctl_kern_proc, "Return process table, no threads"); +static SYSCTL_NODE(_kern_proc, KERN_PROC_OVMMAP, ovmmap, CTLFLAG_RD, + sysctl_kern_proc_ovmmap, "Old Process vm map entries"); + static SYSCTL_NODE(_kern_proc, KERN_PROC_VMMAP, vmmap, CTLFLAG_RD, sysctl_kern_proc_vmmap, "Process vm map entries"); diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h index e34602f81c32..2542703f6e2c 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -463,13 +463,16 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry); #define KERN_PROC_RGID 10 /* by real group id */ #define KERN_PROC_GID 11 /* by effective group id */ #define KERN_PROC_PATHNAME 12 /* path to executable */ -#define KERN_PROC_VMMAP 13 /* VM map entries for process */ -#define KERN_PROC_FILEDESC 14 /* File descriptors for process */ +#define KERN_PROC_OVMMAP 13 /* Old VM map entries for process */ +#define KERN_PROC_OFILEDESC 14 /* Old file descriptors for process */ #define KERN_PROC_KSTACK 15 /* Kernel stacks for process */ #define KERN_PROC_INC_THREAD 0x10 /* * modifier for pid, pgrp, tty, * uid, ruid, gid, rgid and proc + * This effectively uses 16-31 */ +#define KERN_PROC_VMMAP 32 /* VM map entries for process */ +#define KERN_PROC_FILEDESC 33 /* File descriptors for process */ /* * KERN_IPC identifiers diff --git a/sys/sys/user.h b/sys/sys/user.h index bce1cf54e0b7..57256e093ee8 100644 --- a/sys/sys/user.h +++ b/sys/sys/user.h @@ -271,22 +271,57 @@ struct user { #define KF_FLAG_DIRECT 0x00000040 #define KF_FLAG_HASLOCK 0x00000080 -struct kinfo_file { +/* + * Old format. Has variable hidden padding due to alignment. + * This is a compatability hack for pre-build 7.1 packages. + */ +#if defined(__amd64__) +#define KINFO_OFILE_SIZE 1328 +#endif +#if defined(__i386__) +#define KINFO_OFILE_SIZE 1324 +#endif + +struct kinfo_ofile { int kf_structsize; /* Size of kinfo_file. */ int kf_type; /* Descriptor type. */ int kf_fd; /* Array index. */ int kf_ref_count; /* Reference count. */ int kf_flags; /* Flags. */ + /* XXX Hidden alignment padding here on amd64 */ off_t kf_offset; /* Seek location. */ int kf_vnode_type; /* Vnode type. */ int kf_sock_domain; /* Socket domain. */ int kf_sock_type; /* Socket type. */ int kf_sock_protocol; /* Socket protocol. */ - char kf_path[PATH_MAX]; /* Path to file, if any. */ + char kf_path[PATH_MAX]; /* Path to file, if any. */ struct sockaddr_storage kf_sa_local; /* Socket address. */ struct sockaddr_storage kf_sa_peer; /* Peer address. */ }; +#if defined(__amd64__) || defined(__i386__) +#define KINFO_FILE_SIZE 1392 +#endif + +struct kinfo_file { + int kf_structsize; /* Variable size of record. */ + int kf_type; /* Descriptor type. */ + int kf_fd; /* Array index. */ + int kf_ref_count; /* Reference count. */ + int kf_flags; /* Flags. */ + int _kf_pad0; /* Round to 64 bit alignment */ + int64_t kf_offset; /* Seek location. */ + int kf_vnode_type; /* Vnode type. */ + int kf_sock_domain; /* Socket domain. */ + int kf_sock_type; /* Socket type. */ + int kf_sock_protocol; /* Socket protocol. */ + struct sockaddr_storage kf_sa_local; /* Socket address. */ + struct sockaddr_storage kf_sa_peer; /* Peer address. */ + int _kf_ispare[16]; /* Space for more stuff. */ + /* Truncated before copyout in sysctl */ + char kf_path[PATH_MAX]; /* Path to file, if any. */ +}; + /* * The KERN_PROC_VMMAP sysctl allows a process to dump the VM layout of * another process as a series of entries. @@ -307,11 +342,18 @@ struct kinfo_file { #define KVME_FLAG_COW 0x00000001 #define KVME_FLAG_NEEDS_COPY 0x00000002 -struct kinfo_vmentry { +#if defined(__amd64__) +#define KINFO_OVMENTRY_SIZE 1168 +#endif +#if defined(__i386__) +#define KINFO_OVMENTRY_SIZE 1128 +#endif + +struct kinfo_ovmentry { int kve_structsize; /* Size of kinfo_vmmapentry. */ int kve_type; /* Type of map entry. */ - void *kve_start; /* Starting pointer. */ - void *kve_end; /* Finishing pointer. */ + void *kve_start; /* Starting address. */ + void *kve_end; /* Finishing address. */ int kve_flags; /* Flags on map entry. */ int kve_resident; /* Number of resident pages. */ int kve_private_resident; /* Number of private pages. */ @@ -321,11 +363,35 @@ struct kinfo_vmentry { char kve_path[PATH_MAX]; /* Path to VM obj, if any. */ void *_kve_pspare[8]; /* Space for more stuff. */ off_t kve_offset; /* Mapping offset in object */ - uint64_t kve_fileid; /* inode number of vnode */ + uint64_t kve_fileid; /* inode number if vnode */ dev_t kve_fsid; /* dev_t of vnode location */ int _kve_ispare[3]; /* Space for more stuff. */ }; +#if defined(__amd64__) || defined(__i386__) +#define KINFO_VMENTRY_SIZE 1160 +#endif + +struct kinfo_vmentry { + int kve_structsize; /* Variable size of record. */ + int kve_type; /* Type of map entry. */ + uint64_t kve_start; /* Starting address. */ + uint64_t kve_end; /* Finishing address. */ + uint64_t kve_offset; /* Mapping offset in object */ + uint64_t kve_fileid; /* inode number if vnode */ + uint32_t kve_fsid; /* dev_t of vnode location */ + int kve_flags; /* Flags on map entry. */ + int kve_resident; /* Number of resident pages. */ + int kve_private_resident; /* Number of private pages. */ + int kve_protection; /* Protection bitmask. */ + int kve_ref_count; /* VM obj ref count. */ + int kve_shadow_count; /* VM obj shadow count. */ + int _kve_pad0; /* 64bit align next field */ + int _kve_ispare[16]; /* Space for more stuff. */ + /* Truncated before copyout in sysctl */ + char kve_path[PATH_MAX]; /* Path to VM obj, if any. */ +}; + /* * The KERN_PROC_KSTACK sysctl allows a process to dump the kernel stacks of * another process as a series of entries. Each stack is represented by a @@ -337,12 +403,15 @@ struct kinfo_vmentry { #define KKST_STATE_SWAPPED 1 /* Stack swapped out. */ #define KKST_STATE_RUNNING 2 /* Stack ephemeral. */ +#if defined(__amd64__) || defined(__i386__) +#define KINFO_KSTACK_SIZE 1096 +#endif + struct kinfo_kstack { lwpid_t kkst_tid; /* ID of thread. */ int kkst_state; /* Validity of stack. */ char kkst_trace[KKST_MAXLEN]; /* String representing stack. */ - void *_kkst_pspare[8]; /* Space for more stuff. */ - int _kkst_ispare[8]; /* Space for more stuff. */ + int _kkst_ispare[16]; /* Space for more stuff. */ }; #endif diff --git a/usr.bin/procstat/Makefile b/usr.bin/procstat/Makefile index e3c77e432028..1c187b04bd24 100644 --- a/usr.bin/procstat/Makefile +++ b/usr.bin/procstat/Makefile @@ -12,4 +12,7 @@ SRCS= procstat.c \ procstat_threads.c \ procstat_vm.c +LDADD+= -lutil +DPADD+= ${LIBUTIL} + .include diff --git a/usr.bin/procstat/procstat_files.c b/usr.bin/procstat/procstat_files.c index bb1152654bb1..fbe5a57b7090 100644 --- a/usr.bin/procstat/procstat_files.c +++ b/usr.bin/procstat/procstat_files.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "procstat.h" @@ -134,42 +135,18 @@ void procstat_files(pid_t pid, struct kinfo_proc *kipp) { struct kinfo_file *freep, *kif; - int error, name[4]; - unsigned int i; + int i, cnt; const char *str; - size_t len; if (!hflag) printf("%5s %-16s %4s %1s %1s %-8s %3s %7s %-3s %-12s\n", "PID", "COMM", "FD", "T", "V", "FLAGS", "REF", "OFFSET", "PRO", "NAME"); - name[0] = CTL_KERN; - name[1] = KERN_PROC; - name[2] = KERN_PROC_FILEDESC; - name[3] = pid; - - error = sysctl(name, 4, NULL, &len, NULL, 0); - if (error < 0 && errno != ESRCH && errno != EPERM) { - warn("sysctl: kern.proc.filedesc: %d", pid); - return; - } - if (error < 0) - return; - - freep = kif = malloc(len); - if (kif == NULL) - err(-1, "malloc"); - - if (sysctl(name, 4, kif, &len, NULL, 0) < 0) { - warn("sysctl: kern.proc.filedesc %d", pid); - free(freep); - return; - } - - for (i = 0; i < len / sizeof(*kif); i++, kif++) { - if (kif->kf_structsize != sizeof(*kif)) - errx(-1, "kinfo_file mismatch"); + freep = kinfo_getfile(pid, &cnt); + for (i = 0; i < cnt; i++) { + kif = &freep[i]; + printf("%5d ", pid); printf("%-16s ", kipp->ki_comm); switch (kif->kf_fd) { diff --git a/usr.bin/procstat/procstat_vm.c b/usr.bin/procstat/procstat_vm.c index a658382c3155..79c0e57aa5e7 100644 --- a/usr.bin/procstat/procstat_vm.c +++ b/usr.bin/procstat/procstat_vm.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include "procstat.h" @@ -41,10 +43,9 @@ void procstat_vm(pid_t pid, struct kinfo_proc *kipp __unused) { struct kinfo_vmentry *freep, *kve; - int error, name[4], ptrwidth; - unsigned int i; + int ptrwidth; + int i, cnt; const char *str; - size_t len; ptrwidth = 2*sizeof(void *) + 2; if (!hflag) @@ -52,41 +53,12 @@ procstat_vm(pid_t pid, struct kinfo_proc *kipp __unused) "PID", ptrwidth, "START", ptrwidth, "END", "PRT", "RES", "PRES", "REF", "SHD", "FL", "TP", "PATH"); - name[0] = CTL_KERN; - name[1] = KERN_PROC; - name[2] = KERN_PROC_VMMAP; - name[3] = pid; - - len = 0; - error = sysctl(name, 4, NULL, &len, NULL, 0); - if (error < 0 && errno != ESRCH && errno != EPERM) { - warn("sysctl: kern.proc.vmmap: %d", pid); - return; - } - if (error < 0) - return; - - /* - * Especially if running procstat -sv, we may need room for more - * mappings when printing than were present when we queried, so pad - * out the allocation a bit. - */ - len += sizeof(*kve) * 3; - freep = kve = malloc(len); - if (kve == NULL) - err(-1, "malloc"); - if (sysctl(name, 4, kve, &len, NULL, 0) < 0) { - warn("sysctl: kern.proc.vmmap: %d", pid); - free(freep); - return; - } - - for (i = 0; i < (len / sizeof(*kve)); i++, kve++) { - if (kve->kve_structsize != sizeof(*kve)) - errx(-1, "kinfo_vmentry structure mismatch"); + freep = kinfo_getvmmap(pid, &cnt); + for (i = 0; i < cnt; i++) { + kve = &freep[i]; printf("%5d ", pid); - printf("%*p ", ptrwidth, kve->kve_start); - printf("%*p ", ptrwidth, kve->kve_end); + printf("%#*jx ", ptrwidth, (uintmax_t)kve->kve_start); + printf("%#*jx ", ptrwidth, (uintmax_t)kve->kve_end); printf("%s", kve->kve_protection & KVME_PROT_READ ? "r" : "-"); printf("%s", kve->kve_protection & KVME_PROT_WRITE ? "w" : "-"); printf("%s ", kve->kve_protection & KVME_PROT_EXEC ? "x" : "-");