Re-factor the code to provide kern_proc_filedesc_out(), kern_proc_out(),

and kern_proc_vmmap_out() functions to output process kinfo structures
to sbuf, to make the code reusable.

The functions are going to be used in the coredump routine to store
procstat info in the core program header notes.

Reviewed by:	kib
MFC after:	3 weeks
This commit is contained in:
Mikolaj Golub 2013-04-14 20:01:36 +00:00
parent bd3902134c
commit fe52cf5475
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=249487
3 changed files with 173 additions and 92 deletions

View File

@ -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 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS)
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)
{

View File

@ -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)

View File

@ -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 */