263 lines
5.5 KiB
C
263 lines
5.5 KiB
C
|
/*
|
||
|
* CORE
|
||
|
* Copyright (c)2010-2012 the Boeing Company.
|
||
|
* See the LICENSE file included in this distribution.
|
||
|
*
|
||
|
* author: Tom Goff <thomas.goff@boeing.com>
|
||
|
*
|
||
|
* vnode_msg.c
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <errno.h>
|
||
|
#include <assert.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <arpa/inet.h>
|
||
|
|
||
|
#include "myerr.h"
|
||
|
|
||
|
#include "vnode_msg.h"
|
||
|
|
||
|
|
||
|
static void vnode_msg_cb(struct ev_loop *loop, ev_io *w, int revents)
|
||
|
{
|
||
|
vnode_msgio_t *msgio = w->data;
|
||
|
ssize_t tmp;
|
||
|
vnode_msghandler_t msghandlefn;
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
WARNX("new message on fd %d", msgio->fd);
|
||
|
#endif
|
||
|
|
||
|
assert(msgio);
|
||
|
|
||
|
tmp = vnode_recvmsg(msgio);
|
||
|
if (tmp == 0)
|
||
|
return;
|
||
|
else if (tmp < 0)
|
||
|
{
|
||
|
ev_io_stop(loop, w);
|
||
|
if (msgio->ioerror)
|
||
|
msgio->ioerror(msgio);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
msghandlefn = msgio->msghandler[msgio->msgbuf.msg->hdr.type];
|
||
|
if (!msghandlefn)
|
||
|
{
|
||
|
WARNX("no handler found for msg type %u from fd %d",
|
||
|
msgio->msgbuf.msg->hdr.type, msgio->fd);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
msghandlefn(msgio);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ssize_t vnode_sendmsg(int fd, vnode_msgbuf_t *msgbuf)
|
||
|
{
|
||
|
struct msghdr msg = {};
|
||
|
struct iovec iov[1];
|
||
|
char buf[CMSG_SPACE(3 * sizeof(int))];
|
||
|
|
||
|
iov[0].iov_base = msgbuf->msg;
|
||
|
iov[0].iov_len = vnode_msglen(msgbuf);
|
||
|
msg.msg_iov = iov;
|
||
|
msg.msg_iovlen = 1;
|
||
|
|
||
|
if (msgbuf->infd >= 0)
|
||
|
{
|
||
|
struct cmsghdr *cmsg;
|
||
|
int *fdptr;
|
||
|
|
||
|
assert(msgbuf->outfd >= 0);
|
||
|
assert(msgbuf->errfd >= 0);
|
||
|
|
||
|
msg.msg_control = buf;
|
||
|
msg.msg_controllen = sizeof(buf);
|
||
|
|
||
|
cmsg = CMSG_FIRSTHDR(&msg);
|
||
|
cmsg->cmsg_level = SOL_SOCKET;
|
||
|
cmsg->cmsg_type = SCM_RIGHTS;
|
||
|
cmsg->cmsg_len = CMSG_LEN(3 * sizeof(int));
|
||
|
|
||
|
fdptr = (int *)CMSG_DATA(cmsg);
|
||
|
fdptr[0] = msgbuf->infd;
|
||
|
fdptr[1] = msgbuf->outfd;
|
||
|
fdptr[2] = msgbuf->errfd;
|
||
|
|
||
|
msg.msg_controllen = cmsg->cmsg_len;
|
||
|
}
|
||
|
|
||
|
return sendmsg(fd, &msg, 0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* return the number of bytes received
|
||
|
* return 0 if the message should be ignored
|
||
|
* return a negative value if i/o should stop
|
||
|
*/
|
||
|
ssize_t vnode_recvmsg(vnode_msgio_t *msgio)
|
||
|
{
|
||
|
ssize_t recvlen;
|
||
|
struct msghdr msg = {};
|
||
|
struct iovec iov[1];
|
||
|
char buf[CMSG_SPACE(3 * sizeof(int))];
|
||
|
struct cmsghdr *cmsg;
|
||
|
|
||
|
if (msgio->msgbuf.msgbufsize < VNODE_MSGSIZMAX)
|
||
|
{
|
||
|
if (vnode_resizemsgbuf(&msgio->msgbuf, VNODE_MSGSIZMAX))
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
msgio->msgbuf.infd = msgio->msgbuf.outfd = msgio->msgbuf.errfd = -1;
|
||
|
|
||
|
iov[0].iov_base = msgio->msgbuf.msg;
|
||
|
iov[0].iov_len = msgio->msgbuf.msgbufsize;
|
||
|
msg.msg_iov = iov;
|
||
|
msg.msg_iovlen = 1;
|
||
|
msg.msg_control = buf;
|
||
|
msg.msg_controllen = sizeof(buf);
|
||
|
|
||
|
recvlen = recvmsg(msgio->fd, &msg, 0);
|
||
|
if (recvlen == 0)
|
||
|
return -1;
|
||
|
else if (recvlen < 0)
|
||
|
{
|
||
|
if (errno == EAGAIN)
|
||
|
return 0;
|
||
|
WARN("recvmsg() failed");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
cmsg = CMSG_FIRSTHDR(&msg);
|
||
|
if (cmsg != NULL && cmsg->cmsg_type == SCM_RIGHTS)
|
||
|
{
|
||
|
int *fdptr;
|
||
|
|
||
|
fdptr = (int *)CMSG_DATA(cmsg);
|
||
|
msgio->msgbuf.infd = fdptr[0];
|
||
|
msgio->msgbuf.outfd = fdptr[1];
|
||
|
msgio->msgbuf.errfd = fdptr[2];
|
||
|
}
|
||
|
|
||
|
if (recvlen < sizeof(msgio->msgbuf.msg->hdr))
|
||
|
{
|
||
|
WARNX("message header truncated: received %d of %d bytes",
|
||
|
recvlen, sizeof(msgio->msgbuf.msg->hdr));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (msgio->msgbuf.msg->hdr.type == VNODE_MSG_NONE ||
|
||
|
msgio->msgbuf.msg->hdr.type >= VNODE_MSG_MAX)
|
||
|
{
|
||
|
WARNX("invalid message type: %u", msgio->msgbuf.msg->hdr.type);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (recvlen - sizeof(msgio->msgbuf.msg->hdr) !=
|
||
|
msgio->msgbuf.msg->hdr.datalen)
|
||
|
{
|
||
|
WARNX("message length mismatch: received %d bytes; expected %d bytes",
|
||
|
recvlen - sizeof(msgio->msgbuf.msg->hdr),
|
||
|
msgio->msgbuf.msg->hdr.datalen);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return recvlen;
|
||
|
}
|
||
|
|
||
|
int vnode_msgiostart(vnode_msgio_t *msgio, struct ev_loop *loop,
|
||
|
int fd, void *data, vnode_msghandler_t ioerror,
|
||
|
const vnode_msghandler_t msghandler[VNODE_MSG_MAX])
|
||
|
{
|
||
|
#ifdef DEBUG
|
||
|
WARNX("starting message i/o for fd %d", fd);
|
||
|
#endif
|
||
|
|
||
|
if (vnode_initmsgbuf(&msgio->msgbuf))
|
||
|
return -1;
|
||
|
|
||
|
msgio->loop = loop;
|
||
|
msgio->fd = fd;
|
||
|
msgio->fdwatcher.data = msgio;
|
||
|
ev_io_init(&msgio->fdwatcher, vnode_msg_cb, fd, EV_READ);
|
||
|
msgio->data = data;
|
||
|
msgio->ioerror = ioerror;
|
||
|
memcpy(msgio->msghandler, msghandler, sizeof(msgio->msghandler));
|
||
|
|
||
|
ev_io_start(msgio->loop, &msgio->fdwatcher);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void vnode_msgiostop(vnode_msgio_t *msgio)
|
||
|
{
|
||
|
ev_io_stop(msgio->loop, &msgio->fdwatcher);
|
||
|
FREE_MSGBUF(&msgio->msgbuf);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int vnode_parsemsg(vnode_msg_t *msg, void *data,
|
||
|
const vnode_tlvhandler_t tlvhandler[VNODE_TLV_MAX])
|
||
|
{
|
||
|
size_t offset = 0;
|
||
|
vnode_tlv_t *tlv;
|
||
|
vnode_tlvhandler_t tlvhandlefn;
|
||
|
int tmp = -1;
|
||
|
|
||
|
while (offset < msg->hdr.datalen)
|
||
|
{
|
||
|
tlv = (void *)msg->data + offset;
|
||
|
|
||
|
offset += sizeof(*tlv) + tlv->vallen;
|
||
|
|
||
|
if (tlv->vallen == 0 || offset > msg->hdr.datalen)
|
||
|
{
|
||
|
WARNX("invalid value length: %u", tlv->vallen);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ((tlvhandlefn = tlvhandler[tlv->type]) == NULL)
|
||
|
{
|
||
|
WARNX("unknown tlv type: %u", tlv->type);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ((tmp = tlvhandlefn(tlv, data)))
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return tmp;
|
||
|
}
|
||
|
|
||
|
ssize_t vnode_addtlv(vnode_msgbuf_t *msgbuf, size_t offset,
|
||
|
uint32_t type, uint32_t vallen, const void *valp)
|
||
|
{
|
||
|
vnode_tlv_t *tlv;
|
||
|
size_t msglen, tlvlen;
|
||
|
|
||
|
tlv = (void *)msgbuf->msg->data + offset;
|
||
|
msglen = (void *)tlv - (void *)msgbuf->msg;
|
||
|
tlvlen = sizeof(*tlv) + vallen;
|
||
|
|
||
|
if (msglen + tlvlen > msgbuf->msgbufsize)
|
||
|
{
|
||
|
if (vnode_resizemsgbuf(msgbuf, msgbuf->msgbufsize + tlvlen))
|
||
|
return -1;
|
||
|
else
|
||
|
tlv = (void *)msgbuf->msg->data + offset;
|
||
|
}
|
||
|
|
||
|
tlv->type = type;
|
||
|
tlv->vallen = vallen;
|
||
|
memcpy(tlv->val, valp, vallen);
|
||
|
|
||
|
return tlvlen;
|
||
|
}
|