summaryrefslogblamecommitdiffstats
path: root/c/src/libnetworking/rtems/rtems_syscall.c
blob: acba1f8322538e352ebda6e9f403450926dfd61a (plain) (tree)
1
2
3
4
5
6
7



        

                   
                         














                               
                      





                      






                                                                                      






























                                                                        
               





                                                            
                                                                         



















                                                
                                                         

























                                                                        
                                                         














                                                                           

                         






                                                                           


                                         




                                     
        

                                         


                              












                                          
                                                         

















                                                   
                                                           

















                                                                            
                                                    











                                                  
                                                                 
























                                                               














                                                       
                                        

                            

                        



               














                                                         
                                                         





































































































                                                                                               
                                                         

























































































                                                                                                



                                           


























                                                                 
                                                         



































                                                                  
                                                         































                                                                                    

                                                                       






                                         
                                                         








                                                  



                                                                         
                    
                           













                                                  










                                                        

                                                                         
                                                                         


                                                                         
                                       


                          

                                         

                                        


                                                  

                                          

                              

                          



                 
                                                                      
 
                                                   


          
                                                                             
 
                                                   


          
                                                                                  


                          

                                                 
                                                


                                                  
                                                 
                 














                                                                                
                                                                         




                                         

                                        


                                                  
                                                    







                                          
          









                                                          
                                                      








                                                  






                                                                           





                                           





                                               
                                           
  
/*
 *  $Id$
 */

#include <string.h>
#include <stdarg.h>
/* #include <stdlib.h> */
#include <stdio.h>

#include <rtems.h>
#include <rtems/libio.h>
#include <rtems/error.h>
#include <rtems/rtems_bsdnet.h>

#include <sys/errno.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/protosw.h>
#include <sys/proc.h>
#include <sys/fcntl.h>
#include <sys/filio.h>

#include <net/if.h>
#include <net/route.h>

/*
 * Hooks to RTEMS I/O system
 */
static const rtems_filesystem_file_handlers_r socket_handlers;
int rtems_bsdnet_makeFdForSocket(void *so, const rtems_filesystem_file_handlers_r *h);
struct socket *rtems_bsdnet_fdToSocket(int fd);

/*
 * Package system call argument into mbuf.
 */
static int
sockargstombuf (struct mbuf **mp, const void *buf, int buflen, int type)
{
	struct mbuf *m;

	if ((u_int)buflen > MLEN)
		return (EINVAL);
	m = m_get(M_WAIT, type);
	if (m == NULL)
		return (ENOBUFS);
	m->m_len = buflen;
	memcpy (mtod(m, caddr_t), buf, buflen);
	*mp = m;
	if (type == MT_SONAME) {
		struct sockaddr *sa;
		sa = mtod(m, struct sockaddr *);
		sa->sa_len = buflen;
	}
	return 0;
}

/*
 *********************************************************************
 *                       BSD-style entry points                      *
 *********************************************************************
 */
int
socket (int domain, int type, int protocol)
{
	int fd;
	int error;
	struct socket *so;

	rtems_bsdnet_semaphore_obtain ();
	error = socreate(domain, &so, type, protocol, NULL);
	if (error == 0) {
		fd = rtems_bsdnet_makeFdForSocket (so, &socket_handlers);
		if (fd < 0)
			soclose (so);
	}
	else {
		errno = error;
		fd = -1;
	}
	rtems_bsdnet_semaphore_release ();
	return fd;
}

int
bind (int s, struct sockaddr *name, int namelen)
{
	int error;
	int ret = -1;
	struct socket *so;
	struct mbuf *nam;

	rtems_bsdnet_semaphore_obtain ();
	if ((so = rtems_bsdnet_fdToSocket (s)) != NULL) {
		error = sockargstombuf (&nam, name, namelen, MT_SONAME);
		if (error == 0) {
			error = sobind (so, nam);
			if (error == 0)
				ret = 0;
			else
				errno = error;
			m_freem (nam);
		}
		else {
			errno = error;
		}
	}
	rtems_bsdnet_semaphore_release ();
	return ret;
}

