#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/sysmsg.h>
#include <sys/exec.h>
#include <sys/imgact.h>
#include <sys/mman.h>
#include <sys/proc.h>
#include <sys/caps.h>
#include <sys/resourcevar.h>
#include <sys/sysent.h>
#include <sys/stat.h>
#include <sys/vnode.h>
#include <sys/sysctl.h>
#include <sys/lock.h>
#include <sys/resident.h>
#include <sys/malloc.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#include <vm/vm_kern.h>
#include <vm/vm_extern.h>
static int exec_res_id = 0;
static TAILQ_HEAD(,vmresident) exec_res_list;
static MALLOC_DEFINE(M_EXEC_RES, "vmresident", "resident execs");
static struct lock exec_list_lock;
static void
vm_resident_init(void *__dummy)
{
lockinit(&exec_list_lock, "vmres", 0, 0);
TAILQ_INIT(&exec_res_list);
}
SYSINIT(vmres, SI_BOOT1_LOCK, SI_ORDER_ANY, vm_resident_init, 0);
static int
fill_xresident(struct vmresident *vr, struct xresident *in, struct thread *td)
{
struct stat st;
struct vnode *vrtmp;
int error = 0;
vrtmp = vr->vr_vnode;
in->res_entry_addr = vr->vr_entry_addr;
in->res_id = vr->vr_id;
if (vrtmp) {
char *freepath, *fullpath;
error = vn_fullpath(td->td_proc, vrtmp, &fullpath, &freepath, 0);
if (error != 0) {
bzero(in->res_file, MAXPATHLEN);
error = 0;
} else {
strlcpy(in->res_file, fullpath, sizeof(in->res_file));
kfree(freepath, M_TEMP);
}
error = vget(vrtmp, LK_EXCLUSIVE);
if (error)
goto done;
error = vn_stat(vrtmp, &st, td->td_ucred);
vput(vrtmp);
if (error)
goto done;
in->res_stat = st;
}
done:
if (error)
kprintf("fill_xresident, error = %d\n", error);
return (error);
}
static int
sysctl_vm_resident(SYSCTL_HANDLER_ARGS)
{
struct vmresident *vmres;
struct thread *td;
int error;
int count;
td = req->td;
error = caps_priv_check_td(td, SYSCAP_NOVM_RESIDENT);
if (error)
return error;
count = 0;
if (exec_res_id == 0)
return error;
if (!req->oldptr)
return SYSCTL_OUT(req, 0, exec_res_id);
lockmgr(&exec_list_lock, LK_SHARED);
TAILQ_FOREACH(vmres, &exec_res_list, vr_link) {
struct xresident xres;
error = fill_xresident(vmres, &xres, td);
if (error != 0)
break;
error = SYSCTL_OUT(req, (void *)&xres,
sizeof(struct xresident));
if (error != 0)
break;
}
lockmgr(&exec_list_lock, LK_RELEASE);
return (error);
}
SYSCTL_PROC(_vm, OID_AUTO, resident, CTLTYPE_OPAQUE|CTLFLAG_RD, 0, 0,
sysctl_vm_resident, "S,xresident", "resident executables (sys/resident.h)");
int
exec_resident_imgact(struct image_params *imgp)
{
struct vmresident *vmres;
lockmgr(&exec_list_lock, LK_SHARED);
if ((vmres = imgp->vp->v_resident) == NULL) {
lockmgr(&exec_list_lock, LK_RELEASE);
return(-1);
}
atomic_add_int(&vmres->vr_refs, 1);
lockmgr(&exec_list_lock, LK_RELEASE);
exec_new_vmspace(imgp, vmres->vr_vmspace);
imgp->resident = 1;
imgp->interpreted = 0;
imgp->proc->p_sysent = vmres->vr_sysent;
imgp->entry_addr = vmres->vr_entry_addr;
atomic_subtract_int(&vmres->vr_refs, 1);
return(0);
}
int
sys_exec_sys_register(struct sysmsg *sysmsg,
const struct exec_sys_register_args *uap)
{
struct thread *td = curthread;
struct vmresident *vmres;
struct vnode *vp;
struct proc *p;
int error;
p = td->td_proc;
error = caps_priv_check_td(td, SYSCAP_NOVM_RESIDENT);
if (error)
return(error);
if ((vp = p->p_textvp) == NULL)
return(ENOENT);
lockmgr(&exec_list_lock, LK_EXCLUSIVE);
if (vp->v_resident) {
lockmgr(&exec_list_lock, LK_RELEASE);
return(EEXIST);
}
vhold(vp);
vmres = kmalloc(sizeof(*vmres), M_EXEC_RES, M_WAITOK | M_ZERO);
vmres->vr_vnode = vp;
vmres->vr_sysent = p->p_sysent;
vmres->vr_id = ++exec_res_id;
vmres->vr_entry_addr = (intptr_t)uap->entry;
vmres->vr_vmspace = vmspace_fork(p->p_vmspace, NULL, NULL);
pmap_pinit2(vmspace_pmap(vmres->vr_vmspace));
vp->v_resident = vmres;
TAILQ_INSERT_TAIL(&exec_res_list, vmres, vr_link);
lockmgr(&exec_list_lock, LK_RELEASE);
return(0);
}
int
sys_exec_sys_unregister(struct sysmsg *sysmsg,
const struct exec_sys_unregister_args *uap)
{
struct thread *td = curthread;
struct vmresident *vmres;
struct proc *p;
int error;
int id;
int count;
p = td->td_proc;
error = caps_priv_check_td(td, SYSCAP_NOVM_RESIDENT);
if (error)
return(error);
lockmgr(&exec_list_lock, LK_EXCLUSIVE);
if ((id = uap->id) == -1 && p->p_textvp && p->p_textvp->v_resident)
id = p->p_textvp->v_resident->vr_id;
error = ENOENT;
count = 0;
restart:
TAILQ_FOREACH(vmres, &exec_res_list, vr_link) {
if (id == -2 || vmres->vr_id == id) {
if (vmres->vr_refs) {
lockmgr(&exec_list_lock, LK_RELEASE);
tsleep(vmres, 0, "vmres", 1);
lockmgr(&exec_list_lock, LK_EXCLUSIVE);
goto restart;
}
TAILQ_REMOVE(&exec_res_list, vmres, vr_link);
if (vmres->vr_vnode) {
vmres->vr_vnode->v_resident = NULL;
vdrop(vmres->vr_vnode);
vmres->vr_vnode = NULL;
}
if (vmres->vr_vmspace) {
vmspace_rel(vmres->vr_vmspace);
vmres->vr_vmspace = NULL;
}
kfree(vmres, M_EXEC_RES);
exec_res_id--;
error = 0;
++count;
goto restart;
}
}
lockmgr(&exec_list_lock, LK_RELEASE);
if (error == 0)
sysmsg->sysmsg_result = count;
return(error);
}