#include <sys/param.h>
#include <sys/systm.h>
#include <sys/file.h>
#include <sys/lock.h>
#include <sys/protosw.h>
#include <sys/signalvar.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/socketops.h>
#include <sys/filio.h>
#include <sys/sockio.h>
#include <sys/vnode.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <sys/filedesc.h>
#include <sys/ucred.h>
#include <sys/socketvar2.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/route.h>
struct fileops socketops = {
.fo_read = soo_read,
.fo_write = soo_write,
.fo_ioctl = soo_ioctl,
.fo_kqfilter = sokqfilter,
.fo_stat = soo_stat,
.fo_close = soo_close,
.fo_shutdown = soo_shutdown,
.fo_seek = badfo_seek
};
int
soo_read(struct file *fp, struct uio *uio, struct ucred *cred, int fflags)
{
struct socket *so;
int error;
int msgflags;
atomic_set_int(&curthread->td_mpflags, TDF_MP_BATCH_DEMARC);
so = (struct socket *)fp->f_data;
if (fflags & O_FBLOCKING)
msgflags = 0;
else if (fflags & O_FNONBLOCKING)
msgflags = MSG_FNONBLOCKING;
else if (fp->f_flag & FNONBLOCK)
msgflags = MSG_FNONBLOCKING;
else
msgflags = 0;
error = so_pru_soreceive(so, NULL, uio, NULL, NULL, &msgflags);
return (error);
}
int
soo_write(struct file *fp, struct uio *uio, struct ucred *cred, int fflags)
{
struct socket *so;
int error;
int msgflags;
so = (struct socket *)fp->f_data;
if (fflags & O_FBLOCKING)
msgflags = 0;
else if (fflags & O_FNONBLOCKING)
msgflags = MSG_FNONBLOCKING;
else if (fp->f_flag & FNONBLOCK)
msgflags = MSG_FNONBLOCKING;
else
msgflags = 0;
error = so_pru_sosend(so, NULL, uio, NULL, NULL, msgflags, uio->uio_td);
if (error == EPIPE && !(fflags & MSG_NOSIGNAL) &&
!(so->so_options & SO_NOSIGPIPE) &&
uio->uio_td->td_lwp) {
lwpsignal(uio->uio_td->td_proc, uio->uio_td->td_lwp, SIGPIPE);
}
return (error);
}
int
soo_ioctl(struct file *fp, u_long cmd, caddr_t data,
struct ucred *cred, struct sysmsg *msg)
{
struct socket *so;
int error;
so = (struct socket *)fp->f_data;
switch (cmd) {
case FIONBIO:
error = 0;
break;
case FIOASYNC:
if (*(int *)data) {
sosetstate(so, SS_ASYNC);
atomic_set_int(&so->so_rcv.ssb_flags, SSB_ASYNC);
atomic_set_int(&so->so_snd.ssb_flags, SSB_ASYNC);
} else {
soclrstate(so, SS_ASYNC);
atomic_clear_int(&so->so_rcv.ssb_flags, SSB_ASYNC);
atomic_clear_int(&so->so_snd.ssb_flags, SSB_ASYNC);
}
error = 0;
break;
case FIONREAD:
*(int *)data = so->so_rcv.ssb_cc;
error = 0;
break;
case FIOSETOWN:
error = fsetown(*(int *)data, &so->so_sigio);
break;
case FIOGETOWN:
*(int *)data = fgetown(&so->so_sigio);
error = 0;
break;
case SIOCSPGRP:
error = fsetown(-(*(int *)data), &so->so_sigio);
break;
case SIOCGPGRP:
*(int *)data = -fgetown(&so->so_sigio);
error = 0;
break;
case SIOCATMARK:
*(int *)data = (so->so_state&SS_RCVATMARK) != 0;
error = 0;
break;
default:
if (IOCGROUP(cmd) == 'i') {
error = ifioctl(so, cmd, data, cred);
} else if (IOCGROUP(cmd) == 'r') {
error = rtioctl(cmd, data, cred);
} else {
error = so_pru_control_direct(so, cmd, data, NULL);
}
break;
}
return (error);
}
int
soo_stat(struct file *fp, struct stat *ub, struct ucred *cred)
{
struct socket *so;
int error;
bzero((caddr_t)ub, sizeof (*ub));
ub->st_mode = S_IFSOCK;
so = (struct socket *)fp->f_data;
if ((so->so_state & SS_CANTRCVMORE) == 0 ||
so->so_rcv.ssb_cc != 0)
ub->st_mode |= S_IRUSR | S_IRGRP | S_IROTH;
if ((so->so_state & SS_CANTSENDMORE) == 0)
ub->st_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
ub->st_size = so->so_rcv.ssb_cc;
ub->st_uid = so->so_cred->cr_uid;
ub->st_gid = so->so_cred->cr_gid;
ub->st_ino = so->so_inum;
error = so_pru_sense(so, ub);
return (error);
}
int
soo_close(struct file *fp)
{
int error;
fp->f_ops = &badfileops;
if (fp->f_data)
error = soclose((struct socket *)fp->f_data, fp->f_flag);
else
error = 0;
fp->f_data = NULL;
return (error);
}
int
soo_shutdown(struct file *fp, int how)
{
int error;
if (fp->f_data)
error = soshutdown((struct socket *)fp->f_data, how);
else
error = 0;
return (error);
}