int
connect (int s, struct sockaddr *name, int namelen)
{
	int error;
	int ret = -1;
	struct socket *so;
	struct mbuf *nam;

	rtems_bsdnet_semaphore_obtain ();
	if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) {
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
	if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) {
		errno = EALREADY;
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
	error = sockargstombuf (&nam, name, namelen, MT_SONAME);
	if (error) {
		errno = error;
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
	error = soconnect (so, nam);
	if (error)
		goto bad;
	if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) {
		m_freem(nam);
		errno = EINPROGRESS;
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
	while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
		error = soconnsleep (so);
		if (error)
			break;
	}
	if (error == 0) {
		error = so->so_error;
		so->so_error = 0;
	}
    bad:
	so->so_state &= ~SS_ISCONNECTING;
	m_freem (nam);
	if (error)
		errno = error;
	else
		ret = 0;
	rtems_bsdnet_semaphore_release ();
	return ret;
}

int
listen (int s, int backlog)
{
	int error;
	int ret = -1;
	struct socket *so;

	rtems_bsdnet_semaphore_obtain ();
	if ((so = rtems_bsdnet_fdToSocket (s)) != NULL) {
		error = solisten (so, backlog);
		if (error == 0)
			ret = 0;
		else
			errno = error;
	}
	rtems_bsdnet_semaphore_release ();
	return ret;
}

