#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/fcntl.h>
#include <sys/proc.h>
#include <sys/caps.h>
#include <sys/signalvar.h>
#include <sys/vnode.h>
#include <sys/uio.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/dirent.h>
#include <sys/malloc.h>
#include <sys/reg.h>
#include <vm/vm_zone.h>
#include <vfs/procfs/procfs.h>
#include <sys/pioctl.h>
#include <sys/spinlock2.h>
#include <machine/limits.h>
static int procfs_access (struct vop_access_args *);
static int procfs_badop (struct vop_generic_args *);
static int procfs_bmap (struct vop_bmap_args *);
static int procfs_close (struct vop_close_args *);
static int procfs_getattr (struct vop_getattr_args *);
static int procfs_inactive (struct vop_inactive_args *);
static int procfs_ioctl (struct vop_ioctl_args *);
static int procfs_kqfilter (struct vop_kqfilter_args *);
static int procfs_lookup (struct vop_old_lookup_args *);
static int procfs_open (struct vop_open_args *);
static int procfs_print (struct vop_print_args *);
static int procfs_readdir (struct vop_readdir_args *);
static int procfs_readlink (struct vop_readlink_args *);
static int procfs_reclaim (struct vop_reclaim_args *);
static int procfs_setattr (struct vop_setattr_args *);
static int procfs_readdir_proc(struct vop_readdir_args *);
static int procfs_readdir_root(struct vop_readdir_args *);
struct vop_ops procfs_vnode_vops = {
.vop_default = vop_defaultop,
.vop_access = procfs_access,
.vop_advlock = (void *)procfs_badop,
.vop_bmap = procfs_bmap,
.vop_close = procfs_close,
.vop_old_create = (void *)procfs_badop,
.vop_getattr = procfs_getattr,
.vop_inactive = procfs_inactive,
.vop_old_link = (void *)procfs_badop,
.vop_old_lookup = procfs_lookup,
.vop_old_mkdir = (void *)procfs_badop,
.vop_old_mknod = (void *)procfs_badop,
.vop_open = procfs_open,
.vop_pathconf = vop_stdpathconf,
.vop_print = procfs_print,
.vop_read = procfs_rw,
.vop_readdir = procfs_readdir,
.vop_readlink = procfs_readlink,
.vop_reclaim = procfs_reclaim,
.vop_old_remove = (void *)procfs_badop,
.vop_old_rename = (void *)procfs_badop,
.vop_old_rmdir = (void *)procfs_badop,
.vop_setattr = procfs_setattr,
.vop_old_symlink = (void *)procfs_badop,
.vop_write = (void *)procfs_rw,
.vop_ioctl = procfs_ioctl,
.vop_kqfilter = procfs_kqfilter,
};
static struct proc_target {
u_char pt_type;
u_char pt_namlen;
char *pt_name;
pfstype pt_pfstype;
int (*pt_valid) (struct lwp *p);
} proc_targets[] = {
#define N(s) sizeof(s)-1, s
{ DT_DIR, N("."), Pproc, NULL },
{ DT_DIR, N(".."), Proot, NULL },
{ DT_REG, N("mem"), Pmem, NULL },
{ DT_REG, N("regs"), Pregs, procfs_validregs },
{ DT_REG, N("fpregs"), Pfpregs, procfs_validfpregs },
{ DT_REG, N("dbregs"), Pdbregs, procfs_validdbregs },
{ DT_REG, N("ctl"), Pctl, NULL },
{ DT_REG, N("status"), Pstatus, NULL },
{ DT_REG, N("note"), Pnote, NULL },
{ DT_REG, N("notepg"), Pnotepg, NULL },
{ DT_REG, N("map"), Pmap, procfs_validmap },
{ DT_REG, N("etype"), Ptype, procfs_validtype },
{ DT_REG, N("cmdline"), Pcmdline, NULL },
{ DT_REG, N("rlimit"), Prlimit, NULL },
{ DT_LNK, N("file"), Pfile, NULL },
{ DT_LNK, N("exe"), Pfile, NULL },
#undef N
};
static const int nproc_targets = NELEM(proc_targets);
static pid_t atopid (const char *, u_int);
static int
procfs_open(struct vop_open_args *ap)
{
struct pfsnode *pfs = VTOPFS(ap->a_vp);
struct proc *p1, *p2;
int error;
p2 = pfs_pfind(pfs->pfs_pid);
if (p2 == NULL)
return (ENOENT);
if (pfs->pfs_pid && !PRISON_CHECK(ap->a_cred, p2->p_ucred)) {
error = ENOENT;
goto done;
}
switch (pfs->pfs_type) {
case Pmem:
if (((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL)) ||
((pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE))) {
error = EBUSY;
goto done;
}
p1 = curproc;
KKASSERT(p1);
if ((p2->p_flags & P_INEXEC) != 0) {
error = EAGAIN;
goto done;
}
if (!CHECKIO(p1, p2) || p_trespass(ap->a_cred, p2->p_ucred)) {
error = EPERM;
goto done;
}
if (ap->a_mode & FWRITE)
pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL);
break;
default:
break;
}
error = vop_stdopen(ap);
done:
pfs_pdone(p2);
return error;
}
static int
procfs_close(struct vop_close_args *ap)
{
struct pfsnode *pfs = VTOPFS(ap->a_vp);
struct proc *p;
vn_lock(ap->a_vp, LK_UPGRADE | LK_RETRY);
switch (pfs->pfs_type) {
case Pmem:
if ((ap->a_fflag & FWRITE) && (pfs->pfs_flags & O_EXCL))
pfs->pfs_flags &= ~(FWRITE|O_EXCL);
p = NULL;
if ((ap->a_vp->v_opencount < 2)
&& ((p = pfs_pfind(pfs->pfs_pid)) != NULL ||
(p = pfs_zpfind(pfs->pfs_pid)) != NULL)
&& !(p->p_pfsflags & PF_LINGER)) {
spin_lock(&p->p_spin);
p->p_stops = 0;
p->p_step = 0;
spin_unlock(&p->p_spin);
wakeup(&p->p_stype);
wakeup(&p->p_step);
}
pfs_pdone(p);
break;
default:
break;
}
return (vop_stdclose(ap));
}
static int
procfs_ioctl(struct vop_ioctl_args *ap)
{
struct pfsnode *pfs = VTOPFS(ap->a_vp);
struct proc *procp;
struct proc *p;
int error;
int signo;
struct procfs_status *psp;
unsigned char flags;
procp = pfs_pfind(pfs->pfs_pid);
if (procp == NULL)
return ENOTTY;
p = curproc;
if (p == NULL) {
error = EINVAL;
goto done;
}
if ((procp->p_flags & P_INEXEC) != 0) {
error = EAGAIN;
goto done;
}
if (!CHECKIO(p, procp) || p_trespass(ap->a_cred, procp->p_ucred)) {
error = EPERM;
goto done;
}
switch (ap->a_command) {
case PIOCBIS:
spin_lock(&procp->p_spin);
procp->p_stops |= *(unsigned int*)ap->a_data;
spin_unlock(&procp->p_spin);
break;
case PIOCBIC:
spin_lock(&procp->p_spin);
procp->p_stops &= ~*(unsigned int*)ap->a_data;
spin_unlock(&procp->p_spin);
break;
case PIOCSFL:
#define NFLAGS (PF_ISUGID)
flags = (unsigned char)*(unsigned int*)ap->a_data;
if (flags & NFLAGS &&
(error = caps_priv_check(ap->a_cred,
SYSCAP_RESTRICTEDROOT))) {
goto done;
}
procp->p_pfsflags = flags;
break;
case PIOCGFL:
*(unsigned int*)ap->a_data = (unsigned int)procp->p_pfsflags;
break;
case PIOCSTATUS:
psp = (struct procfs_status *)ap->a_data;
psp->flags = procp->p_pfsflags;
psp->events = procp->p_stops;
spin_lock(&procp->p_spin);
if (procp->p_step) {
psp->state = 0;
psp->why = procp->p_stype;
psp->val = procp->p_xstat;
spin_unlock(&procp->p_spin);
} else {
psp->state = 1;
spin_unlock(&procp->p_spin);
psp->why = 0;
psp->val = 0;
}
break;
case PIOCWAIT:
psp = (struct procfs_status *)ap->a_data;
spin_lock(&procp->p_spin);
while (procp->p_step == 0) {
tsleep_interlock(&procp->p_stype, PCATCH);
spin_unlock(&procp->p_spin);
if (procp->p_stops == 0) {
error = 0;
goto done;
}
if (procp->p_flags & P_POSTEXIT) {
error = EINVAL;
goto done;
}
if (procp->p_flags & P_INEXEC) {
error = EAGAIN;
goto done;
}
error = tsleep(&procp->p_stype, PCATCH | PINTERLOCKED,
"piocwait", 0);
if (error)
goto done;
spin_lock(&procp->p_spin);
}
spin_unlock(&procp->p_spin);
psp->state = 1;
psp->flags = procp->p_pfsflags;
psp->events = procp->p_stops;
psp->why = procp->p_stype;
psp->val = procp->p_xstat;
break;
case PIOCCONT:
if (procp->p_step == 0) {
error = EINVAL;
goto done;
}
if ((signo = *(int*)ap->a_data) != 0) {
if (signo >= NSIG || signo <= 0) {
error = EINVAL;
goto done;
}
ksignal(procp, signo);
}
procp->p_step = 0;
wakeup(&procp->p_step);
break;
default:
error = ENOTTY;
goto done;
}
error = 0;
done:
pfs_pdone(procp);
return error;
}
static int
procfs_bmap(struct vop_bmap_args *ap)
{
if (ap->a_doffsetp != NULL)
*ap->a_doffsetp = ap->a_loffset;
if (ap->a_runp != NULL)
*ap->a_runp = 0;
if (ap->a_runb != NULL)
*ap->a_runb = 0;
return (0);
}
static int
procfs_inactive(struct vop_inactive_args *ap)
{
struct pfsnode *pfs = VTOPFS(ap->a_vp);
if (pfs->pfs_pid & PFS_DEAD)
vrecycle(ap->a_vp);
return (0);
}
static int
procfs_reclaim(struct vop_reclaim_args *ap)
{
return (procfs_freevp(ap->a_vp));
}
static int
procfs_print(struct vop_print_args *ap)
{
struct pfsnode *pfs = VTOPFS(ap->a_vp);
kprintf("tag VT_PROCFS, type %d, pid %ld, mode %x, flags %lx\n",
pfs->pfs_type, (long)pfs->pfs_pid, pfs->pfs_mode, pfs->pfs_flags);
return (0);
}
static int
procfs_badop(struct vop_generic_args *ap)
{
return (EIO);
}
static int
procfs_getattr(struct vop_getattr_args *ap)
{
struct pfsnode *pfs = VTOPFS(ap->a_vp);
struct vattr *vap = ap->a_vap;
struct proc *procp;
int error;
switch (pfs->pfs_type) {
case Proot:
case Pcurproc:
procp = NULL;
break;
default:
procp = pfs_pfind(pfs->pfs_pid);
if (procp == NULL || procp->p_ucred == NULL) {
error = ENOENT;
goto done;
}
break;
}
error = 0;
VATTR_NULL(vap);
vap->va_type = ap->a_vp->v_type;
vap->va_mode = pfs->pfs_mode;
vap->va_fileid = pfs->pfs_fileno;
vap->va_flags = 0;
vap->va_blocksize = PAGE_SIZE;
vap->va_bytes = vap->va_size = 0;
vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
vfs_timestamp(&vap->va_ctime);
vap->va_atime = vap->va_mtime = vap->va_ctime;
switch (pfs->pfs_type) {
case Pctl:
case Pregs:
case Pfpregs:
case Pdbregs:
case Pmem:
if (procp->p_flags & P_SUGID) {
vap->va_mode &= ~((VREAD|VWRITE)|
((VREAD|VWRITE)>>3)|
((VREAD|VWRITE)>>6));
}
break;
default:
break;
}
vap->va_nlink = 1;
if (procp) {
if (procp->p_ucred) {
vap->va_uid = procp->p_ucred->cr_uid;
vap->va_gid = procp->p_ucred->cr_gid;
} else {
vap->va_uid = -1;
vap->va_gid = -1;
}
}
switch (pfs->pfs_type) {
case Proot:
vap->va_nlink = 1;
vap->va_uid = 0;
vap->va_gid = 0;
vap->va_size = vap->va_bytes = DEV_BSIZE;
break;
case Pcurproc: {
char buf[16];
vap->va_uid = 0;
vap->va_gid = 0;
vap->va_size = ksnprintf(buf, sizeof(buf),
"%ld", (long)curproc->p_pid);
vap->va_bytes = vap->va_size;
break;
}
case Pproc:
vap->va_nlink = nproc_targets;
vap->va_size = vap->va_bytes = DEV_BSIZE;
break;
case Pfile: {
char *fullpath, *freepath;
if (procp->p_textnch.ncp) {
struct nchandle nch;
cache_copy(&procp->p_textnch, &nch);
error = cache_fullpath(procp, &nch, NULL,
&fullpath, &freepath, 0);
cache_drop(&nch);
} else {
error = EINVAL;
}
if (error == 0) {
vap->va_size = strlen(fullpath);
kfree(freepath, M_TEMP);
} else {
vap->va_size = sizeof("unknown") - 1;
error = 0;
}
vap->va_bytes = vap->va_size;
break;
}
case Pmem:
if (procp->p_flags & P_SUGID)
vap->va_uid = 0;
else if (procp->p_ucred)
vap->va_uid = procp->p_ucred->cr_uid;
else
vap->va_uid = -1;
break;
case Pregs:
vap->va_bytes = vap->va_size = sizeof(struct reg);
break;
case Pfpregs:
vap->va_bytes = vap->va_size = sizeof(struct fpreg);
break;
case Pdbregs:
vap->va_bytes = vap->va_size = sizeof(struct dbreg);
break;
case Ptype:
case Pmap:
case Pctl:
case Pstatus:
case Pnote:
case Pnotepg:
case Pcmdline:
case Prlimit:
break;
default:
panic("procfs_getattr");
}
done:
pfs_pdone(procp);
return (error);
}
static int
procfs_setattr(struct vop_setattr_args *ap)
{
if (ap->a_vap->va_flags != VNOVAL)
return (EOPNOTSUPP);
return (0);
}
static int
procfs_access(struct vop_access_args *ap)
{
struct vattr vattr;
int error;
error = VOP_GETATTR(ap->a_vp, &vattr);
if (!error)
error = vop_helper_access(ap, vattr.va_uid, vattr.va_gid,
vattr.va_mode, 0);
return (error);
}
static int
procfs_lookup(struct vop_old_lookup_args *ap)
{
struct componentname *cnp = ap->a_cnp;
struct vnode **vpp = ap->a_vpp;
struct vnode *dvp = ap->a_dvp;
char *pname = cnp->cn_nameptr;
struct proc_target *pt;
pid_t pid;
struct pfsnode *pfs;
struct proc *p;
struct lwp *lp;
int i;
int error;
*vpp = NULL;
if (cnp->cn_nameiop == NAMEI_DELETE || cnp->cn_nameiop == NAMEI_RENAME)
return (EROFS);
p = NULL;
error = 0;
if (cnp->cn_namelen == 1 && *pname == '.') {
*vpp = dvp;
vref(*vpp);
goto out;
}
pfs = VTOPFS(dvp);
switch (pfs->pfs_type) {
case Proot:
if (cnp->cn_flags & CNP_ISDOTDOT)
return (EIO);
if (CNEQ(cnp, "curproc", 7) || CNEQ(cnp, "self", 4)) {
error = procfs_allocvp(dvp->v_mount, vpp, 0, Pcurproc);
goto out;
}
pid = atopid(pname, cnp->cn_namelen);
if (pid == NO_PID)
break;
p = pfs_pfind(pid);
if (p == NULL)
break;
if (!PRISON_CHECK(ap->a_cnp->cn_cred, p->p_ucred))
break;
if (ps_showallprocs == 0 && ap->a_cnp->cn_cred->cr_uid != 0 &&
ap->a_cnp->cn_cred->cr_uid != p->p_ucred->cr_uid)
break;
error = procfs_allocvp(dvp->v_mount, vpp, pid, Pproc);
goto out;
case Pproc:
if (cnp->cn_flags & CNP_ISDOTDOT) {
error = procfs_root(dvp->v_mount, vpp);
goto out;
}
p = pfs_pfind(pfs->pfs_pid);
if (p == NULL)
break;
lp = FIRST_LWP_IN_PROC(p);
if (lp == NULL)
break;
if (!PRISON_CHECK(ap->a_cnp->cn_cred, p->p_ucred))
break;
if (ps_showallprocs == 0 && ap->a_cnp->cn_cred->cr_uid != 0 &&
ap->a_cnp->cn_cred->cr_uid != p->p_ucred->cr_uid)
break;
for (pt = proc_targets, i = 0; i < nproc_targets; pt++, i++) {
if (cnp->cn_namelen == pt->pt_namlen &&
bcmp(pt->pt_name, pname, cnp->cn_namelen) == 0 &&
(pt->pt_valid == NULL || (*pt->pt_valid)(lp)))
goto found;
}
break;
found:
error = procfs_allocvp(dvp->v_mount, vpp, pfs->pfs_pid,
pt->pt_pfstype);
goto out;
default:
error = ENOTDIR;
goto out;
}
if (cnp->cn_nameiop == NAMEI_LOOKUP)
error = ENOENT;
else
error = EROFS;
out:
if (error == 0 && *vpp != dvp) {
if ((cnp->cn_flags & CNP_LOCKPARENT) == 0) {
cnp->cn_flags |= CNP_PDIRUNLOCK;
vn_unlock(dvp);
}
}
pfs_pdone(p);
return (error);
}
int
procfs_validfile(struct lwp *lp)
{
return (procfs_findtextvp(lp->lwp_proc) != NULLVP);
}
static int
procfs_readdir(struct vop_readdir_args *ap)
{
struct pfsnode *pfs;
int error;
if (ap->a_uio->uio_offset < 0 || ap->a_uio->uio_offset > INT_MAX)
return (EINVAL);
error = vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY | LK_FAILRECLAIM);
if (error)
return (error);
pfs = VTOPFS(ap->a_vp);
switch (pfs->pfs_type) {
case Pproc:
error = procfs_readdir_proc(ap);
break;
case Proot:
error = procfs_readdir_root(ap);
break;
default:
error = ENOTDIR;
break;
}
vn_unlock(ap->a_vp);
return (error);
}
static int
procfs_readdir_proc(struct vop_readdir_args *ap)
{
struct pfsnode *pfs;
int error, i, retval;
struct proc *p;
struct lwp *lp;
struct proc_target *pt;
struct uio *uio = ap->a_uio;
pfs = VTOPFS(ap->a_vp);
p = pfs_pfind(pfs->pfs_pid);
if (p == NULL)
return(0);
if (!PRISON_CHECK(ap->a_cred, p->p_ucred)) {
error = 0;
goto done;
}
lp = FIRST_LWP_IN_PROC(p);
if (lp == NULL) {
error = EINVAL;
goto done;
}
error = 0;
i = (int)uio->uio_offset;
if (i < 0) {
error = EINVAL;
goto done;
}
for (pt = &proc_targets[i];
!error && uio->uio_resid > 0 && i < nproc_targets; pt++, i++) {
if (pt->pt_valid && (*pt->pt_valid)(lp) == 0)
continue;
retval = vop_write_dirent(&error, uio,
PROCFS_FILENO(pfs->pfs_pid, pt->pt_pfstype), pt->pt_type,
pt->pt_namlen, pt->pt_name);
if (retval)
break;
}
uio->uio_offset = (off_t)i;
error = 0;
done:
pfs_pdone(p);
return error;
}
struct procfs_readdir_root_info {
int error;
int i;
int pcnt;
struct uio *uio;
struct ucred *cred;
};
static int procfs_readdir_root_callback(struct proc *p, void *data);
static int
procfs_readdir_root(struct vop_readdir_args *ap)
{
struct procfs_readdir_root_info info;
struct uio *uio = ap->a_uio;
int res;
res = 0;
info.error = 0;
info.i = (int)uio->uio_offset;
if (info.i < 0)
return (EINVAL);
info.pcnt = 0;
info.uio = uio;
info.cred = ap->a_cred;
while (info.pcnt < 4) {
res = procfs_readdir_root_callback(NULL, &info);
if (res < 0)
break;
}
if (res >= 0)
allproc_scan(procfs_readdir_root_callback, &info, 0);
uio->uio_offset = (off_t)info.i;
return (info.error);
}
static int
procfs_readdir_root_callback(struct proc *p, void *data)
{
struct procfs_readdir_root_info *info = data;
struct uio *uio;
int retval;
ino_t d_ino;
const char *d_name;
char d_name_pid[20];
size_t d_namlen;
uint8_t d_type;
uio = info->uio;
if (uio->uio_resid <= 0 || info->error)
return(-1);
switch (info->pcnt) {
case 0:
d_ino = PROCFS_FILENO(0, Proot);
d_name = ".";
d_namlen = 1;
d_type = DT_DIR;
break;
case 1:
d_ino = PROCFS_FILENO(0, Proot);
d_name = "..";
d_namlen = 2;
d_type = DT_DIR;
break;
case 2:
d_ino = PROCFS_FILENO(0, Pcurproc);
d_namlen = 7;
d_name = "curproc";
d_type = DT_LNK;
break;
case 3:
d_ino = PROCFS_FILENO(0, Pcurproc);
d_namlen = 4;
d_name = "self";
d_type = DT_LNK;
break;
default:
if (!PRISON_CHECK(info->cred, p->p_ucred))
return(0);
if (ps_showallprocs == 0 &&
info->cred->cr_uid != 0 &&
info->cred->cr_uid != p->p_ucred->cr_uid) {
return(0);
}
if (info->pcnt < info->i) {
++info->pcnt;
return(0);
}
d_ino = PROCFS_FILENO(p->p_pid, Pproc);
d_namlen = ksnprintf(d_name_pid, sizeof(d_name_pid),
"%ld", (long)p->p_pid);
d_name = d_name_pid;
d_type = DT_DIR;
break;
}
if (info->pcnt < info->i) {
++info->pcnt;
return(0);
}
retval = vop_write_dirent(&info->error, uio,
d_ino, d_type, d_namlen, d_name);
if (retval)
return(-1);
++info->pcnt;
++info->i;
return(0);
}
static int
procfs_readlink(struct vop_readlink_args *ap)
{
char buf[16];
struct proc *procp;
struct vnode *vp = ap->a_vp;
struct pfsnode *pfs = VTOPFS(vp);
char *fullpath, *freepath;
int error, len;
switch (pfs->pfs_type) {
case Pcurproc:
if (pfs->pfs_fileno != PROCFS_FILENO(0, Pcurproc))
return (EINVAL);
len = ksnprintf(buf, sizeof(buf), "%ld", (long)curproc->p_pid);
return (uiomove(buf, len, ap->a_uio));
case Pfile:
procp = pfs_pfind(pfs->pfs_pid);
if (procp == NULL || procp->p_ucred == NULL) {
pfs_pdone(procp);
return (uiomove("unknown", sizeof("unknown") - 1,
ap->a_uio));
}
if (procp->p_textnch.ncp) {
struct nchandle nch;
cache_copy(&procp->p_textnch, &nch);
error = cache_fullpath(procp, &nch, NULL,
&fullpath, &freepath, 0);
cache_drop(&nch);
} else {
error = EINVAL;
}
if (error != 0) {
pfs_pdone(procp);
return (uiomove("unknown", sizeof("unknown") - 1,
ap->a_uio));
}
error = uiomove(fullpath, strlen(fullpath), ap->a_uio);
kfree(freepath, M_TEMP);
pfs_pdone(procp);
return (error);
default:
return (EINVAL);
}
}
static pid_t
atopid(const char *b, u_int len)
{
pid_t p = 0;
while (len--) {
char c = *b++;
if (c < '0' || c > '9')
return (NO_PID);
p = 10 * p + (c - '0');
if (p > PID_MAX)
return (NO_PID);
}
return (p);
}
static void
procfs_filt_detach(struct knote *kn)
{
struct vnode *vp = (void *)kn->kn_hook;
knote_remove(&vp->v_pollinfo.vpi_kqinfo.ki_note, kn);
}
static int
procfs_filt_read(struct knote *kn, long hint)
{
if (hint == NOTE_REVOKE) {
kn->kn_flags |= (EV_EOF | EV_NODATA | EV_ONESHOT);
return (1);
}
kn->kn_data = 0;
if (kn->kn_sfflags & NOTE_OLDAPI)
return (1);
return (kn->kn_data != 0);
}
static int
procfs_filt_write(struct knote *kn, long hint)
{
if (hint == NOTE_REVOKE)
kn->kn_flags |= (EV_EOF | EV_NODATA | EV_ONESHOT);
kn->kn_data = 0;
return (1);
}
static int
procfs_filt_vnode(struct knote *kn, long hint)
{
if (kn->kn_sfflags & hint)
kn->kn_fflags |= hint;
if (hint == NOTE_REVOKE) {
kn->kn_flags |= (EV_EOF | EV_NODATA);
return (1);
}
return (kn->kn_fflags != 0);
}
static struct filterops procfs_read_filtops = {
FILTEROP_ISFD | FILTEROP_MPSAFE, NULL,
procfs_filt_detach, procfs_filt_read,
};
static struct filterops procfs_write_filtops = {
FILTEROP_ISFD | FILTEROP_MPSAFE, NULL,
procfs_filt_detach, procfs_filt_write,
};
static struct filterops procfs_vnode_filtops = {
FILTEROP_ISFD | FILTEROP_MPSAFE, NULL,
procfs_filt_detach, procfs_filt_vnode,
};
static int
procfs_kqfilter(struct vop_kqfilter_args *ap)
{
struct vnode *vp = ap->a_vp;
struct knote *kn = ap->a_kn;
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &procfs_read_filtops;
break;
case EVFILT_WRITE:
kn->kn_fop = &procfs_write_filtops;
break;
case EVFILT_VNODE:
kn->kn_fop = &procfs_vnode_filtops;
break;
default:
return (EOPNOTSUPP);
}
kn->kn_hook = (caddr_t)vp;
knote_insert(&vp->v_pollinfo.vpi_kqinfo.ki_note, kn);
return (0);
}