#define VFS_COPYFD (VFS_BASE + 46)
#define VFS_CHECKPERMS (VFS_BASE + 47)
#define VFS_GETSYSINFO (VFS_BASE + 48)
+#define VFS_SOCKET (VFS_BASE + 49)
+#define VFS_SOCKETPAIR (VFS_BASE + 50)
+#define VFS_BIND (VFS_BASE + 51)
+#define VFS_CONNECT (VFS_BASE + 52)
+#define VFS_LISTEN (VFS_BASE + 53)
+#define VFS_ACCEPT (VFS_BASE + 54)
+#define VFS_SENDTO (VFS_BASE + 55)
+#define VFS_SENDMSG (VFS_BASE + 56)
+#define VFS_RECVFROM (VFS_BASE + 57)
+#define VFS_RECVMSG (VFS_BASE + 58)
+#define VFS_SETSOCKOPT (VFS_BASE + 59)
+#define VFS_GETSOCKOPT (VFS_BASE + 60)
+#define VFS_GETSOCKNAME (VFS_BASE + 61)
+#define VFS_GETPEERNAME (VFS_BASE + 62)
+#define VFS_SHUTDOWN (VFS_BASE + 63)
-#define NR_VFS_CALLS 49 /* highest number from base plus one */
+#define NR_VFS_CALLS 64 /* highest number from base plus one */
#endif /* !_MINIX_CALLNR_H */
} mess_lc_vfs_link;
_ASSERT_MSG_SIZE(mess_lc_vfs_link);
+typedef struct {
+ int fd;
+ int backlog;
+
+ u8_t padding[48];
+} mess_lc_vfs_listen;
+_ASSERT_MSG_SIZE(mess_lc_vfs_listen);
+
typedef struct {
off_t offset;
} mess_lc_vfs_select;
_ASSERT_MSG_SIZE(mess_lc_vfs_select);
+typedef struct {
+ int fd;
+ vir_bytes buf; /* void * */
+ size_t len;
+ int flags;
+ vir_bytes addr; /* struct sockaddr * */
+ unsigned int addr_len; /* socklen_t */
+
+ uint8_t padding[32];
+} mess_lc_vfs_sendrecv;
+_ASSERT_MSG_SIZE(mess_lc_vfs_sendrecv);
+
+typedef struct {
+ int fd;
+ int how;
+
+ uint8_t padding[48];
+} mess_lc_vfs_shutdown;
+_ASSERT_MSG_SIZE(mess_lc_vfs_shutdown);
+
+typedef struct {
+ int fd;
+ vir_bytes addr; /* struct sockaddr * */
+ unsigned int addr_len; /* socklen_t */
+
+ uint8_t padding[44];
+} mess_lc_vfs_sockaddr;
+_ASSERT_MSG_SIZE(mess_lc_vfs_sockaddr);
+
+typedef struct {
+ int domain;
+ int type;
+ int protocol;
+
+ uint8_t padding[44];
+} mess_lc_vfs_socket;
+_ASSERT_MSG_SIZE(mess_lc_vfs_socket);
+
+typedef struct {
+ int fd;
+ vir_bytes msgbuf; /* struct msghdr * */
+ int flags;
+
+ uint8_t padding[44];
+} mess_lc_vfs_sockmsg;
+_ASSERT_MSG_SIZE(mess_lc_vfs_sockmsg);
+
+typedef struct {
+ int fd;
+ int level;
+ int name;
+ vir_bytes buf; /* void * */
+ unsigned int len; /* socklen_t */
+
+ uint8_t padding[36];
+} mess_lc_vfs_sockopt;
+_ASSERT_MSG_SIZE(mess_lc_vfs_sockopt);
+
typedef struct {
size_t len;
vir_bytes name; /* const char * */
} mess_vfs_lc_lseek;
_ASSERT_MSG_SIZE(mess_vfs_lc_lseek);
+typedef struct {
+ unsigned int len; /* socklen_t */
+
+ uint8_t padding[52];
+} mess_vfs_lc_socklen;
+_ASSERT_MSG_SIZE(mess_vfs_lc_socklen);
+
typedef struct {
endpoint_t id;
devminor_t minor;
mess_lc_vfs_getvfsstat m_lc_vfs_getvfsstat;
mess_lc_vfs_ioctl m_lc_vfs_ioctl;
mess_lc_vfs_link m_lc_vfs_link;
+ mess_lc_vfs_listen m_lc_vfs_listen;
mess_lc_vfs_lseek m_lc_vfs_lseek;
mess_lc_vfs_mknod m_lc_vfs_mknod;
mess_lc_vfs_mount m_lc_vfs_mount;
mess_lc_vfs_readlink m_lc_vfs_readlink;
mess_lc_vfs_readwrite m_lc_vfs_readwrite;
mess_lc_vfs_select m_lc_vfs_select;
+ mess_lc_vfs_sendrecv m_lc_vfs_sendrecv;
+ mess_lc_vfs_shutdown m_lc_vfs_shutdown;
+ mess_lc_vfs_sockaddr m_lc_vfs_sockaddr;
+ mess_lc_vfs_socket m_lc_vfs_socket;
+ mess_lc_vfs_sockmsg m_lc_vfs_sockmsg;
+ mess_lc_vfs_sockopt m_lc_vfs_sockopt;
mess_lc_vfs_stat m_lc_vfs_stat;
mess_lc_vfs_statvfs1 m_lc_vfs_statvfs1;
mess_lc_vfs_truncate m_lc_vfs_truncate;
mess_vfs_fs_utime m_vfs_fs_utime;
mess_vfs_lc_fdpair m_vfs_lc_fdpair;
mess_vfs_lc_lseek m_vfs_lc_lseek;
+ mess_vfs_lc_socklen m_vfs_lc_socklen;
mess_vfs_lchardriver_cancel m_vfs_lchardriver_cancel;
mess_vfs_lchardriver_openclose m_vfs_lchardriver_openclose;
mess_vfs_lchardriver_readwrite m_vfs_lchardriver_readwrite;
#include <sys/cdefs.h>
#include "namespace.h"
+#include <lib.h>
#include <errno.h>
#include <fcntl.h>
#include <net/gen/udp.h>
#include <net/gen/udp_io.h>
-#define DEBUG 0
-
static int _tcp_accept(int sock, struct sockaddr *__restrict address,
socklen_t *__restrict address_len);
static int _uds_accept(int sock, struct sockaddr *__restrict address,
socklen_t *__restrict address_len);
+/*
+ * Accept a connection on a listening socket, creating a new socket.
+ */
+static int
+__accept(int fd, struct sockaddr * __restrict address,
+ socklen_t * __restrict address_len)
+{
+ message m;
+ int r;
+
+ if (address != NULL && address_len == NULL) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ memset(&m, 0, sizeof(m));
+ m.m_lc_vfs_sockaddr.fd = fd;
+ m.m_lc_vfs_sockaddr.addr = (vir_bytes)address;
+ m.m_lc_vfs_sockaddr.addr_len = (address != NULL) ? *address_len : 0;
+
+ if ((r = _syscall(VFS_PROC_NR, VFS_ACCEPT, &m)) < 0)
+ return -1;
+
+ if (address != NULL)
+ *address_len = m.m_vfs_lc_socklen.len;
+ return r;
+}
+
int accept(int sock, struct sockaddr *__restrict address,
socklen_t *__restrict address_len)
{
int r;
nwio_udpopt_t udpopt;
+ r = __accept(sock, address, address_len);
+ if (r != -1 || errno != ENOTSOCK)
+ return r;
+
r= _tcp_accept(sock, address, address_len);
if (r != -1 || errno != ENOTTY)
return r;
* filedescriptors that do not refer to a socket.
*/
r= ioctl(sock, NWIOGUDPOPT, &udpopt);
- if (r == 0)
- {
+ if (r == 0 || (r == -1 && errno != ENOTTY)) {
/* UDP socket */
errno= EOPNOTSUPP;
return -1;
}
- if (errno == ENOTTY)
- {
- errno= ENOTSOCK;
- return -1;
- }
- return r;
+ errno = ENOTSOCK;
+ return -1;
}
static int _tcp_accept(int sock, struct sockaddr *__restrict address,
#include <sys/cdefs.h>
#include "namespace.h"
+#include <lib.h>
#include <unistd.h>
#include <stdint.h>
static int _uds_bind(int sock, const struct sockaddr *address,
socklen_t address_len, struct sockaddr_un *uds_addr);
+/*
+ * Bind a socket to a local address.
+ */
+static int
+__bind(int fd, const struct sockaddr * address, socklen_t address_len)
+{
+ message m;
+
+ memset(&m, 0, sizeof(m));
+ m.m_lc_vfs_sockaddr.fd = fd;
+ m.m_lc_vfs_sockaddr.addr = (vir_bytes)address;
+ m.m_lc_vfs_sockaddr.addr_len = address_len;
+
+ return _syscall(VFS_PROC_NR, VFS_BIND, &m);
+}
+
int bind(int sock, const struct sockaddr *address, socklen_t address_len)
{
int r;
nwio_udpopt_t udpopt;
struct sockaddr_un uds_addr;
+ r = __bind(sock, address, address_len);
+ if (r != -1 || errno != ENOTSOCK)
+ return r;
+
r= ioctl(sock, NWIOGTCPCONF, &tcpconf);
if (r != -1 || errno != ENOTTY)
{
return _uds_bind(sock, address, address_len, &uds_addr);
}
-#if DEBUG
- fprintf(stderr, "bind: not implemented for fd %d\n", sock);
-#endif
- errno= ENOSYS;
+ errno = ENOTSOCK;
return -1;
}
#include <sys/cdefs.h>
#include "namespace.h"
+#include <lib.h>
+
#include <minix/config.h>
#include <errno.h>
static int _uds_connect(int sock, const struct sockaddr *address,
socklen_t address_len);
+/*
+ * Connect a socket to a remote address.
+ */
+static int
+__connect(int fd, const struct sockaddr * address, socklen_t address_len)
+{
+ message m;
+
+ memset(&m, 0, sizeof(m));
+ m.m_lc_vfs_sockaddr.fd = fd;
+ m.m_lc_vfs_sockaddr.addr = (vir_bytes)address;
+ m.m_lc_vfs_sockaddr.addr_len = address_len;
+
+ return _syscall(VFS_PROC_NR, VFS_CONNECT, &m);
+}
+
int connect(int sock, const struct sockaddr *address,
socklen_t address_len)
{
nwio_tcpconf_t tcpconf;
nwio_udpopt_t udpopt;
+ r = __connect(sock, address, address_len);
+ if (r != -1 || errno != ENOTSOCK)
+ return r;
+
r= ioctl(sock, NWIOGTCPCONF, &tcpconf);
if (r != -1 || errno != ENOTTY)
{
return r;
}
-#if DEBUG
- fprintf(stderr, "connect: not implemented for fd %d\n", sock);
-#endif
- errno= ENOSYS;
+ errno = ENOTSOCK;
return -1;
}
#include <sys/cdefs.h>
#include "namespace.h"
+#include <lib.h>
#include <errno.h>
#include <stdio.h>
#include <net/gen/udp_io.h>
#include <sys/un.h>
-#define DEBUG 0
-
static int _tcp_getpeername(int sock, struct sockaddr *__restrict address,
socklen_t *__restrict address_len, nwio_tcpconf_t *tcpconfp);
static int _uds_getpeername(int sock, struct sockaddr *__restrict address,
socklen_t *__restrict address_len, struct sockaddr_un *uds_addr);
+/*
+ * Get the remote address of a socket.
+ */
+static int
+__getpeername(int fd, struct sockaddr * __restrict address,
+ socklen_t * __restrict address_len)
+{
+ message m;
+
+ if (address_len == NULL) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ memset(&m, 0, sizeof(m));
+ m.m_lc_vfs_sockaddr.fd = fd;
+ m.m_lc_vfs_sockaddr.addr = (vir_bytes)address;
+ m.m_lc_vfs_sockaddr.addr_len = *address_len;
+
+ if (_syscall(VFS_PROC_NR, VFS_GETPEERNAME, &m) < 0)
+ return -1;
+
+ *address_len = m.m_vfs_lc_socklen.len;
+ return 0;
+}
+
int getpeername(int sock, struct sockaddr *__restrict address,
socklen_t *__restrict address_len)
{
nwio_udpopt_t udpopt;
struct sockaddr_un uds_addr;
+ r = __getpeername(sock, address, address_len);
+ if (r != -1 || errno != ENOTSOCK)
+ return r;
+
r= ioctl(sock, NWIOGTCPCONF, &tcpconf);
if (r != -1 || errno != ENOTTY)
{
&uds_addr);
}
-
-#if DEBUG
- fprintf(stderr, "getpeername: not implemented for fd %d\n", sock);
-#endif
- errno= ENOSYS;
+ errno = ENOTSOCK;
return -1;
}
-/*
-
- getsockname()
-
- from socket emulation library for Minix 2.0.x
-
-*/
-
#include <sys/cdefs.h>
#include "namespace.h"
+#include <lib.h>
+
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <net/gen/udp_io.h>
#include <sys/un.h>
-/*
#define DEBUG 0
-*/
static int _tcp_getsockname(int fd, struct sockaddr *__restrict address,
socklen_t *__restrict address_len, nwio_tcpconf_t *tcpconfp);
static int _uds_getsockname(int fd, struct sockaddr *__restrict address,
socklen_t *__restrict address_len, struct sockaddr_un *uds_addr);
+/*
+ * Get the local address of a socket.
+ */
+static int
+__getsockname(int fd, struct sockaddr * __restrict address,
+ socklen_t * __restrict address_len)
+{
+ message m;
+
+ if (address_len == NULL) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ memset(&m, 0, sizeof(m));
+ m.m_lc_vfs_sockaddr.fd = fd;
+ m.m_lc_vfs_sockaddr.addr = (vir_bytes)address;
+ m.m_lc_vfs_sockaddr.addr_len = *address_len;
+
+ if (_syscall(VFS_PROC_NR, VFS_GETSOCKNAME, &m) < 0)
+ return -1;
+
+ *address_len = m.m_vfs_lc_socklen.len;
+ return 0;
+}
+
int getsockname(int fd, struct sockaddr *__restrict address,
socklen_t *__restrict address_len)
{
nwio_udpopt_t udpopt;
struct sockaddr_un uds_addr;
-#ifdef DEBUG
+ r = __getsockname(fd, address, address_len);
+ if (r != -1 || errno != ENOTSOCK)
+ return r;
+
+#if DEBUG
fprintf(stderr,"mnx_getsockname: ioctl fd %d.\n", fd);
#endif
return _uds_getsockname(fd, address, address_len, &uds_addr);
}
-#if DEBUG
- fprintf(stderr, "getsockname: not implemented for fd %d\n", socket);
-#endif
-
- errno= ENOSYS;
+ errno = ENOTSOCK;
return -1;
}
#include <sys/cdefs.h>
#include "namespace.h"
+#include <lib.h>
#include <assert.h>
#include <errno.h>
static void getsockopt_copy(void *return_value, size_t return_len,
void *__restrict option_value, socklen_t *__restrict option_len);
+/*
+ * Get socket options.
+ */
+static int
+__getsockopt(int fd, int level, int option_name,
+ void * __restrict option_value, socklen_t * __restrict option_len)
+{
+ message m;
+
+ if (option_len == NULL) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ memset(&m, 0, sizeof(m));
+ m.m_lc_vfs_sockopt.fd = fd;
+ m.m_lc_vfs_sockopt.level = level;
+ m.m_lc_vfs_sockopt.name = option_name;
+ m.m_lc_vfs_sockopt.buf = (vir_bytes)option_value;
+ m.m_lc_vfs_sockopt.len = *option_len;
+
+ if (_syscall(VFS_PROC_NR, VFS_GETSOCKOPT, &m) < 0)
+ return -1;
+
+ *option_len = m.m_vfs_lc_socklen.len;
+ return 0;
+}
+
int getsockopt(int sock, int level, int option_name,
void *__restrict option_value, socklen_t *__restrict option_len)
{
nwio_udpopt_t udpopt;
struct sockaddr_un uds_addr;
+ r = __getsockopt(sock, level, option_name, option_value, option_len);
+ if (r != -1 || errno != ENOTSOCK)
+ return r;
+
r= ioctl(sock, NWIOGTCPOPT, &tcpopt);
if (r != -1 || errno != ENOTTY)
{
option_value, option_len);
}
-
-#if DEBUG
- fprintf(stderr, "getsockopt: not implemented for fd %d\n", sock);
-#endif
- errno= ENOTSOCK;
+ errno = ENOTSOCK;
return -1;
}
#include <sys/cdefs.h>
#include "namespace.h"
+#include <lib.h>
#include <errno.h>
#include <stdio.h>
#include <net/gen/udp.h>
#include <net/gen/udp_io.h>
-#define DEBUG 0
+/*
+ * Put a socket in listening mode.
+ */
+static int
+__listen(int fd, int backlog)
+{
+ message m;
+
+ memset(&m, 0, sizeof(m));
+ m.m_lc_vfs_listen.fd = fd;
+ m.m_lc_vfs_listen.backlog = backlog;
+
+ return _syscall(VFS_PROC_NR, VFS_LISTEN, &m);
+}
int listen(int sock, int backlog)
{
int r;
+ r = __listen(sock, backlog);
+ if (r != -1 || errno != ENOTSOCK)
+ return r;
+
r= ioctl(sock, NWIOTCPLISTENQ, &backlog);
if (r != -1 || errno != ENOTTY)
return r;
if (r != -1 || errno != ENOTTY)
return r;
-#if DEBUG
- fprintf(stderr, "listen: not implemented for fd %d\n", sock);
-#endif
- errno= ENOSYS;
+ errno = ENOTSOCK;
return -1;
}
-
#include <sys/cdefs.h>
#include "namespace.h"
+#include <lib.h>
#include <assert.h>
#include <errno.h>
size_t length, int flags, struct sockaddr *__restrict address,
socklen_t *__restrict address_len);
+/*
+ * Receive a message from a socket.
+ */
+static ssize_t
+__recvfrom(int fd, void * __restrict buffer, size_t length, int flags,
+ struct sockaddr * __restrict address,
+ socklen_t * __restrict address_len)
+{
+ message m;
+ ssize_t r;
+
+ if (address != NULL && address_len == NULL) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ memset(&m, 0, sizeof(m));
+ m.m_lc_vfs_sendrecv.fd = fd;
+ m.m_lc_vfs_sendrecv.buf = (vir_bytes)buffer;
+ m.m_lc_vfs_sendrecv.len = length;
+ m.m_lc_vfs_sendrecv.flags = flags;
+ m.m_lc_vfs_sendrecv.addr = (vir_bytes)address;
+ m.m_lc_vfs_sendrecv.addr_len = (address != NULL) ? *address_len : 0;
+
+ if ((r = _syscall(VFS_PROC_NR, VFS_RECVFROM, &m)) < 0)
+ return -1;
+
+ if (address != NULL)
+ *address_len = m.m_vfs_lc_socklen.len;
+ return r;
+}
+
ssize_t recvfrom(int sock, void *__restrict buffer, size_t length,
int flags, struct sockaddr *__restrict address,
socklen_t *__restrict address_len)
struct sockaddr_un uds_addr;
int uds_sotype = -1;
+ r = __recvfrom(sock, buffer, length, flags, address, address_len);
+ if (r != -1 || errno != ENOTSOCK)
+ return r;
+
#if DEBUG
fprintf(stderr, "recvfrom: for fd %d\n", sock);
#endif
}
return rd;
- }
+ }
-#if DEBUG
- fprintf(stderr, "recvfrom: not implemented for fd %d\n", sock);
-#endif
- abort();
+ errno = ENOTSOCK;
+ return -1;
}
static ssize_t _tcp_recvfrom(int sock, void *__restrict buffer, size_t length,
#include <sys/cdefs.h>
#include "namespace.h"
+#include <lib.h>
#include <errno.h>
#include <stdio.h>
static ssize_t _uds_recvmsg_conn(int sock, struct msghdr *msg, int flags);
static ssize_t _uds_recvmsg_dgram(int sock, struct msghdr *msg, int flags);
+/*
+ * Receive a message from a socket using a message structure.
+ */
+static ssize_t
+__recvmsg(int fd, struct msghdr * msg, int flags)
+{
+ struct iovec iov;
+ struct msghdr msg2, *msgp;
+ char *ptr;
+ message m;
+ ssize_t r;
+
+ /*
+ * Currently, MINIX3 does not support vector I/O operations. Like in
+ * the readv and writev implementations, we coalesce the data vector
+ * into a single buffer used for I/O. For future ABI compatibility, we
+ * then supply this buffer as a single vector element. This involves
+ * supplying a modified copy of the message header, as well as extra
+ * pre-checks. Once true vector I/O support has been added, the checks
+ * and vector I/O coalescing can be removed from here, leaving just the
+ * system call. Nothing will change at the system call ABI level.
+ */
+ if (msg == NULL || (msg->msg_iovlen > 1 && msg->msg_iov == NULL)) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (msg->msg_iovlen < 0 || msg->msg_iovlen > IOV_MAX) {
+ errno = EMSGSIZE; /* different from readv/writev */
+ return -1;
+ }
+
+ if (msg->msg_iovlen > 1) {
+ if ((r = _vectorio_setup(msg->msg_iov, msg->msg_iovlen, &ptr,
+ _VECTORIO_READ)) < 0)
+ return -1;
+
+ iov.iov_base = ptr;
+ iov.iov_len = r;
+
+ memcpy(&msg2, msg, sizeof(msg2));
+ msg2.msg_iov = &iov;
+ msg2.msg_iovlen = 1;
+ msgp = &msg2;
+ } else
+ msgp = msg;
+
+ /* Issue the actual system call. */
+ memset(&m, 0, sizeof(m));
+ m.m_lc_vfs_sockmsg.fd = fd;
+ m.m_lc_vfs_sockmsg.msgbuf = (vir_bytes)msgp;
+ m.m_lc_vfs_sockmsg.flags = flags;
+
+ r = _syscall(VFS_PROC_NR, VFS_RECVMSG, &m);
+
+ /* If we coalesced the vector, clean up and copy back the results. */
+ if (msgp != msg) {
+ _vectorio_cleanup(msg->msg_iov, msg->msg_iovlen, ptr, r,
+ _VECTORIO_READ);
+
+ if (r >= 0)
+ memcpy(msg, &msg2, sizeof(msg2));
+ }
+
+ return r;
+}
+
ssize_t recvmsg(int sock, struct msghdr *msg, int flags)
{
int r;
int uds_sotype;
+ r = __recvmsg(sock, msg, flags);
+ if (r != -1 || errno != ENOTSOCK)
+ return r;
+
if (msg == NULL) {
errno= EFAULT;
return -1;
}
}
-#if DEBUG
- fprintf(stderr, "recvmsg: not implemented for fd %d\n", sock);
-#endif
-
- errno= ENOSYS;
+ errno = ENOTSOCK;
return -1;
}
#include <sys/cdefs.h>
#include "namespace.h"
+#include <lib.h>
#include <errno.h>
#include <stdlib.h>
static ssize_t _uds_sendmsg_dgram(int sock, const struct msghdr *msg,
int flags);
+/*
+ * Send a message on a socket using a message structure.
+ */
+static ssize_t
+__sendmsg(int fd, const struct msghdr * msg, int flags)
+{
+ struct iovec iov;
+ const struct msghdr *msgp;
+ struct msghdr msg2;
+ char *ptr;
+ message m;
+ ssize_t r;
+
+ /*
+ * Currently, MINIX3 does not support vector I/O operations. Like in
+ * the readv and writev implementations, we coalesce the data vector
+ * into a single buffer used for I/O. For future ABI compatibility, we
+ * then supply this buffer as a single vector element. This involves
+ * supplying a modified copy of the message header, as well as extra
+ * pre-checks. Once true vector I/O support has been added, the checks
+ * and vector I/O coalescing can be removed from here, leaving just the
+ * system call. Nothing will change at the system call ABI level.
+ */
+ if (msg == NULL || (msg->msg_iovlen > 1 && msg->msg_iov == NULL)) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (msg->msg_iovlen < 0 || msg->msg_iovlen > IOV_MAX) {
+ errno = EMSGSIZE; /* different from readv/writev */
+ return -1;
+ }
+
+ if (msg->msg_iovlen > 1) {
+ if ((r = _vectorio_setup(msg->msg_iov, msg->msg_iovlen, &ptr,
+ _VECTORIO_WRITE)) < 0)
+ return -1;
+
+ iov.iov_base = ptr;
+ iov.iov_len = r;
+
+ memcpy(&msg2, msg, sizeof(msg2));
+ msg2.msg_iov = &iov;
+ msg2.msg_iovlen = 1;
+ msgp = &msg2;
+ } else
+ msgp = msg;
+
+ memset(&m, 0, sizeof(m));
+ m.m_lc_vfs_sockmsg.fd = fd;
+ m.m_lc_vfs_sockmsg.msgbuf = (vir_bytes)msgp;
+ m.m_lc_vfs_sockmsg.flags = flags;
+
+ r = _syscall(VFS_PROC_NR, VFS_SENDMSG, &m);
+
+ /* If we coalesced the vector, clean up. */
+ if (msgp != msg) {
+ _vectorio_cleanup(msg->msg_iov, msg->msg_iovlen, ptr, r,
+ _VECTORIO_WRITE);
+ }
+
+ return r;
+}
+
ssize_t sendmsg(int sock, const struct msghdr *msg, int flags)
{
int r;
int uds_sotype;
+ r = __sendmsg(sock, msg, flags);
+ if (r != -1 || errno != ENOTSOCK)
+ return r;
+
if (msg == NULL) {
errno= EFAULT;
return -1;
}
-#if DEBUG
- fprintf(stderr, "sendmsg: not implemented for fd %d\n", sock);
-#endif
-
- errno= ENOSYS;
+ errno = ENOTSOCK;
return -1;
}
#include <sys/cdefs.h>
#include "namespace.h"
+#include <lib.h>
#include <assert.h>
#include <errno.h>
static ssize_t _uds_sendto_dgram(int sock, const void *message, size_t length,
int flags, const struct sockaddr *dest_addr, socklen_t dest_len);
+/*
+ * Send a message on a socket.
+ */
+static ssize_t
+__sendto(int fd, const void * buffer, size_t length, int flags,
+ const struct sockaddr * dest_addr, socklen_t dest_len)
+{
+ message m;
+
+ memset(&m, 0, sizeof(m));
+ m.m_lc_vfs_sendrecv.fd = fd;
+ m.m_lc_vfs_sendrecv.buf = (vir_bytes)buffer;
+ m.m_lc_vfs_sendrecv.len = length;
+ m.m_lc_vfs_sendrecv.flags = flags;
+ m.m_lc_vfs_sendrecv.addr = (vir_bytes)dest_addr;
+ m.m_lc_vfs_sendrecv.addr_len = dest_len;
+
+ return _syscall(VFS_PROC_NR, VFS_SENDTO, &m);
+}
+
ssize_t sendto(int sock, const void *message, size_t length, int flags,
const struct sockaddr *dest_addr, socklen_t dest_len)
{
nwio_ipopt_t ipopt;
int uds_sotype = -1;
+ r = __sendto(sock, message, length, flags, dest_addr, dest_len);
+ if (r != -1 || errno != ENOTSOCK)
+ return r;
+
r= ioctl(sock, NWIOGTCPOPT, &tcpopt);
if (r != -1 || errno != ENOTTY)
{
return retval;
}
-#if DEBUG
- fprintf(stderr, "sendto: not implemented for fd %d\n", sock);
-#endif
- errno= ENOSYS;
+ errno = ENOTSOCK;
return -1;
}
#include <sys/cdefs.h>
#include "namespace.h"
+#include <lib.h>
+#include <string.h>
#include <assert.h>
#include <errno.h>
#include <stdio.h>
static int _uds_setsockopt(int sock, int level, int option_name,
const void *option_value, socklen_t option_len);
+/*
+ * Set socket options.
+ */
+static int
+__setsockopt(int fd, int level, int option_name, const void * option_value,
+ socklen_t option_len)
+{
+ message m;
+
+ memset(&m, 0, sizeof(m));
+ m.m_lc_vfs_sockopt.fd = fd;
+ m.m_lc_vfs_sockopt.level = level;
+ m.m_lc_vfs_sockopt.name = option_name;
+ m.m_lc_vfs_sockopt.buf = (vir_bytes)option_value;
+ m.m_lc_vfs_sockopt.len = option_len;
+
+ return _syscall(VFS_PROC_NR, VFS_SETSOCKOPT, &m);
+}
+
int setsockopt(int sock, int level, int option_name,
const void *option_value, socklen_t option_len)
{
nwio_udpopt_t udpopt;
struct sockaddr_un uds_addr;
+ r = __setsockopt(sock, level, option_name, option_value, option_len);
+ if (r != -1 || errno != ENOTSOCK)
+ return r;
+
r= ioctl(sock, NWIOGTCPOPT, &tcpopt);
if (r != -1 || errno != ENOTTY)
{
option_value, option_len);
}
-
-#if DEBUG
- fprintf(stderr, "setsockopt: not implemented for fd %d\n", sock);
-#endif
- errno= ENOTSOCK;
+ errno = ENOTSOCK;
return -1;
}
#include <sys/cdefs.h>
#include "namespace.h"
+#include <lib.h>
+#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <sys/ioctl.h>
static int _tcp_shutdown(int sock, int how);
static int _uds_shutdown(int sock, int how);
+/*
+ * Shut down socket send and receive operations.
+ */
+static int
+__shutdown(int fd, int how)
+{
+ message m;
+
+ memset(&m, 0, sizeof(m));
+ m.m_lc_vfs_shutdown.fd = fd;
+ m.m_lc_vfs_shutdown.how = how;
+
+ return _syscall(VFS_PROC_NR, VFS_SHUTDOWN, &m);
+}
+
int shutdown(int sock, int how)
{
int r;
struct sockaddr_un uds_addr;
nwio_tcpconf_t tcpconf;
+ r = __shutdown(sock, how);
+ if (r != -1 || errno != ENOTSOCK)
+ return r;
+
r= ioctl(sock, NWIOGTCPCONF, &tcpconf);
if (r != -1 || errno != ENOTTY)
{
return _uds_shutdown(sock, how);
}
-#if DEBUG
- fprintf(stderr, "shutdown: not implemented for fd %d\n", sock);
-#endif
- errno= ENOSYS;
+ errno = ENOTSOCK;
return -1;
}
#include <sys/cdefs.h>
#include "namespace.h"
+#include <lib.h>
#ifdef __weak_alias
__weak_alias(socket, __socket30)
static int _raw_socket(int type, int protocol);
static void _socket_flags(int type, int *result);
+/*
+ * Create a socket.
+ */
+static int
+__socket(int domain, int type, int protocol)
+{
+ message m;
+
+ memset(&m, 0, sizeof(m));
+ m.m_lc_vfs_socket.domain = domain;
+ m.m_lc_vfs_socket.type = type;
+ m.m_lc_vfs_socket.protocol = protocol;
+
+ return _syscall(VFS_PROC_NR, VFS_SOCKET, &m);
+}
+
int socket(int domain, int type, int protocol)
{
- int sock_type;
+ int r, sock_type;
+
+ r = __socket(domain, type, protocol);
+ if (r != -1 || errno != EAFNOSUPPORT)
+ return r;
sock_type = type & ~SOCK_FLAGS_MASK;
fprintf(stderr, "socket: domain %d, type %d, protocol %d\n",
domain, type, protocol);
#endif
- if (domain != AF_INET && domain != AF_UNIX)
- {
-#if DEBUG
- fprintf(stderr, "socket: bad domain %d\n", domain);
-#endif
- errno= EAFNOSUPPORT;
- return -1;
- }
- if (domain == AF_UNIX && (sock_type == SOCK_STREAM ||
- sock_type == SOCK_DGRAM ||
- sock_type == SOCK_SEQPACKET))
+ if (domain == AF_UNIX)
return _uds_socket(type, protocol);
- if (domain == AF_INET && sock_type == SOCK_STREAM)
- return _tcp_socket(type, protocol);
-
- if (domain == AF_INET && sock_type == SOCK_DGRAM)
- return _udp_socket(type, protocol);
-
- if (domain == AF_INET && sock_type == SOCK_RAW && protocol == IPPROTO_ICMP)
- return _raw_socket(type, protocol);
-
- if (domain == AF_INET && sock_type == SOCK_RAW && protocol == IPPROTO_UDP)
- return _raw_socket(type, protocol);
+ if (domain == AF_INET) {
+ switch (sock_type) {
+ case SOCK_STREAM:
+ return _tcp_socket(type, protocol);
+ case SOCK_DGRAM:
+ return _udp_socket(type, protocol);
+ case SOCK_RAW:
+ return _raw_socket(type, protocol);
+ default:
+ errno = EPROTOTYPE;
+ return -1;
+ }
+ }
-#if DEBUG
- fprintf(stderr, "socket: nothing for domain %d, type %d, protocol %d\n",
- domain, type, protocol);
-#endif
- errno= EPROTOTYPE;
+ errno = EAFNOSUPPORT;
return -1;
}
static int _uds_socket(int type, int protocol)
{
int fd, r, flags = O_RDWR, sock_type;
+
+ sock_type = type & ~SOCK_FLAGS_MASK;
+ if (sock_type != SOCK_STREAM &&
+ sock_type != SOCK_DGRAM &&
+ sock_type != SOCK_SEQPACKET) {
+ errno = EPROTOTYPE;
+ return -1;
+ }
+
if (protocol != 0)
{
#if DEBUG
/* set the type for the socket via ioctl (SOCK_DGRAM,
* SOCK_STREAM, SOCK_SEQPACKET, etc)
*/
- sock_type = type & ~SOCK_FLAGS_MASK;
r= ioctl(fd, NWIOSUDSTYPE, &sock_type);
if (r == -1) {
int ioctl_errno;
#include <sys/cdefs.h>
#include "namespace.h"
+#include <lib.h>
+#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
static int _uds_socketpair(int type, int protocol, int sv[2]);
/*
- * Create a pair of connected sockets
+ * Create a pair of connected sockets.
*/
-int socketpair(int domain, int type, int protocol, int sv[2]) {
+static int
+__socketpair(int domain, int type, int protocol, int sv[2])
+{
+ message m;
-#if DEBUG
- fprintf(stderr, "socketpair: domain %d, type %d, protocol %d\n",
- domain, type, protocol);
-#endif
+ memset(&m, 0, sizeof(m));
+ m.m_lc_vfs_socket.domain = domain;
+ m.m_lc_vfs_socket.type = type;
+ m.m_lc_vfs_socket.protocol = protocol;
- if (domain != AF_UNIX)
- {
- errno = EAFNOSUPPORT;
+ if (_syscall(VFS_PROC_NR, VFS_SOCKETPAIR, &m) < 0)
return -1;
- }
- if (domain == AF_UNIX &&
- (type == SOCK_STREAM || type == SOCK_SEQPACKET))
- return _uds_socketpair(type, protocol, sv);
+ sv[0] = m.m_vfs_lc_fdpair.fd0;
+ sv[1] = m.m_vfs_lc_fdpair.fd1;
+ return 0;
+}
+
+int
+socketpair(int domain, int type, int protocol, int sv[2])
+{
+ int r;
+
+ r = __socketpair(domain, type, protocol, sv);
+ if (r != -1 || errno != EAFNOSUPPORT)
+ return r;
#if DEBUG
- fprintf(stderr,
- "socketpair: nothing for domain %d, type %d, protocol %d\n",
+ fprintf(stderr, "socketpair: domain %d, type %d, protocol %d\n",
domain, type, protocol);
#endif
- errno= EPROTOTYPE;
+ if (domain == AF_UNIX)
+ return _uds_socketpair(type, protocol, sv);
+
+ errno = EAFNOSUPPORT;
return -1;
}
int r, i;
struct stat sbuf;
+ if (type != SOCK_STREAM && type != SOCK_SEQPACKET) {
+ errno = EPROTOTYPE;
+ return -1;
+ }
+
if (protocol != 0)
{
#if DEBUG
filedes.c stadir.c protect.c time.c \
lock.c misc.c utility.c select.c table.c \
vnode.c vmnt.c request.c \
- tll.c comm.c worker.c coredump.c
+ tll.c comm.c worker.c coredump.c \
+ socket.c
.if ${MKCOVERAGE} != "no"
SRCS+= gcov.c
struct timespec * modtv);
int req_newdriver(endpoint_t fs_e, dev_t dev, char *label);
+/* socket.c */
+int do_socket(void);
+int do_socketpair(void);
+int do_bind(void);
+int do_connect(void);
+int do_listen(void);
+int do_accept(void);
+void resume_accept(struct fproc *rfp, int status, dev_t dev,
+ unsigned int addr_len, int listen_fd);
+int do_sendto(void);
+int do_recvfrom(void);
+void resume_recvfrom(struct fproc *rfp, int status, unsigned int addr_len);
+int do_sockmsg(void);
+void resume_recvmsg(struct fproc *rfp, int status, unsigned int ctl_len,
+ unsigned int addr_len, int flags, vir_bytes msg_buf);
+int do_setsockopt(void);
+int do_getsockopt(void);
+int do_getsockname(void);
+int do_getpeername(void);
+int do_shutdown(void);
+
/* stadir.c */
int do_chdir(void);
int do_fchdir(void);
--- /dev/null
+/*
+ * IMPORTANT NOTICE: THIS FILE CONTAINS STUBS ONLY RIGHT NOW, TO ENABLE A
+ * SEAMLESS TRANSITION TO THE NEW API FOR PROGRAMS STATICALLY LINKED TO LIBC!
+ *
+ * This file implements the upper socket layer of VFS: the BSD socket system
+ * calls, and any associated file descriptor, file pointer, vnode, and file
+ * system processing. In most cases, this layer will call into the lower
+ * socket layer in order to send the request to a socket driver. Generic file
+ * calls (e.g., read, write, ioctl, and select) are not implemented here, and
+ * will directly call into the lower socket layer as well.
+ *
+ * The following table shows the system call numbers implemented in this file,
+ * along with their request and reply message types. Each request layout
+ * message type is prefixed with "m_lc_vfs_". Each reply layout message type
+ * is prefixed with "m_vfs_lc_". For requests without a specific reply layout,
+ * only the "m_type" message field is used in the reply message.
+ *
+ * Type Request layout Reply layout
+ * ---- -------------- ------------
+ * VFS_SOCKET socket
+ * VFS_SOCKETPAIR socket fdpair
+ * VFS_BIND sockaddr
+ * VFS_CONNECT sockaddr
+ * VFS_LISTEN listen
+ * VFS_ACCEPT sockaddr socklen
+ * VFS_SENDTO sendrecv
+ * VFS_RECVFROM sendrecv socklen
+ * VFS_SENDMSG sockmsg
+ * VFS_RECVMSG sockmsg
+ * VFS_SETSOCKOPT sockopt
+ * VFS_GETSOCKOPT sockopt socklen
+ * VFS_GETSOCKNAME sockaddr socklen
+ * VFS_GETPEERNAME sockaddr socklen
+ * VFS_SHUTDOWN shutdown
+ */
+
+#include "fs.h"
+
+#include <sys/socket.h>
+
+/*
+ * Create a socket.
+ */
+int
+do_socket(void)
+{
+
+ return EAFNOSUPPORT;
+}
+
+/*
+ * Create a pair of connected sockets.
+ */
+int
+do_socketpair(void)
+{
+
+ return EAFNOSUPPORT;
+}
+
+/*
+ * Bind a socket to a local address.
+ */
+int
+do_bind(void)
+{
+
+ return ENOTSOCK;
+}
+
+/*
+ * Connect a socket to a remote address.
+ */
+int
+do_connect(void)
+{
+
+ return ENOTSOCK;
+}
+
+/*
+ * Put a socket in listening mode.
+ */
+int
+do_listen(void)
+{
+
+ return ENOTSOCK;
+}
+
+/*
+ * Accept a connection on a listening socket, creating a new socket.
+ */
+int
+do_accept(void)
+{
+
+ return ENOTSOCK;
+}
+
+/*
+ * Send a message on a socket.
+ */
+int
+do_sendto(void)
+{
+
+ return ENOTSOCK;
+}
+
+/*
+ * Receive a message from a socket.
+ */
+int
+do_recvfrom(void)
+{
+
+ return ENOTSOCK;
+}
+
+/*
+ * Send or receive a message on a socket using a message structure.
+ */
+int
+do_sockmsg(void)
+{
+
+ return ENOTSOCK;
+}
+
+/*
+ * Set socket options.
+ */
+int
+do_setsockopt(void)
+{
+
+ return ENOTSOCK;
+}
+
+/*
+ * Get socket options.
+ */
+int
+do_getsockopt(void)
+{
+
+ return ENOTSOCK;
+}
+
+/*
+ * Get the local address of a socket.
+ */
+int
+do_getsockname(void)
+{
+
+ return ENOTSOCK;
+}
+
+/*
+ * Get the remote address of a socket.
+ */
+int
+do_getpeername(void)
+{
+
+ return ENOTSOCK;
+}
+
+/*
+ * Shut down socket send and receive operations.
+ */
+int
+do_shutdown(void)
+{
+
+ return ENOTSOCK;
+}
CALL(VFS_COPYFD) = do_copyfd, /* copyfd(2) */
CALL(VFS_CHECKPERMS) = do_checkperms, /* checkperms(2) */
CALL(VFS_GETSYSINFO) = do_getsysinfo, /* getsysinfo(2) */
+ CALL(VFS_SOCKET) = do_socket, /* socket(2) */
+ CALL(VFS_SOCKETPAIR) = do_socketpair, /* socketpair(2) */
+ CALL(VFS_BIND) = do_bind, /* bind(2) */
+ CALL(VFS_CONNECT) = do_connect, /* connect(2) */
+ CALL(VFS_LISTEN) = do_listen, /* listen(2) */
+ CALL(VFS_ACCEPT) = do_accept, /* accept(2) */
+ CALL(VFS_SENDTO) = do_sendto, /* sendto(2) */
+ CALL(VFS_SENDMSG) = do_sockmsg, /* sendmsg(2) */
+ CALL(VFS_RECVFROM) = do_recvfrom, /* recvfrom(2) */
+ CALL(VFS_RECVMSG) = do_sockmsg, /* recvmsg(2) */
+ CALL(VFS_SETSOCKOPT) = do_setsockopt, /* setsockopt(2) */
+ CALL(VFS_GETSOCKOPT) = do_getsockopt, /* getsockopt(2) */
+ CALL(VFS_GETSOCKNAME) = do_getsockname, /* getsockname(2) */
+ CALL(VFS_GETPEERNAME) = do_getpeername, /* getpeername(2) */
+ CALL(VFS_SHUTDOWN) = do_shutdown, /* shutdown(2) */
};
errno = 0;
rc = shutdown(0, how[i]);
- if (!(rc == -1 && errno == ENOSYS) && !info->bug_shutdown) {
- test_fail("shutdown() should have failed with ENOSYS");
+ if (!(rc == -1 && errno == ENOTSOCK) && !info->bug_shutdown) {
+ test_fail("shutdown() should have failed with "
+ "ENOTSOCK");
}
debug("test shutdown() with a socket that is not connected");
addr.sun_path[2] = 'o';
addr.sun_path[3] = '\0';
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
- rc = bind(sd, (struct sockaddr *) &addr, strlen(addr.sun_path) + 1);
+ rc = bind(sd, (struct sockaddr *) &addr,
+ offsetof(struct sockaddr_un, sun_path) + strlen(addr.sun_path) +
+ 1);
if (rc == -1) {
test_fail("bind() should have worked");
}
if (flags & PF_ALT)
put_endpoint(proc, "m_source", m.m_source);
- put_value(proc, "m_type", "%x", m.m_type);
+ put_value(proc, "m_type", "0x%x", m.m_type);
put_close_struct(proc, FALSE /*all*/);
}
#include <minix/endpoint.h>
#include <machine/stackframe.h>
+#include <netinet/in.h>
+
#include "proc.h"
#include "type.h"
#include "proto.h"
{
struct in_addr in;
- if (!valuesonly) {
- in.s_addr = ipaddr;
+ in.s_addr = ipaddr;
- /* Is this an acceptable encapsulation? */
- put_value(proc, name, "[%s]", inet_ntoa(in));
- } else
- put_value(proc, name, "0x%08x", ntohl(ipaddr));
+ put_in_addr(proc, name, in);
}
static void
FLAG(NWUO_DI_IPOPT),
};
-static void
-put_family(struct trace_proc * proc, const char * name, int family)
-{
- const char *text = NULL;
-
- if (!valuesonly) {
- /* TODO: add all the other protocols */
- switch (family) {
- TEXT(AF_UNSPEC);
- TEXT(AF_LOCAL);
- TEXT(AF_INET);
- TEXT(AF_INET6);
- }
- }
-
- if (text != NULL)
- put_field(proc, name, text);
- else
- put_value(proc, name, "%d", family);
-}
-
-static const struct flags sock_type[] = {
- FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_STREAM),
- FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_DGRAM),
- FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_RAW),
- FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_RDM),
- FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_SEQPACKET),
- FLAG(SOCK_CLOEXEC),
- FLAG(SOCK_NONBLOCK),
- FLAG(SOCK_NOSIGPIPE),
-};
-
-static void
-put_shutdown_how(struct trace_proc * proc, const char * name, int how)
-{
- const char *text = NULL;
-
- if (!valuesonly) {
- switch (how) {
- TEXT(SHUT_RD);
- TEXT(SHUT_WR);
- TEXT(SHUT_RDWR);
- }
- }
-
- if (text != NULL)
- put_field(proc, name, text);
- else
- put_value(proc, name, "%d", how);
-}
-
-static void
-put_struct_uucred(struct trace_proc * proc, const char * name, int flags,
- vir_bytes addr)
-{
- struct uucred cred;
-
- if (!put_open_struct(proc, name, flags, addr, &cred, sizeof(cred)))
- return;
-
- put_value(proc, "cr_uid", "%u", cred.cr_uid);
- if (verbose > 0) {
- put_value(proc, "cr_gid", "%u", cred.cr_gid);
- if (verbose > 1)
- put_value(proc, "cr_ngroups", "%d", cred.cr_ngroups);
- put_groups(proc, "cr_groups", PF_LOCADDR,
- (vir_bytes)&cred.cr_groups, cred.cr_ngroups);
- }
-
- put_close_struct(proc, verbose > 0);
-}
-
-static void
-put_cmsg_type(struct trace_proc * proc, const char * name, int type)
-{
- const char *text = NULL;
-
- if (!valuesonly) {
- switch (type) {
- TEXT(SCM_RIGHTS);
- TEXT(SCM_CREDS);
- TEXT(SCM_TIMESTAMP);
- }
- }
-
- if (text != NULL)
- put_field(proc, name, text);
- else
- put_value(proc, name, "%d", type);
-}
-
static void
put_msg_control(struct trace_proc * proc, struct msg_control * ptr)
{
if ((sun = (struct sockaddr_un *)ptr) == NULL)
return dir;
- put_family(proc, "sun_family", sun->sun_family);
+ put_socket_family(proc, "sun_family", sun->sun_family);
/* This could be extended to a generic sockaddr printer.. */
if (sun->sun_family == AF_LOCAL) {
if (ptr == NULL)
return dir;
- put_flags(proc, NULL, sock_type, COUNT(sock_type), "0x%x",
- *(int *)ptr);
+ put_socket_type(proc, NULL, *(int *)ptr);
return IF_ALL;
case NWIOSUDSSHUT:
extern unsigned int verbose;
extern unsigned int valuesonly;
-/* vfs.c */
-void put_fd(struct trace_proc *proc, const char *name, int fd);
-void put_dev(struct trace_proc *proc, const char *name, dev_t dev);
-
/* service */
const struct calls pm_calls;
const struct calls vfs_calls;
const struct calls vm_calls;
const struct calls ipc_calls;
+/* service/vfs.c */
+void put_fd(struct trace_proc *proc, const char *name, int fd);
+void put_dev(struct trace_proc *proc, const char *name, dev_t dev);
+void put_in_addr(struct trace_proc *proc, const char *name, struct in_addr in);
+void put_socket_type(struct trace_proc *proc, const char *name, int type);
+void put_socket_family(struct trace_proc *proc, const char *name, int family);
+void put_struct_uucred(struct trace_proc *proc, const char *name, int flags,
+ vir_bytes addr);
+void put_cmsg_type(struct trace_proc *proc, const char *name, int type);
+void put_shutdown_how(struct trace_proc *proc, const char *name, int how);
+
/* ioctl/block.c */
const char *block_ioctl_name(unsigned long req);
int block_ioctl_arg(struct trace_proc *proc, unsigned long req, void *ptr,
#include <dirent.h>
#include <sys/mount.h>
#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#if 0 /* not yet, header is missing */
+#include <netbt/bluetooth.h>
+#endif
+#include <arpa/inet.h>
/*
* This function should always be used when printing a file descriptor. It
}
static int
-vfs_read_out(struct trace_proc * proc, const message *m_out)
+vfs_read_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_readwrite.fd);
}
static void
-vfs_read_in(struct trace_proc * proc, const message *m_out,
- const message *m_in, int failed)
+vfs_read_in(struct trace_proc * proc, const message * m_out,
+ const message * m_in, int failed)
{
put_buf(proc, "buf", failed, m_out->m_lc_vfs_readwrite.buf,
}
static int
-vfs_write_out(struct trace_proc * proc, const message *m_out)
+vfs_write_out(struct trace_proc * proc, const message * m_out)
{
put_fd(proc, "fd", m_out->m_lc_vfs_readwrite.fd);
struct statvfs buf;
int i, max;
- if ((flags & PF_FAILED) || valuesonly || count < 0) {
+ if ((flags & PF_FAILED) || valuesonly > 1 || count < 0) {
put_ptr(proc, name, addr);
return;
return CT_DONE;
}
+void
+put_socket_family(struct trace_proc * proc, const char * name, int family)
+{
+ const char *text = NULL;
+
+ if (!valuesonly) {
+ /*
+ * For socket(2) and socketpair(2) this should really be using
+ * the prefix "PF_" since those functions take a protocol
+ * family rather than an address family. This rule is applied
+ * fairly consistently within the system. Here I caved because
+ * I don't want to duplicate this entire function just for the
+ * one letter. There are exceptions however; some names only
+ * exist as "PF_".
+ */
+ switch (family) {
+ TEXT(AF_UNSPEC);
+ TEXT(AF_LOCAL);
+ TEXT(AF_INET);
+ TEXT(AF_IMPLINK);
+ TEXT(AF_PUP);
+ TEXT(AF_CHAOS);
+ TEXT(AF_NS);
+ TEXT(AF_ISO);
+ TEXT(AF_ECMA);
+ TEXT(AF_DATAKIT);
+ TEXT(AF_CCITT);
+ TEXT(AF_SNA);
+ TEXT(AF_DECnet);
+ TEXT(AF_DLI);
+ TEXT(AF_LAT);
+ TEXT(AF_HYLINK);
+ TEXT(AF_APPLETALK);
+ TEXT(AF_OROUTE);
+ TEXT(AF_LINK);
+ TEXT(PF_XTP);
+ TEXT(AF_COIP);
+ TEXT(AF_CNT);
+ TEXT(PF_RTIP);
+ TEXT(AF_IPX);
+ TEXT(AF_INET6);
+ TEXT(PF_PIP);
+ TEXT(AF_ISDN);
+ TEXT(AF_NATM);
+ TEXT(AF_ARP);
+ TEXT(PF_KEY);
+ TEXT(AF_BLUETOOTH);
+ TEXT(AF_IEEE80211);
+ TEXT(AF_MPLS);
+ TEXT(AF_ROUTE);
+ }
+ }
+
+ if (text != NULL)
+ put_field(proc, name, text);
+ else
+ put_value(proc, name, "%d", family);
+}
+
+static const struct flags socket_types[] = {
+ FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_STREAM),
+ FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_DGRAM),
+ FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_RAW),
+ FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_RDM),
+ FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_SEQPACKET),
+ FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_CONN_DGRAM),
+ FLAG(SOCK_CLOEXEC),
+ FLAG(SOCK_NONBLOCK),
+ FLAG(SOCK_NOSIGPIPE),
+};
+
+void
+put_socket_type(struct trace_proc * proc, const char * name, int type)
+{
+
+ put_flags(proc, name, socket_types, COUNT(socket_types), "%d", type);
+}
+
+static void
+put_socket_protocol(struct trace_proc * proc, const char * name, int family,
+ int type, int protocol)
+{
+ const char *text = NULL;
+
+ if (!valuesonly && (type == SOCK_RAW || protocol != 0)) {
+ switch (family) {
+ case PF_INET:
+ case PF_INET6:
+ /* TODO: is this all that is used in socket(2)? */
+ switch (protocol) {
+ TEXT(IPPROTO_IP);
+ TEXT(IPPROTO_ICMP);
+ TEXT(IPPROTO_IGMP);
+ TEXT(IPPROTO_TCP);
+ TEXT(IPPROTO_UDP);
+ TEXT(IPPROTO_ICMPV6);
+ TEXT(IPPROTO_RAW);
+ }
+ break;
+#if 0 /* not yet */
+ case PF_BLUETOOTH:
+ switch (protocol) {
+ TEXT(BTPROTO_HCI);
+ TEXT(BTPROTO_L2CAP);
+ TEXT(BTPROTO_RFCOMM);
+ TEXT(BTPROTO_SCO);
+ }
+ break;
+#endif
+ }
+ }
+
+ if (text != NULL)
+ put_field(proc, name, text);
+ else
+ put_value(proc, name, "%d", protocol);
+}
+
+static int
+vfs_socket_out(struct trace_proc * proc, const message * m_out)
+{
+
+ put_socket_family(proc, "domain", m_out->m_lc_vfs_socket.domain);
+ put_socket_type(proc, "type", m_out->m_lc_vfs_socket.type);
+ put_socket_protocol(proc, "protocol", m_out->m_lc_vfs_socket.domain,
+ m_out->m_lc_vfs_socket.type & ~SOCK_FLAGS_MASK,
+ m_out->m_lc_vfs_socket.protocol);
+
+ return CT_DONE;
+}
+
+static int
+vfs_socketpair_out(struct trace_proc * proc, const message * m_out)
+{
+
+ put_socket_family(proc, "domain", m_out->m_lc_vfs_socket.domain);
+ put_socket_type(proc, "type", m_out->m_lc_vfs_socket.type);
+ put_socket_protocol(proc, "protocol", m_out->m_lc_vfs_socket.domain,
+ m_out->m_lc_vfs_socket.type & ~SOCK_FLAGS_MASK,
+ m_out->m_lc_vfs_socket.protocol);
+
+ return CT_NOTDONE;
+}
+
+static void
+vfs_socketpair_in(struct trace_proc * proc, const message * m_out,
+ const message * m_in, int failed)
+{
+
+ if (!failed) {
+ put_open(proc, "fd", PF_NONAME, "[", ", ");
+ put_fd(proc, "fd0", m_in->m_vfs_lc_fdpair.fd0);
+ put_fd(proc, "fd1", m_in->m_vfs_lc_fdpair.fd1);
+ put_close(proc, "]");
+ } else
+ put_field(proc, "fd", "&..");
+ put_equals(proc);
+ put_result(proc);
+}
+
+void
+put_in_addr(struct trace_proc * proc, const char * name, struct in_addr in)
+{
+
+ if (!valuesonly) {
+ /* Is this an acceptable encapsulation? */
+ put_value(proc, name, "[%s]", inet_ntoa(in));
+ } else
+ put_value(proc, name, "0x%08x", ntohl(in.s_addr));
+}
+
+static void
+put_in6_addr(struct trace_proc * proc, const char * name, struct in6_addr * in)
+{
+ char buf[INET6_ADDRSTRLEN];
+ const char *ptr;
+ unsigned int i, n;
+
+ if (!valuesonly &&
+ (ptr = inet_ntop(AF_INET6, in, buf, sizeof(buf))) != NULL) {
+ put_value(proc, name, "[%s]", ptr);
+ } else {
+ for (i = n = 0; i < 16; i++)
+ n += snprintf(buf + n, sizeof(buf) - n, "%02x",
+ ((unsigned char *)in)[i]);
+ put_value(proc, name, "0x%s", buf);
+ }
+}
+
+static void
+put_struct_sockaddr(struct trace_proc * proc, const char * name, int flags,
+ vir_bytes addr, socklen_t addr_len)
+{
+ char buf[UCHAR_MAX + 1];
+ uint8_t len;
+ sa_family_t family;
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ int all, off, left;
+
+ /*
+ * For UNIX domain sockets, make sure there's always room to add a
+ * trailing NULL byte, because UDS paths are not necessarily null
+ * terminated.
+ */
+ if (addr_len < offsetof(struct sockaddr, sa_data) ||
+ addr_len >= sizeof(buf)) {
+ put_ptr(proc, name, addr);
+
+ return;
+ }
+
+ if (!put_open_struct(proc, name, flags, addr, buf, addr_len))
+ return;
+
+ memcpy(&sa, buf, sizeof(sa));
+ len = sa.sa_len;
+ family = sa.sa_family;
+ all = (verbose > 1);
+
+ switch (family) {
+ case AF_LOCAL:
+ if (verbose > 1)
+ put_value(proc, "sun_len", "%u", len);
+ if (verbose > 0)
+ put_socket_family(proc, "sun_family", family);
+ off = (int)offsetof(struct sockaddr_un, sun_path);
+ left = addr_len - off;
+ if (left > 0) {
+ buf[addr_len] = 0; /* force null termination */
+ put_buf(proc, "sun_path", PF_LOCADDR | PF_PATH,
+ (vir_bytes)&buf[off],
+ left + 1 /* include null byte */);
+ }
+ break;
+ case AF_INET:
+ if (verbose > 1)
+ put_value(proc, "sin_len", "%u", len);
+ if (verbose > 0)
+ put_socket_family(proc, "sin_family", family);
+ if (addr_len == sizeof(sin)) {
+ memcpy(&sin, buf, sizeof(sin));
+ put_value(proc, "sin_port", "%u", ntohs(sin.sin_port));
+ put_in_addr(proc, "sin_addr", sin.sin_addr);
+ } else
+ all = FALSE;
+ break;
+ case AF_INET6:
+ if (verbose > 1)
+ put_value(proc, "sin6_len", "%u", len);
+ if (verbose > 0)
+ put_socket_family(proc, "sin6_family", family);
+ if (addr_len == sizeof(sin6)) {
+ memcpy(&sin6, buf, sizeof(sin6));
+ put_value(proc, "sin6_port", "%u",
+ ntohs(sin6.sin6_port));
+ if (verbose > 1)
+ put_value(proc, "sin6_flowinfo", "%"PRIu32,
+ sin6.sin6_flowinfo);
+ put_in6_addr(proc, "sin6_addr", &sin6.sin6_addr);
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) ||
+ IN6_IS_ADDR_SITELOCAL(&sin6.sin6_addr) ||
+ verbose > 0)
+ put_value(proc, "sin6_scope_id", "%"PRIu32,
+ sin6.sin6_scope_id);
+ } else
+ all = FALSE;
+ break;
+ /* TODO: support for other address families */
+ default:
+ if (verbose > 1)
+ put_value(proc, "sa_len", "%u", len);
+ put_socket_family(proc, "sa_family", family);
+ all = (verbose > 1 && family == AF_UNSPEC);
+ }
+
+ put_close_struct(proc, all);
+}
+
+/* This function is shared between bind and connect. */
+static int
+vfs_bind_out(struct trace_proc * proc, const message * m_out)
+{
+
+ put_fd(proc, "fd", m_out->m_lc_vfs_sockaddr.fd);
+ put_struct_sockaddr(proc, "addr", 0, m_out->m_lc_vfs_sockaddr.addr,
+ m_out->m_lc_vfs_sockaddr.addr_len);
+ put_value(proc, "addr_len", "%u", m_out->m_lc_vfs_sockaddr.addr_len);
+
+ return CT_DONE;
+}
+
+static int
+vfs_listen_out(struct trace_proc * proc, const message * m_out)
+{
+
+ put_fd(proc, "fd", m_out->m_lc_vfs_listen.fd);
+ put_value(proc, "backlog", "%d", m_out->m_lc_vfs_listen.backlog);
+
+ return CT_DONE;
+}
+
+static int
+vfs_accept_out(struct trace_proc * proc, const message * m_out)
+{
+
+ put_fd(proc, "fd", m_out->m_lc_vfs_sockaddr.fd);
+
+ return CT_NOTDONE;
+}
+
+static void
+vfs_accept_in(struct trace_proc * proc, const message * m_out,
+ const message * m_in, int failed)
+{
+
+ put_struct_sockaddr(proc, "addr", failed,
+ m_out->m_lc_vfs_sockaddr.addr, m_in->m_vfs_lc_socklen.len);
+ /*
+ * We print the resulting address length rather than the given buffer
+ * size here, as we do in recvfrom, getsockname, getpeername, and (less
+ * explicitly) recvmsg. We could also print both, by adding the
+ * resulting length after the call result.
+ */
+ if (m_out->m_lc_vfs_sockaddr.addr == 0)
+ put_field(proc, "addr_len", "NULL");
+ else if (!failed)
+ put_value(proc, "addr_len", "{%u}",
+ m_in->m_vfs_lc_socklen.len);
+ else
+ put_field(proc, "addr_len", "&..");
+
+ put_equals(proc);
+ put_result(proc);
+}
+
+static const struct flags msg_flags[] = {
+ FLAG(MSG_OOB),
+ FLAG(MSG_PEEK),
+ FLAG(MSG_DONTROUTE),
+ FLAG(MSG_EOR),
+ FLAG(MSG_TRUNC),
+ FLAG(MSG_CTRUNC),
+ FLAG(MSG_WAITALL),
+ FLAG(MSG_DONTWAIT),
+ FLAG(MSG_BCAST),
+ FLAG(MSG_MCAST),
+#ifdef MSG_NOSIGNAL
+ FLAG(MSG_NOSIGNAL),
+#endif
+ FLAG(MSG_CMSG_CLOEXEC),
+ FLAG(MSG_NBIO),
+ FLAG(MSG_WAITFORONE),
+};
+
+static int
+vfs_sendto_out(struct trace_proc * proc, const message * m_out)
+{
+
+ put_fd(proc, "fd", m_out->m_lc_vfs_sendrecv.fd);
+ put_buf(proc, "buf", 0, m_out->m_lc_vfs_sendrecv.buf,
+ m_out->m_lc_vfs_readwrite.len);
+ put_value(proc, "len", "%zu", m_out->m_lc_vfs_sendrecv.len);
+ put_flags(proc, "flags", msg_flags, COUNT(msg_flags), "0x%x",
+ m_out->m_lc_vfs_sendrecv.flags);
+ put_struct_sockaddr(proc, "addr", 0, m_out->m_lc_vfs_sendrecv.addr,
+ m_out->m_lc_vfs_sendrecv.addr_len);
+ put_value(proc, "addr_len", "%u", m_out->m_lc_vfs_sendrecv.addr_len);
+
+ return CT_DONE;
+}
+
+static void
+put_struct_iovec(struct trace_proc * proc, const char * name, int flags,
+ vir_bytes addr, int len, ssize_t bmax)
+{
+ struct iovec iov;
+ size_t bytes;
+ int i, imax;
+
+ /*
+ * For simplicity and clarity reasons, we currently print the I/O
+ * vector as an array of data elements rather than an array of
+ * structures. We also copy in each element separately, because as of
+ * writing there is no system support for more than one element anyway.
+ * All of this may be changed later.
+ */
+ if ((flags & PF_FAILED) || valuesonly > 1 || addr == 0 || len < 0) {
+ put_ptr(proc, name, addr);
+
+ return;
+ }
+
+ if (len == 0 || bmax == 0) {
+ put_field(proc, name, "[]");
+
+ return;
+ }
+
+ /* As per logic below, 'imax' must be set to a nonzero value here. */
+ if (verbose == 0)
+ imax = 4;
+ else if (verbose == 1)
+ imax = 16;
+ else
+ imax = INT_MAX;
+
+ for (i = 0; i < len && bmax > 0; i++) {
+ if (mem_get_data(proc->pid, addr, &iov, sizeof(iov)) < 0) {
+ if (i == 0) {
+ put_ptr(proc, name, addr);
+
+ return;
+ }
+
+ len = imax = 0; /* make put_tail() print an error */
+ break;
+ }
+
+ if (i == 0)
+ put_open(proc, name, 0, "[", ", ");
+
+ bytes = MIN(iov.iov_len, (size_t)bmax);
+
+ if (len < imax)
+ put_buf(proc, NULL, 0, (vir_bytes)iov.iov_base, bytes);
+
+ addr += sizeof(struct iovec);
+ bmax -= bytes;
+ }
+
+ if (imax == 0 || imax < len)
+ put_tail(proc, len, imax);
+ put_close(proc, "]");
+}
+
+void
+put_struct_uucred(struct trace_proc * proc, const char * name, int flags,
+ vir_bytes addr)
+{
+ struct uucred cred;
+
+ if (!put_open_struct(proc, name, flags, addr, &cred, sizeof(cred)))
+ return;
+
+ put_value(proc, "cr_uid", "%u", cred.cr_uid);
+ if (verbose > 0) {
+ put_value(proc, "cr_gid", "%u", cred.cr_gid);
+ if (verbose > 1)
+ put_value(proc, "cr_ngroups", "%d", cred.cr_ngroups);
+ put_groups(proc, "cr_groups", PF_LOCADDR,
+ (vir_bytes)&cred.cr_groups, cred.cr_ngroups);
+ }
+
+ put_close_struct(proc, verbose > 0);
+}
+
+static void
+put_socket_level(struct trace_proc * proc, const char * name, int level)
+{
+
+ /*
+ * Unfortunately, the level is a domain-specific protocol number. That
+ * means that without knowing how the socket was created, we cannot
+ * tell what it means. The only thing we can print is SOL_SOCKET,
+ * which is the same across all domains.
+ */
+ if (!valuesonly && level == SOL_SOCKET)
+ put_field(proc, name, "SOL_SOCKET");
+ else
+ put_value(proc, name, "%d", level);
+}
+
+void
+put_cmsg_type(struct trace_proc * proc, const char * name, int type)
+{
+ const char *text = NULL;
+
+ if (!valuesonly) {
+ switch (type) {
+ TEXT(SCM_RIGHTS);
+ TEXT(SCM_CREDS);
+ TEXT(SCM_TIMESTAMP);
+ }
+ }
+
+ if (text != NULL)
+ put_field(proc, name, text);
+ else
+ put_value(proc, name, "%d", type);
+}
+
+static void
+put_cmsg_rights(struct trace_proc * proc, const char * name, char * buf,
+ size_t size, char * cptr, size_t chunk, vir_bytes addr, size_t len)
+{
+ unsigned int i, nfds;
+ int *ptr;
+
+ put_open(proc, name, PF_NONAME, "[", ", ");
+
+ /*
+ * Since file descriptors are important, we print them all, regardless
+ * of the current verbosity level. Start with the file descriptors
+ * that are already copied into the local buffer.
+ */
+ ptr = (int *)cptr;
+ chunk = MIN(chunk, len);
+
+ nfds = chunk / sizeof(int);
+ for (i = 0; i < nfds; i++)
+ put_fd(proc, NULL, ptr[i]);
+
+ /* Then do the remaining file descriptors, in chunks. */
+ size -= size % sizeof(int);
+
+ for (len -= chunk; len >= sizeof(int); len -= chunk) {
+ chunk = MIN(len, size);
+
+ if (mem_get_data(proc->pid, addr, buf, chunk) < 0) {
+ put_field(proc, NULL, "..");
+
+ break;
+ }
+
+ ptr = (int *)buf;
+ nfds = chunk / sizeof(int);
+ for (i = 0; i < nfds; i++)
+ put_fd(proc, NULL, ptr[i]);
+
+ addr += chunk;
+ }
+
+ put_close(proc, "]");
+}
+
+static void
+put_cmsg(struct trace_proc * proc, const char * name, vir_bytes addr,
+ size_t len)
+{
+ struct cmsghdr cmsg;
+ char buf[CMSG_SPACE(sizeof(struct uucred))];
+ size_t off, chunk, datalen;
+
+ if (valuesonly > 1 || addr == 0 || len < CMSG_LEN(0)) {
+ put_ptr(proc, name, addr);
+
+ return;
+ }
+
+ for (off = 0; off < len; off += CMSG_SPACE(datalen)) {
+ chunk = MIN(len - off, sizeof(buf));
+
+ if (chunk < CMSG_LEN(0))
+ break;
+
+ if (mem_get_data(proc->pid, addr + off, buf, chunk) < 0) {
+ if (off == 0) {
+ put_ptr(proc, name, addr);
+
+ return;
+ }
+ break;
+ }
+
+ if (off == 0)
+ put_open(proc, name, 0, "[", ", ");
+
+ memcpy(&cmsg, buf, sizeof(cmsg));
+
+ put_open(proc, NULL, 0, "{", ", ");
+ if (verbose > 0)
+ put_value(proc, "cmsg_len", "%u", cmsg.cmsg_len);
+ put_socket_level(proc, "cmsg_level", cmsg.cmsg_level);
+ if (cmsg.cmsg_level == SOL_SOCKET)
+ put_cmsg_type(proc, "cmsg_type", cmsg.cmsg_type);
+ else
+ put_value(proc, "cmsg_type", "%d", cmsg.cmsg_type);
+
+ if (cmsg.cmsg_len < CMSG_LEN(0) || off + cmsg.cmsg_len > len) {
+ put_tail(proc, 0, 0);
+ put_close(proc, "}");
+ break;
+ }
+
+ datalen = cmsg.cmsg_len - CMSG_LEN(0);
+
+ if (cmsg.cmsg_level == SOL_SOCKET &&
+ cmsg.cmsg_type == SCM_RIGHTS) {
+ put_cmsg_rights(proc, "cmsg_data", buf, sizeof(buf),
+ &buf[CMSG_LEN(0)], chunk - CMSG_LEN(0),
+ addr + off + chunk, datalen);
+ } else if (cmsg.cmsg_level == SOL_SOCKET &&
+ cmsg.cmsg_type == SCM_CREDS &&
+ datalen >= sizeof(struct uucred) &&
+ chunk >= CMSG_LEN(datalen)) {
+ put_struct_uucred(proc, "cmsg_data", PF_LOCADDR,
+ (vir_bytes)&buf[CMSG_LEN(0)]);
+ } else if (datalen > 0)
+ put_field(proc, "cmsg_data", "..");
+
+ if (verbose == 0)
+ put_field(proc, NULL, "..");
+ put_close(proc, "}");
+ }
+
+ if (off < len)
+ put_field(proc, NULL, "..");
+ put_close(proc, "]");
+}
+
+static void
+put_struct_msghdr(struct trace_proc * proc, const char * name, int flags,
+ vir_bytes addr, ssize_t max)
+{
+ struct msghdr msg;
+ int all;
+
+ if (!put_open_struct(proc, name, flags, addr, &msg, sizeof(msg)))
+ return;
+
+ all = TRUE;
+
+ if (msg.msg_name != NULL || verbose > 1) {
+ put_struct_sockaddr(proc, "msg_name", 0,
+ (vir_bytes)msg.msg_name, msg.msg_namelen);
+ if (verbose > 0)
+ put_value(proc, "msg_namelen", "%u", msg.msg_namelen);
+ else
+ all = FALSE;
+ } else
+ all = FALSE;
+
+ put_struct_iovec(proc, "msg_iov", 0, (vir_bytes)msg.msg_iov,
+ msg.msg_iovlen, max);
+ if (verbose > 0)
+ put_value(proc, "msg_iovlen", "%d", msg.msg_iovlen);
+ else
+ all = FALSE;
+
+ if (msg.msg_control != NULL || verbose > 1) {
+ put_cmsg(proc, "msg_control", (vir_bytes)msg.msg_control,
+ msg.msg_controllen);
+
+ if (verbose > 0)
+ put_value(proc, "msg_controllen", "%u",
+ msg.msg_controllen);
+ else
+ all = FALSE;
+ } else
+ all = FALSE;
+
+ /* When receiving, print the flags field as well. */
+ if (flags & PF_ALT)
+ put_flags(proc, "msg_flags", msg_flags, COUNT(msg_flags),
+ "0x%x", msg.msg_flags);
+
+ put_close_struct(proc, all);
+}
+
+static int
+vfs_sendmsg_out(struct trace_proc * proc, const message * m_out)
+{
+
+ put_fd(proc, "fd", m_out->m_lc_vfs_sockmsg.fd);
+ put_struct_msghdr(proc, "msg", 0, m_out->m_lc_vfs_sockmsg.msgbuf,
+ SSIZE_MAX);
+ put_flags(proc, "flags", msg_flags, COUNT(msg_flags), "0x%x",
+ m_out->m_lc_vfs_sockmsg.flags);
+
+ return CT_DONE;
+}
+
+static int
+vfs_recvfrom_out(struct trace_proc * proc, const message * m_out)
+{
+
+ put_fd(proc, "fd", m_out->m_lc_vfs_sendrecv.fd);
+
+ return CT_NOTDONE;
+}
+
+static void
+vfs_recvfrom_in(struct trace_proc * proc, const message * m_out,
+ const message * m_in, int failed)
+{
+
+ put_buf(proc, "buf", failed, m_out->m_lc_vfs_sendrecv.buf,
+ m_in->m_type);
+ put_value(proc, "len", "%zu", m_out->m_lc_vfs_sendrecv.len);
+ put_flags(proc, "flags", msg_flags, COUNT(msg_flags), "0x%x",
+ m_out->m_lc_vfs_sendrecv.flags);
+ put_struct_sockaddr(proc, "addr", failed,
+ m_out->m_lc_vfs_sendrecv.addr, m_in->m_vfs_lc_socklen.len);
+ if (m_out->m_lc_vfs_sendrecv.addr == 0)
+ put_field(proc, "addr_len", "NULL");
+ else if (!failed)
+ put_value(proc, "addr_len", "{%u}",
+ m_in->m_vfs_lc_socklen.len);
+ else
+ put_field(proc, "addr_len", "&..");
+
+ put_equals(proc);
+ put_result(proc);
+}
+
+static int
+vfs_recvmsg_out(struct trace_proc * proc, const message * m_out)
+{
+
+ put_fd(proc, "fd", m_out->m_lc_vfs_sockmsg.fd);
+
+ return CT_NOTDONE;
+}
+
+static void
+vfs_recvmsg_in(struct trace_proc * proc, const message * m_out,
+ const message * m_in, int failed)
+{
+
+ /*
+ * We choose to print only the resulting structure in this case. Doing
+ * so is easier and less messy than printing both the original and the
+ * result for the fields that are updated by the system (msg_namelen
+ * and msg_controllen); also, this approach is stateless. Admittedly
+ * it is not entirely consistent with many other parts of the trace
+ * output, though.
+ */
+ put_struct_msghdr(proc, "msg", PF_ALT | failed,
+ m_out->m_lc_vfs_sockmsg.msgbuf, m_in->m_type);
+ put_flags(proc, "flags", msg_flags, COUNT(msg_flags), "0x%x",
+ m_out->m_lc_vfs_sockmsg.flags);
+
+ put_equals(proc);
+ put_result(proc);
+}
+
+static void
+put_sockopt_name(struct trace_proc * proc, const char * name, int level,
+ int optname)
+{
+ const char *text = NULL;
+
+ /*
+ * The only level for which we can know names is SOL_SOCKET. See also
+ * put_socket_level(). Of course we could guess, but then we need a
+ * proper guessing system, which should probably also take into account
+ * the [gs]etsockopt option length. TODO.
+ */
+ if (!valuesonly && level == SOL_SOCKET) {
+ switch (optname) {
+ TEXT(SO_DEBUG);
+ TEXT(SO_ACCEPTCONN);
+ TEXT(SO_REUSEADDR);
+ TEXT(SO_KEEPALIVE);
+ TEXT(SO_DONTROUTE);
+ TEXT(SO_BROADCAST);
+ TEXT(SO_USELOOPBACK);
+ TEXT(SO_LINGER);
+ TEXT(SO_OOBINLINE);
+ TEXT(SO_REUSEPORT);
+ TEXT(SO_NOSIGPIPE);
+ TEXT(SO_TIMESTAMP);
+ TEXT(SO_PASSCRED);
+ TEXT(SO_PEERCRED);
+ TEXT(SO_SNDBUF);
+ TEXT(SO_RCVBUF);
+ TEXT(SO_SNDLOWAT);
+ TEXT(SO_RCVLOWAT);
+ TEXT(SO_ERROR);
+ TEXT(SO_TYPE);
+ TEXT(SO_OVERFLOWED);
+ TEXT(SO_NOHEADER);
+ TEXT(SO_SNDTIMEO);
+ TEXT(SO_RCVTIMEO);
+ }
+ }
+
+ if (text != NULL)
+ put_field(proc, name, text);
+ else
+ put_value(proc, name, "0x%x", optname);
+}
+
+static void
+put_sockopt_data(struct trace_proc * proc, const char * name, int flags,
+ int level, int optname, vir_bytes addr, socklen_t len)
+{
+ const char *text;
+ int i;
+ struct linger l;
+ struct uucred cr;
+ struct timeval tv;
+ void *ptr;
+ size_t size;
+
+ /* See above regarding ambiguity for levels other than SOL_SOCKET. */
+ if ((flags & PF_FAILED) || valuesonly > 1 || len == 0 ||
+ level != SOL_SOCKET) {
+ put_ptr(proc, name, addr);
+
+ return;
+ }
+
+ /* Determine how much data to get, and where to put it. */
+ switch (optname) {
+ case SO_DEBUG:
+ case SO_ACCEPTCONN:
+ case SO_REUSEADDR:
+ case SO_KEEPALIVE:
+ case SO_DONTROUTE:
+ case SO_BROADCAST:
+ case SO_USELOOPBACK:
+ case SO_OOBINLINE:
+ case SO_REUSEPORT:
+ case SO_NOSIGPIPE:
+ case SO_TIMESTAMP:
+ case SO_PASSCRED:
+ case SO_SNDBUF:
+ case SO_RCVBUF:
+ case SO_SNDLOWAT:
+ case SO_RCVLOWAT:
+ case SO_ERROR:
+ case SO_TYPE:
+ case SO_OVERFLOWED:
+ case SO_NOHEADER:
+ ptr = &i;
+ size = sizeof(i);
+ break;
+ case SO_LINGER:
+ ptr = &l;
+ size = sizeof(l);
+ break;
+ case SO_PEERCRED:
+ ptr = &cr;
+ size = sizeof(cr);
+ break;
+ case SO_SNDTIMEO:
+ case SO_RCVTIMEO:
+ ptr = &tv;
+ size = sizeof(tv);
+ break;
+ default:
+ put_ptr(proc, name, addr);
+ return;
+ }
+
+ /* Get the data. Do not bother with truncated values. */
+ if (len < size || mem_get_data(proc->pid, addr, ptr, size) < 0) {
+ put_ptr(proc, name, addr);
+
+ return;
+ }
+
+ /* Print the data according to the option name. */
+ switch (optname) {
+ case SO_LINGER:
+ /* This isn't going to appear anywhere else; do it inline. */
+ put_open(proc, name, 0, "{", ", ");
+ put_value(proc, "l_onoff", "%d", l.l_onoff);
+ put_value(proc, "l_linger", "%d", l.l_linger);
+ put_close(proc, "}");
+ break;
+ case SO_PEERCRED:
+ put_struct_uucred(proc, name, PF_LOCADDR, (vir_bytes)&cr);
+ break;
+ case SO_ERROR:
+ put_open(proc, name, 0, "{", ", ");
+ if (!valuesonly && (text = get_error_name(i)) != NULL)
+ put_field(proc, NULL, text);
+ else
+ put_value(proc, NULL, "%d", i);
+ put_close(proc, "}");
+ break;
+ case SO_TYPE:
+ put_open(proc, name, 0, "{", ", ");
+ put_socket_type(proc, NULL, i);
+ put_close(proc, "}");
+ break;
+ case SO_SNDTIMEO:
+ case SO_RCVTIMEO:
+ put_struct_timeval(proc, name, PF_LOCADDR, (vir_bytes)&tv);
+ break;
+ default:
+ /* All other options are integer values. */
+ put_value(proc, name, "{%d}", i);
+ }
+}
+
+static int
+vfs_setsockopt_out(struct trace_proc * proc, const message * m_out)
+{
+ int level, name;
+
+ level = m_out->m_lc_vfs_sockopt.level;
+ name = m_out->m_lc_vfs_sockopt.name;
+
+ put_fd(proc, "fd", m_out->m_lc_vfs_sockopt.fd);
+ put_socket_level(proc, "level", level);
+ put_sockopt_name(proc, "name", level, name);
+ put_sockopt_data(proc, "buf", 0, level, name,
+ m_out->m_lc_vfs_sockopt.buf, m_out->m_lc_vfs_sockopt.len);
+ put_value(proc, "len", "%u", m_out->m_lc_vfs_sockopt.len);
+
+ return CT_DONE;
+}
+
+static int
+vfs_getsockopt_out(struct trace_proc * proc, const message * m_out)
+{
+ int level;
+
+ level = m_out->m_lc_vfs_sockopt.level;
+
+ put_fd(proc, "fd", m_out->m_lc_vfs_sockopt.fd);
+ put_socket_level(proc, "level", level);
+ put_sockopt_name(proc, "name", level, m_out->m_lc_vfs_sockopt.name);
+
+ return CT_NOTDONE;
+}
+
+static void
+vfs_getsockopt_in(struct trace_proc * proc, const message * m_out,
+ const message * m_in, int failed)
+{
+
+ put_sockopt_data(proc, "buf", failed, m_out->m_lc_vfs_sockopt.level,
+ m_out->m_lc_vfs_sockopt.name, m_out->m_lc_vfs_sockopt.buf,
+ m_in->m_vfs_lc_socklen.len);
+ /*
+ * For the length, we follow the same scheme as for addr_len pointers
+ * in accept() et al., in that we print the result only. We need not
+ * take into account that the given buffer is NULL as it must not be.
+ */
+ if (!failed)
+ put_value(proc, "len", "%u", m_out->m_lc_vfs_sockopt.len);
+ else
+ put_field(proc, "len", "&..");
+
+ put_equals(proc);
+ put_result(proc);
+}
+
+/* This function is shared between getsockname and getpeername. */
+static int
+vfs_getsockname_out(struct trace_proc * proc, const message * m_out)
+{
+
+ put_fd(proc, "fd", m_out->m_lc_vfs_sockaddr.fd);
+
+ return CT_NOTDONE;
+}
+
+static void
+vfs_getsockname_in(struct trace_proc * proc, const message * m_out,
+ const message * m_in, int failed)
+{
+
+ put_struct_sockaddr(proc, "addr", failed,
+ m_out->m_lc_vfs_sockaddr.addr, m_in->m_vfs_lc_socklen.len);
+ if (m_out->m_lc_vfs_sockaddr.addr == 0)
+ put_field(proc, "addr_len", "NULL");
+ else if (!failed)
+ put_value(proc, "addr_len", "{%u}",
+ m_in->m_vfs_lc_socklen.len);
+ else
+ put_field(proc, "addr_len", "&..");
+
+ put_equals(proc);
+ put_result(proc);
+}
+
+void
+put_shutdown_how(struct trace_proc * proc, const char * name, int how)
+{
+ const char *text = NULL;
+
+ if (!valuesonly) {
+ switch (how) {
+ TEXT(SHUT_RD);
+ TEXT(SHUT_WR);
+ TEXT(SHUT_RDWR);
+ }
+ }
+
+ if (text != NULL)
+ put_field(proc, name, text);
+ else
+ put_value(proc, name, "%d", how);
+}
+
+static int
+vfs_shutdown_out(struct trace_proc * proc, const message * m_out)
+{
+
+ put_fd(proc, "fd", m_out->m_lc_vfs_shutdown.fd);
+ put_shutdown_how(proc, "how", m_out->m_lc_vfs_shutdown.how);
+
+ return CT_DONE;
+}
+
#define VFS_CALL(c) [((VFS_ ## c) - VFS_BASE)]
static const struct call_handler vfs_map[] = {
vfs_svrctl_in),
VFS_CALL(GCOV_FLUSH) = HANDLER("gcov_flush", vfs_gcov_flush_out,
default_in),
+ VFS_CALL(SOCKET) = HANDLER("socket", vfs_socket_out, default_in),
+ VFS_CALL(SOCKETPAIR) = HANDLER("socketpair", vfs_socketpair_out,
+ vfs_socketpair_in),
+ VFS_CALL(BIND) = HANDLER("bind", vfs_bind_out, default_in),
+ VFS_CALL(CONNECT) = HANDLER("connect", vfs_bind_out, default_in),
+ VFS_CALL(LISTEN) = HANDLER("listen", vfs_listen_out, default_in),
+ VFS_CALL(ACCEPT) = HANDLER("accept", vfs_accept_out, vfs_accept_in),
+ VFS_CALL(SENDTO) = HANDLER("sendto", vfs_sendto_out, default_in),
+ VFS_CALL(SENDMSG) = HANDLER("sendmsg", vfs_sendmsg_out, default_in),
+ VFS_CALL(RECVFROM) = HANDLER("recvfrom", vfs_recvfrom_out,
+ vfs_recvfrom_in),
+ VFS_CALL(RECVMSG) = HANDLER("recvmsg", vfs_recvmsg_out,
+ vfs_recvmsg_in),
+ VFS_CALL(SETSOCKOPT) = HANDLER("setsockopt", vfs_setsockopt_out,
+ default_in),
+ VFS_CALL(GETSOCKOPT) = HANDLER("getsockopt", vfs_getsockopt_out,
+ vfs_getsockopt_in),
+ VFS_CALL(GETSOCKNAME) = HANDLER("getsockname", vfs_getsockname_out,
+ vfs_getsockname_in),
+ VFS_CALL(GETPEERNAME) = HANDLER("getpeername", vfs_getsockname_out,
+ vfs_getsockname_in),
+ VFS_CALL(SHUTDOWN) = HANDLER("shutdown", vfs_shutdown_out, default_in),
};
const struct calls vfs_calls = {