diff options
-rw-r--r-- | sys/kern/kern_descrip.c | 108 | ||||
-rw-r--r-- | sys/kern/kern_proc.c | 138 | ||||
-rw-r--r-- | sys/sys/user.h | 19 |
3 files changed, 173 insertions, 92 deletions
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index c765ee2..a9795ca 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$"); #include <sys/protosw.h> #include <sys/racct.h> #include <sys/resourcevar.h> +#include <sys/sbuf.h> #include <sys/signalvar.h> #include <sys/socketvar.h> #include <sys/stat.h> @@ -3168,9 +3169,9 @@ CTASSERT(sizeof(struct kinfo_file) == KINFO_FILE_SIZE); #endif static int -export_fd_for_sysctl(void *data, int type, int fd, int fflags, int refcnt, +export_fd_to_sb(void *data, int type, int fd, int fflags, int refcnt, int64_t offset, cap_rights_t fd_cap_rights, struct kinfo_file *kif, - struct sysctl_req *req) + struct sbuf *sb, ssize_t *remainder) { struct { int fflag; @@ -3197,6 +3198,8 @@ export_fd_for_sysctl(void *data, int type, int fd, int fflags, int refcnt, int error; unsigned int i; + if (*remainder == 0) + return (0); bzero(kif, sizeof(*kif)); switch (type) { case KF_TYPE_FIFO: @@ -3241,32 +3244,40 @@ export_fd_for_sysctl(void *data, int type, int fd, int fflags, int refcnt, 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 (*remainder != -1) { + if (*remainder < kif->kf_structsize) { + /* Terminate export. */ + *remainder = 0; + return (0); + } + *remainder -= kif->kf_structsize; + } + error = sbuf_bcat(sb, kif, kif->kf_structsize); return (error); } /* - * Get per-process file descriptors for use by procstat(1), et al. + * Store a process file descriptor information to sbuf. + * + * Takes a locked proc as argument, and returns with the proc unlocked. */ -static int -sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) +int +kern_proc_filedesc_out(struct proc *p, struct sbuf *sb, ssize_t maxlen) { struct file *fp; struct filedesc *fdp; struct kinfo_file *kif; - struct proc *p; struct vnode *cttyvp, *textvp, *tracevp; - size_t oldidx; int64_t offset; void *data; - int error, i, *name; + ssize_t remainder; + int error, i; int type, refcnt, fflags; cap_rights_t fd_cap_rights; - name = (int *)arg1; - error = pget((pid_t)name[0], PGET_CANDEBUG, &p); - if (error != 0) - return (error); + PROC_LOCK_ASSERT(p, MA_OWNED); + + remainder = maxlen; /* ktrace vnode */ tracevp = p->p_tracevp; if (tracevp != NULL) @@ -3286,14 +3297,15 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) PROC_UNLOCK(p); kif = malloc(sizeof(*kif), M_TEMP, M_WAITOK); if (tracevp != NULL) - export_fd_for_sysctl(tracevp, KF_TYPE_VNODE, KF_FD_TYPE_TRACE, - FREAD | FWRITE, -1, -1, 0, kif, req); + export_fd_to_sb(tracevp, KF_TYPE_VNODE, KF_FD_TYPE_TRACE, + FREAD | FWRITE, -1, -1, 0, kif, sb, &remainder); if (textvp != NULL) - export_fd_for_sysctl(textvp, KF_TYPE_VNODE, KF_FD_TYPE_TEXT, - FREAD, -1, -1, 0, kif, req); + export_fd_to_sb(textvp, KF_TYPE_VNODE, KF_FD_TYPE_TEXT, + FREAD, -1, -1, 0, kif, sb, &remainder); if (cttyvp != NULL) - export_fd_for_sysctl(cttyvp, KF_TYPE_VNODE, KF_FD_TYPE_CTTY, - FREAD | FWRITE, -1, -1, 0, kif, req); + export_fd_to_sb(cttyvp, KF_TYPE_VNODE, KF_FD_TYPE_CTTY, + FREAD | FWRITE, -1, -1, 0, kif, sb, &remainder); + error = 0; if (fdp == NULL) goto fail; FILEDESC_SLOCK(fdp); @@ -3302,8 +3314,8 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) vref(fdp->fd_cdir); data = fdp->fd_cdir; FILEDESC_SUNLOCK(fdp); - export_fd_for_sysctl(data, KF_TYPE_VNODE, KF_FD_TYPE_CWD, - FREAD, -1, -1, 0, kif, req); + export_fd_to_sb(data, KF_TYPE_VNODE, KF_FD_TYPE_CWD, + FREAD, -1, -1, 0, kif, sb, &remainder); FILEDESC_SLOCK(fdp); } /* root directory */ @@ -3311,8 +3323,8 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) vref(fdp->fd_rdir); data = fdp->fd_rdir; FILEDESC_SUNLOCK(fdp); - export_fd_for_sysctl(data, KF_TYPE_VNODE, KF_FD_TYPE_ROOT, - FREAD, -1, -1, 0, kif, req); + export_fd_to_sb(data, KF_TYPE_VNODE, KF_FD_TYPE_ROOT, + FREAD, -1, -1, 0, kif, sb, &remainder); FILEDESC_SLOCK(fdp); } /* jail directory */ @@ -3320,8 +3332,8 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) vref(fdp->fd_jdir); data = fdp->fd_jdir; FILEDESC_SUNLOCK(fdp); - export_fd_for_sysctl(data, KF_TYPE_VNODE, KF_FD_TYPE_JAIL, - FREAD, -1, -1, 0, kif, req); + export_fd_to_sb(data, KF_TYPE_VNODE, KF_FD_TYPE_JAIL, + FREAD, -1, -1, 0, kif, sb, &remainder); FILEDESC_SLOCK(fdp); } for (i = 0; i < fdp->fd_nfiles; i++) { @@ -3403,26 +3415,14 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) * re-validate and re-evaluate its properties when * the loop continues. */ - oldidx = req->oldidx; if (type == KF_TYPE_VNODE || type == KF_TYPE_FIFO) FILEDESC_SUNLOCK(fdp); - error = export_fd_for_sysctl(data, type, i, fflags, refcnt, - offset, fd_cap_rights, kif, req); + error = export_fd_to_sb(data, type, i, fflags, refcnt, + offset, fd_cap_rights, kif, sb, &remainder); if (type == KF_TYPE_VNODE || type == KF_TYPE_FIFO) FILEDESC_SLOCK(fdp); - if (error) { - if (error == ENOMEM) { - /* - * The hack to keep the ABI of sysctl - * kern.proc.filedesc intact, but not - * to account a partially copied - * kinfo_file into the oldidx. - */ - req->oldidx = oldidx; - error = 0; - } + if (error) break; - } } FILEDESC_SUNLOCK(fdp); fail: @@ -3432,6 +3432,34 @@ fail: return (error); } +#define FILEDESC_SBUF_SIZE (sizeof(struct kinfo_file) * 5) + +/* + * Get per-process file descriptors for use by procstat(1), et al. + */ +static int +sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) +{ + struct sbuf sb; + struct proc *p; + ssize_t maxlen; + int error, error2, *name; + + name = (int *)arg1; + + sbuf_new_for_sysctl(&sb, NULL, FILEDESC_SBUF_SIZE, req); + error = pget((pid_t)name[0], PGET_CANDEBUG, &p); + if (error != 0) { + sbuf_delete(&sb); + return (error); + } + maxlen = req->oldptr != NULL ? req->oldlen : -1; + error = kern_proc_filedesc_out(p, &sb, maxlen); + error2 = sbuf_finish(&sb); + sbuf_delete(&sb); + return (error != 0 ? error : error2); +} + int vntype_to_kinfo(int vtype) { diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index e3f3211..ded8950 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -1089,9 +1089,6 @@ zpfind(pid_t pid) return (p); } -#define KERN_PROC_ZOMBMASK 0x3 -#define KERN_PROC_NOTHREADS 0x4 - #ifdef COMPAT_FREEBSD32 /* @@ -1191,59 +1188,69 @@ freebsd32_kinfo_proc_out(const struct kinfo_proc *ki, struct kinfo_proc32 *ki32) CP(*ki, *ki32, ki_sflag); CP(*ki, *ki32, ki_tdflags); } - -static int -sysctl_out_proc_copyout(struct kinfo_proc *ki, struct sysctl_req *req) -{ - struct kinfo_proc32 ki32; - int error; - - if (req->flags & SCTL_MASK32) { - freebsd32_kinfo_proc_out(ki, &ki32); - error = SYSCTL_OUT(req, &ki32, sizeof(struct kinfo_proc32)); - } else - error = SYSCTL_OUT(req, ki, sizeof(struct kinfo_proc)); - return (error); -} -#else -static int -sysctl_out_proc_copyout(struct kinfo_proc *ki, struct sysctl_req *req) -{ - - return (SYSCTL_OUT(req, ki, sizeof(struct kinfo_proc))); -} #endif -/* - * Must be called with the process locked and will return with it unlocked. - */ -static int -sysctl_out_proc(struct proc *p, struct sysctl_req *req, int flags) +int +kern_proc_out(struct proc *p, struct sbuf *sb, int flags) { struct thread *td; - struct kinfo_proc kinfo_proc; - int error = 0; - struct proc *np; - pid_t pid = p->p_pid; + struct kinfo_proc ki; +#ifdef COMPAT_FREEBSD32 + struct kinfo_proc32 ki32; +#endif + int error; PROC_LOCK_ASSERT(p, MA_OWNED); MPASS(FIRST_THREAD_IN_PROC(p) != NULL); - fill_kinfo_proc(p, &kinfo_proc); - if (flags & KERN_PROC_NOTHREADS) - error = sysctl_out_proc_copyout(&kinfo_proc, req); - else { + error = 0; + fill_kinfo_proc(p, &ki); + if ((flags & KERN_PROC_NOTHREADS) != 0) { +#ifdef COMPAT_FREEBSD32 + if ((flags & KERN_PROC_MASK32) != 0) { + freebsd32_kinfo_proc_out(&ki, &ki32); + error = sbuf_bcat(sb, &ki32, sizeof(ki32)); + } else +#endif + error = sbuf_bcat(sb, &ki, sizeof(ki)); + } else { FOREACH_THREAD_IN_PROC(p, td) { - fill_kinfo_thread(td, &kinfo_proc, 1); - error = sysctl_out_proc_copyout(&kinfo_proc, req); + fill_kinfo_thread(td, &ki, 1); +#ifdef COMPAT_FREEBSD32 + if ((flags & KERN_PROC_MASK32) != 0) { + freebsd32_kinfo_proc_out(&ki, &ki32); + error = sbuf_bcat(sb, &ki32, sizeof(ki32)); + } else +#endif + error = sbuf_bcat(sb, &ki, sizeof(ki)); if (error) break; } } PROC_UNLOCK(p); - if (error) + return (error); +} + +static int +sysctl_out_proc(struct proc *p, struct sysctl_req *req, int flags, + int doingzomb) +{ + struct sbuf sb; + struct kinfo_proc ki; + struct proc *np; + int error, error2; + pid_t pid; + + pid = p->p_pid; + sbuf_new_for_sysctl(&sb, (char *)&ki, sizeof(ki), req); + error = kern_proc_out(p, &sb, flags); + error2 = sbuf_finish(&sb); + sbuf_delete(&sb); + if (error != 0) return (error); - if (flags & KERN_PROC_ZOMBMASK) + else if (error2 != 0) + return (error2); + if (doingzomb) np = zpfind(pid); else { if (pid == 0) @@ -1277,6 +1284,10 @@ sysctl_kern_proc(SYSCTL_HANDLER_ARGS) flags = 0; oid_number &= ~KERN_PROC_INC_THREAD; } +#ifdef COMPAT_FREEBSD32 + if (req->flags & SCTL_MASK32) + flags |= KERN_PROC_MASK32; +#endif if (oid_number == KERN_PROC_PID) { if (namelen != 1) return (EINVAL); @@ -1286,7 +1297,7 @@ sysctl_kern_proc(SYSCTL_HANDLER_ARGS) error = pget((pid_t)name[0], PGET_CANSEE, &p); if (error != 0) return (error); - error = sysctl_out_proc(p, req, flags); + error = sysctl_out_proc(p, req, flags, 0); return (error); } @@ -1415,7 +1426,7 @@ sysctl_kern_proc(SYSCTL_HANDLER_ARGS) } - error = sysctl_out_proc(p, req, flags | doingzomb); + error = sysctl_out_proc(p, req, flags, doingzomb); if (error) { sx_sunlock(&allproc_lock); return (error); @@ -2119,8 +2130,11 @@ sysctl_kern_proc_ovmmap(SYSCTL_HANDLER_ARGS) CTASSERT(sizeof(struct kinfo_vmentry) == KINFO_VMENTRY_SIZE); #endif -static int -sysctl_kern_proc_vmmap(SYSCTL_HANDLER_ARGS) +/* + * Must be called with the process locked and will return unlocked. + */ +int +kern_proc_vmmap_out(struct proc *p, struct sbuf *sb) { vm_map_entry_t entry, tmp_entry; unsigned int last_timestamp; @@ -2128,16 +2142,15 @@ sysctl_kern_proc_vmmap(SYSCTL_HANDLER_ARGS) struct kinfo_vmentry *kve; struct vattr va; struct ucred *cred; - int error, *name; + int error; struct vnode *vp; - struct proc *p; struct vmspace *vm; vm_map_t map; - name = (int *)arg1; - error = pget((pid_t)name[0], PGET_WANTREAD, &p); - if (error != 0) - return (error); + PROC_LOCK_ASSERT(p, MA_OWNED); + + _PHOLD(p); + PROC_UNLOCK(p); vm = vmspace_acquire_ref(p); if (vm == NULL) { PRELE(p); @@ -2145,6 +2158,7 @@ sysctl_kern_proc_vmmap(SYSCTL_HANDLER_ARGS) } kve = malloc(sizeof(*kve), M_TEMP, M_WAITOK); + error = 0; map = &vm->vm_map; vm_map_lock_read(map); for (entry = map->header.next; entry != &map->header; @@ -2284,7 +2298,7 @@ sysctl_kern_proc_vmmap(SYSCTL_HANDLER_ARGS) strlen(kve->kve_path) + 1; kve->kve_structsize = roundup(kve->kve_structsize, sizeof(uint64_t)); - error = SYSCTL_OUT(req, kve, kve->kve_structsize); + error = sbuf_bcat(sb, kve, kve->kve_structsize); vm_map_lock_read(map); if (error) break; @@ -2300,6 +2314,26 @@ sysctl_kern_proc_vmmap(SYSCTL_HANDLER_ARGS) return (error); } +static int +sysctl_kern_proc_vmmap(SYSCTL_HANDLER_ARGS) +{ + struct proc *p; + struct sbuf sb; + int error, error2, *name; + + name = (int *)arg1; + sbuf_new_for_sysctl(&sb, NULL, sizeof(struct kinfo_vmentry), req); + error = pget((pid_t)name[0], PGET_CANDEBUG | PGET_NOTWEXIT, &p); + if (error != 0) { + sbuf_delete(&sb); + return (error); + } + error = kern_proc_vmmap_out(p, &sb); + error2 = sbuf_finish(&sb); + sbuf_delete(&sb); + return (error != 0 ? error : error2); +} + #if defined(STACK) || defined(DDB) static int sysctl_kern_proc_kstack(SYSCTL_HANDLER_ARGS) diff --git a/sys/sys/user.h b/sys/sys/user.h index 5de76ac..2f6514b 100644 --- a/sys/sys/user.h +++ b/sys/sys/user.h @@ -491,6 +491,25 @@ struct kinfo_kstack { }; #ifdef _KERNEL +/* Flags for kern_proc_out function. */ +#define KERN_PROC_NOTHREADS 0x1 +#define KERN_PROC_MASK32 0x2 + +struct sbuf; + +/* + * The kern_proc out functions are helper functions to dump process + * miscellaneous kinfo structures to sbuf. The main consumers are KERN_PROC + * sysctls but they may also be used by other kernel subsystems. + * + * The functions manipulate the process locking state and expect the process + * to be locked on enter. On return the process is unlocked. + */ + +int kern_proc_filedesc_out(struct proc *p, struct sbuf *sb, ssize_t maxlen); +int kern_proc_out(struct proc *p, struct sbuf *sb, int flags); +int kern_proc_vmmap_out(struct proc *p, struct sbuf *sb); + int vntype_to_kinfo(int vtype); #endif /* !_KERNEL */ |