summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/kern/kern_descrip.c108
-rw-r--r--sys/kern/kern_proc.c138
-rw-r--r--sys/sys/user.h19
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 */
OpenPOWER on IntegriCloud