int
accept (int s, struct sockaddr *name, int *namelen)
{
	int fd;
	struct socket *head, *so;
	struct mbuf *nam;

	rtems_bsdnet_semaphore_obtain ();
	if ((head = rtems_bsdnet_fdToSocket (s)) == NULL) {
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
	if ((head->so_options & SO_ACCEPTCONN) == 0) {
		errno = EINVAL;
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
        if ((head->so_state & SS_NBIO) && head->so_comp.tqh_first == NULL) {
                errno = EWOULDBLOCK;
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
        while (head->so_comp.tqh_first == NULL && head->so_error == 0) {
                if (head->so_state & SS_CANTRCVMORE) {
                        head->so_error = ECONNABORTED;
                        break;
                }
		head->so_error = soconnsleep (head);
        }
	if (head->so_error) {
		errno = head->so_error;
		head->so_error = 0;
		rtems_bsdnet_semaphore_release ();
		return -1;
	}

	so = head->so_comp.tqh_first;
	TAILQ_REMOVE(&head->so_comp, so, so_list);
	head->so_qlen--;

	fd = rtems_bsdnet_makeFdForSocket (so, &socket_handlers);
	if (fd < 0) {
		TAILQ_INSERT_HEAD(&head->so_comp, so, so_list);
		head->so_qlen++;
		soconnwakeup (head);
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
	so->so_state &= ~SS_COMP;
	so->so_head = NULL;

	nam = m_get(M_WAIT, MT_SONAME);
	(void) soaccept(so, nam);
	if (name) {
		 /* check length before it is destroyed */
		if (*namelen > nam->m_len)
			*namelen = nam->m_len;
		memcpy (name, mtod(nam, caddr_t), *namelen);
	}
	m_freem(nam);
	rtems_bsdnet_semaphore_release ();
	return (fd);

}

/*
 *  Shutdown routine
 */

int
shutdown (int s, int how)
{
      struct socket *so;
      int error;

      rtems_bsdnet_semaphore_obtain ();
      if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) {
              rtems_bsdnet_semaphore_release ();
              return -1;
      }
      error = soshutdown(so, how);
      rtems_bsdnet_semaphore_release ();
      if (error) {
              errno = error;
              return -1;
      }
      return 0;
}

/*
 * All `transmit' operations end up calling this routine.
 */
ssize_t
sendmsg (int s, const struct msghdr *mp, int flags)
{
	int ret = -1;
	int error;
	struct uio auio;
	struct iovec *iov;
	struct socket *so;
	struct mbuf *to, *control;
	int i;
	int len;

	rtems_bsdnet_semaphore_obtain ();
	if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) {
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
	auio.uio_iov = mp->msg_iov;
	auio.uio_iovcnt = mp->msg_iovlen;
	auio.uio_segflg = UIO_USERSPACE;
	auio.uio_rw = UIO_WRITE;
	auio.uio_offset = 0;
	auio.uio_resid = 0;
	iov = mp->msg_iov;
	for (i = 0; i < mp->msg_iovlen; i++, iov++) {
		if ((auio.uio_resid += iov->iov_len) < 0) {
			errno = EINVAL;
			rtems_bsdnet_semaphore_release ();
			return -1;
		}
	}
	if (mp->msg_name) {
		error = sockargstombuf (&to, mp->msg_name, mp->msg_namelen, MT_SONAME);
		if (error) {
			errno = error;
			rtems_bsdnet_semaphore_release ();
			return -1;
		}
	}
	else {
		to = NULL;
	}
	if (mp->msg_control) {
		if (mp->msg_controllen < sizeof (struct cmsghdr)) {
			errno = EINVAL;
			if (to)
				m_freem(to);
			rtems_bsdnet_semaphore_release ();
			return -1;
		}
		sockargstombuf (&control, mp->msg_control, mp->msg_controllen, MT_CONTROL);
	}
	else {
		control = NULL;
	}
	len = auio.uio_resid;
	error = sosend (so, to, &auio, (struct mbuf *)0, control, flags);
	if (error) {
		if (auio.uio_resid != len && (error == EINTR || error == EWOULDBLOCK))
			error = 0;
	}
	if (error) 
		errno = error;
	else
		ret = len - auio.uio_resid;
	if (to)
		m_freem(to);
	rtems_bsdnet_semaphore_release ();
	return (ret);
}

/*
 * Send a message to a host
 */
ssize_t
sendto (int s, const void *buf, size_t buflen, int flags, const struct sockaddr *to, int tolen)
{
	struct msghdr msg;
	struct iovec iov;

	iov.iov_base = (void *)buf;
	iov.iov_len = buflen;
	msg.msg_name = (caddr_t)to;
	msg.msg_namelen = tolen;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = NULL;
	msg.msg_controllen = 0;
	return sendmsg (s, &msg, flags);
}

/*
 * Send a message to a connected host
 */
ssize_t
send (int s, const void *buf, size_t buflen, int flags)
{
	return sendto (s, buf, buflen, flags, NULL, 0);
}

/*
 * All `receive' operations end up calling this routine.
 */
ssize_t
recvmsg (int s, struct msghdr *mp, int flags)
{
	int ret = -1;
	int error;
	struct uio auio;
	struct iovec *iov;
	struct socket *so;
	struct mbuf *from = NULL, *control = NULL;
	int i;
	int len;

	rtems_bsdnet_semaphore_obtain ();
	if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) {
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
	auio.uio_iov = mp->msg_iov;
	auio.uio_iovcnt = mp->msg_iovlen;
	auio.uio_segflg = UIO_USERSPACE;
	auio.uio_rw = UIO_READ;
	auio.uio_offset = 0;
	auio.uio_resid = 0;
	iov = mp->msg_iov;
	for (i = 0; i < mp->msg_iovlen; i++, iov++) {
		if ((auio.uio_resid += iov->iov_len) < 0) {
			errno = EINVAL;
			rtems_bsdnet_semaphore_release ();
			return -1;
		}
	}
	len = auio.uio_resid;
	mp->msg_flags = flags;
	error = soreceive (so, &from, &auio, (struct mbuf **)NULL, 
			mp->msg_control ? &control : (struct mbuf **)NULL,
			&mp->msg_flags);
	if (error) {
		if (auio.uio_resid != len && (error == EINTR || error == EWOULDBLOCK))
			error = 0;
	}
	if (error) {
		errno = error;
	}
	else {
		ret = len - auio.uio_resid;
		if (mp->msg_name) {
			len = mp->msg_namelen;
			if ((len <= 0) || (from == NULL)) {
				len = 0;
			}
			else {
				if (len > from->m_len)
					len = from->m_len;
				memcpy (mp->msg_name, mtod(from, caddr_t), len);
			}
			mp->msg_namelen = len;
		}
		if (mp->msg_control) {
			struct mbuf *m;
			caddr_t ctlbuf;

			len = mp->msg_controllen;
			m = control;
			mp->msg_controllen = 0;
			ctlbuf = (caddr_t) mp->msg_control;

			while (m && (len > 0)) {
				unsigned int tocopy;

				if (len >= m->m_len) 
					tocopy = m->m_len;
				else {
					mp->msg_flags |= MSG_CTRUNC;
					tocopy = len;
				}
				memcpy(ctlbuf, mtod(m, caddr_t), tocopy);
				ctlbuf += tocopy;
				len -= tocopy;
				m = m->m_next;
			}
			mp->msg_controllen = ctlbuf - mp->msg_control;
		}
	}
	if (from)
		m_freem (from);
	if (control)
		m_freem (control);
	rtems_bsdnet_semaphore_release ();
	return (ret);
}

/*
 * Receive a message from a host
 */
ssize_t
recvfrom (int s, void *buf, size_t buflen, int flags, const struct sockaddr *from, int *fromlen)
{
	struct msghdr msg;
	struct iovec iov;
	int ret;

	iov.iov_base = buf;
	iov.iov_len = buflen;
	msg.msg_name = (caddr_t)from;
	if (fromlen)
		msg.msg_namelen = *fromlen;
	else
	msg.msg_namelen = 0;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = NULL;
	msg.msg_controllen = 0;
	ret = recvmsg (s, &msg, flags);
	if ((from != NULL) && (fromlen != NULL) && (ret >= 0))
		*fromlen = msg.msg_namelen;
	return ret;
}

/*
 * Receive a message from a connected host
 */
ssize_t
recv (int s, void *buf, size_t buflen, int flags)
{
	return recvfrom (s, buf, buflen, flags, NULL, NULL);
}

int
setsockopt (int s, int level, int name, const void *val, int len)
{
	struct socket *so;
	struct mbuf *m = NULL;
	int error;

	rtems_bsdnet_semaphore_obtain ();
	if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) {
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
	if (len > MLEN) {
		errno = EINVAL;
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
	if (val) {
		error = sockargstombuf (&m, val, len, MT_SOOPTS);
		if (error) {
			errno = error;
			rtems_bsdnet_semaphore_release ();
			return -1;
		}
	}
	error = sosetopt(so, level, name, m);
	if (error) {
		errno = error;
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
	rtems_bsdnet_semaphore_release ();
	return 0;
}

int
getsockopt (int s, int level, int name, void *aval, int *avalsize)
{
	struct socket *so;
	struct mbuf *m = NULL, *m0;
	char *val = aval;
	int i, op, valsize;
	int error;

	rtems_bsdnet_semaphore_obtain ();
	if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) {
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
	if (val)
		valsize = *avalsize;
	else
		valsize = 0;
	if (((error = sogetopt(so, level, name, &m)) == 0) && val && valsize && m) {
		op = 0;
		while (m && op < valsize) {
			i = valsize - op;
			if (i > m->m_len)
				i = m->m_len;
			memcpy (val, mtod(m, caddr_t), i);
			op += i;
			val += i;
			m0 = m;
			MFREE (m0, m);
		}
		*avalsize = op;
	}
	if (m != NULL)
		(void) m_free(m);
	if (error) {
		errno = error;
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
	rtems_bsdnet_semaphore_release ();
	return 0;
}

static int
getpeersockname (int s, struct sockaddr *name, int *namelen, int pflag)
{
	struct socket *so;
	struct mbuf *m;
	int len = *namelen;
	int error;

	rtems_bsdnet_semaphore_obtain ();
	if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) {
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
	m = m_getclr(M_WAIT, MT_SONAME);
	if (m == NULL) {
		errno = ENOBUFS;
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
	if (pflag)
		error = (*so->so_proto->pr_usrreqs->pru_peeraddr)(so, m);
	else
		error = (*so->so_proto->pr_usrreqs->pru_sockaddr)(so, m);
	if (error) {
		m_freem(m);
		errno = error;
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
	if (len > m->m_len) {
		len = m->m_len;
		*namelen = len;
	}
	memcpy (name, mtod(m, caddr_t), len);
	m_freem (m);
	rtems_bsdnet_semaphore_release ();
	return 0;
}

int
getpeername (int s, struct sockaddr *name, int *namelen)
{
	return getpeersockname (s, name, namelen, 1);
}
int
getsockname (int s, struct sockaddr *name, int *namelen)
{
	return getpeersockname (s, name, namelen, 0);
}

/*
 ************************************************************************
 *                      RTEMS I/O HANDLER ROUTINES                      *
 ************************************************************************
 */
static int
rtems_bsdnet_close (rtems_libio_t *iop)
{
	struct socket *so;
	int error;

	rtems_bsdnet_semaphore_obtain ();
	if ((so = iop->data1) == NULL) {
		errno = EBADF;
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
	error = soclose (so);
	rtems_bsdnet_semaphore_release ();
	if (error) {
		errno = error;
		return -1;
	}
	return 0;
}

static int
rtems_bsdnet_read (rtems_libio_t *iop, void *buffer, unsigned32 count)
{
	return recv (iop->data0, buffer, count, 0);
}

static int
rtems_bsdnet_write (rtems_libio_t *iop, const void *buffer, unsigned32 count)
{
	return send (iop->data0, buffer, count, 0);
}

static int
so_ioctl (rtems_libio_t *iop, struct socket *so, unsigned32 command, void *buffer)
{
	switch (command) {
	case FIONBIO:
		if (*(int *)buffer) {
			iop->flags |= O_NONBLOCK;
			so->so_state |= SS_NBIO;
		}
		else {
			iop->flags &= ~O_NONBLOCK;
			so->so_state &= ~SS_NBIO;
		}
		return 0;

	case FIONREAD:
		*(int *)buffer = so->so_rcv.sb_cc;
		return 0;
	}

	if (IOCGROUP(command) == 'i')
		return ifioctl (so, command, buffer, NULL);
	if (IOCGROUP(command) == 'r')
		return rtioctl (command, buffer, NULL);
	return (*so->so_proto->pr_usrreqs->pru_control)(so, command, buffer, 0);
}

static int
rtems_bsdnet_ioctl (rtems_libio_t *iop, unsigned32 command, void *buffer)
{
	struct socket *so;
	int error;

	rtems_bsdnet_semaphore_obtain ();
	if ((so = iop->data1) == NULL) {
		errno = EBADF;
		rtems_bsdnet_semaphore_release ();
		return -1;
	}
	error = so_ioctl (iop, so, command, buffer);
	rtems_bsdnet_semaphore_release ();
	if (error) {
		errno = error;
		return -1;
	}
	return 0;
}

static int
rtems_bsdnet_fcntl (int cmd, rtems_libio_t *iop)
{
	struct socket *so;

	if (cmd == F_SETFL) {
		rtems_bsdnet_semaphore_obtain ();
		if ((so = iop->data1) == NULL) {
			rtems_bsdnet_semaphore_release ();
			return EBADF;
		}
		if (iop->flags & LIBIO_FLAGS_NO_DELAY)
			so->so_state |= SS_NBIO;
		else
			so->so_state &= ~SS_NBIO;
		rtems_bsdnet_semaphore_release ();
	}
        return 0;
}

static int
rtems_bsdnet_fstat (rtems_filesystem_location_info_t *loc, struct stat *sp)
{
	sp->st_mode = S_IFSOCK;
	return 0;
}

static const rtems_filesystem_file_handlers_r socket_handlers = {
	NULL,			/* open */
	rtems_bsdnet_close,	/* close */
	rtems_bsdnet_read,	/* read */
	rtems_bsdnet_write,	/* write */
	rtems_bsdnet_ioctl,	/* ioctl */
	NULL,			/* lseek */
	rtems_bsdnet_fstat,	/* fstat */
	NULL,			/* fchmod */
	NULL,			/* ftruncate */
	NULL,			/* fpathconf */
	NULL,			/* fsync */
	NULL,			/* fdatasync */
	rtems_bsdnet_fcntl,	/* fcntl */
};