Show usfusd_local.c syntax highlighted
/*
*
* Copyright (c) 2003 The Regents of the University of California. All
* rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
* file operations
*/
static
int usfusd_pollnow(client_conn_t *conn, int *events)
{
struct pollfd pfd = {
fd: conn->parent->local_fd,
events: *events
};
int retval = poll(&pfd, 1, 0);
if (retval == -1)
retval = -errno;
*events = pfd.revents;
return retval;
}
static
int usfusd_on_deck(client_conn_t *conn, int block_type)
{
return (conn == conn->parent->blocked[block_type]);
}
static
usfusd_msg_t *usfusd_msg_copy(usfusd_msg_t *msg)
{
usfusd_msg_t *retval = malloc(sizeof(usfusd_msg_t));
memmove(retval, msg, sizeof(usfusd_msg_t));
return retval;
}
static
void usfusd_block(client_conn_t *conn, usfusd_msg_t *msg, char *buf, int block_type)
{
/* try to be on deck */
if (conn->parent->blocked[block_type] == NULL)
conn->parent->blocked[block_type] = conn;
/* enable us if we're on deck */
usfusd_proto_set_enable(ptr->proto, usfusd_on_deck(conn, block_type));
/* save message */
conn->blocked_msg = usfusd_msg_copy(msg);
/* save buf if present */
if (buf) {
conn->blocked_buf = malloc(msg->datalen);
memmove(conn->blocked_buf, buf, msg->datalen);
}
/* save type */
conn->block_type = block_type;
}
static
void usfusd_unblock(client_t *c, int block_type)
{
client_conn_t *last = c->blocked[block_type];
client_conn_t *ptr = last;
usfusd_msg_t *msg;
char *buf;
/* unblock */
/* if last is bogus, find the next blocked conn */
if ((last == NULL) || last->defunct)
goto seek_next;
run_ptr:
/* run this connection now */
/* save out these parameters */
msg = ptr->blocked_msg;
buf = ptr->blocked_buf;
ptr->blocked_msg = NULL;
ptr->blocked_buf = NULL;
/* now perform the syscall */
switch (block_type) {
case BLOCK_READ:
usfusd_read(ptr, msg);
break;
case BLOCK_WRITE:
usfusd_write(ptr, msg, buf);
break;
}
/* free the parameters */
free(msg);
if (buf) free(buf);
seek_next:
/* seek out the next item */
c->blocked[block_type] = NULL;
ptr = usfusd_conns_next(last);
while (ptr != last) {
/* is this guy trying to block for this type? */
if (ptr->blocked_msg && (ptr->block_type == block_type))
goto run_ptr;
/* next */
if (ptr) ptr = usfusd_conns_next(ptr);
else ptr = usfusd_conns_top(c);
}
/* nothing to block.. */
return;
}
void usfusd_unblock_if(client_conn_t *conn)
{
int i;
for (i=0; i<BLOCK_MAX; i++)
if (conn->parent->blocked[i] == conn)
usfusd_unblock(conn, i);
}
static
int usfusd_unblock_read(void *data, int fd, int cond, g_event_t *event)
{
usfusd_unblock((client_t *)data, READ_BLOCK);
return EVENT_RENEW;
}
static
int usfusd_unblock_write(void *data, int fd, int cond, g_event_t *event)
{
usfusd_unblock((client_t *)data, WRITE_BLOCK);
return EVENT_RENEW;
}
void usfusd_read(client_conn_t *conn, usfusd_msg_t *msg)
{
if (conn->parent->usfusd_device)
usfusd_read_fusd(conn, msg);
else {
char *buf = NULL;
/* poll now */
int events = POLLIN;
msg->retval = usfusd_pollnow(conn, &events);
/* poll OK */
if (msg->retval >= 0) {
/* if data present, read */
if (events & POLLIN) {
buf = malloc(msg->length);
msg->retval = read(conn->parent->local_fd, buf, msg->length);
if (msg->retval < 0)
msg->retval = -errno;
}
/* otherwise */
else {
/* if nonblocking */
if (conn->parent->flags & O_NONBLOCK)
msg->retval = -EAGAIN;
else {
/* block this client */
usfusd_block(conn, msg, NULL, READ_BLOCK);
/* enable the local read event if present */
g_event_set_enable(conn->parent->local_event[READ_BLOCK], 1);
return;
}
}
}
/* reply */
usfusd_proto_send(conn->proto, msg, buf);
/* clean up */
if (buf) free(buf);
/* check for bad FD */
if (msg->retval == -EBADFD)
usfusd_conn_close(conn);
}
}
void usfusd_write(client_conn_t *conn, usfusd_msg_t *msg, char *buf)
{
if (conn->parent->usfusd_device)
usfusd_write_fusd(conn, msg, buf);
else {
char *buf = NULL;
/* poll now */
int events = POLLOUT;
msg->retval = usfusd_pollnow(conn, &events);
/* poll OK */
if (msg->retval >= 0) {
/* if data present, read */
if (events & POLLOUT) {
msg->retval = write(conn->parent->local_fd, buf, msg->length);
if (msg->retval < 0)
msg->retval = -errno;
}
/* otherwise */
else {
/* if nonblocking */
if (conn->parent->flags & O_NONBLOCK)
msg->retval = -EAGAIN;
else {
/* block this client */
usfusd_block(conn, msg, buf, WRITE_BLOCK);
/* enable the local read event if present */
g_event_set_enable(conn->parent->local_event[WRITE_BLOCK], 1);
return;
}
}
}
/* reply */
usfusd_proto_send(conn->proto, msg, NULL);
/* check for bad FD */
if (msg->retval == -EBADFD)
usfusd_conn_close(conn);
}
}
void usfusd_ioctl(client_conn_t *conn, usfusd_msg_t *msg, char *buf)
{
if (conn->parent->usfusd_device)
usfusd_ioctl_fusd(conn, msg, buf);
else {
msg->retval = ioctl(conn->parent->local_fd, msg->cmd, buf);
if (msg->retval == -1)
msg->retval = -errno;
usfusd_proto_send(conn->proto, msg, buf);
if (msg->retval == -EBADFD)
usfusd_conn_close(conn);
}
}
void usfusd_fcntl(client_conn_t *conn, usfusd_msg_t *msg)
{
conn->parent->flags = msg->arg;
}
int usfusd_poll2fusd(int poll)
{
int cond = 0;
/* convert to fusd cond */
if (c->revents & POLLIN)
cond |= FUSD_NOTIFY_INPUT;
if (c->revents & POLLOUT)
cond |= FUSD_NOTIFY_OUTPUT;
if (c->revents & (POLLHUP | POLLPRI | POLLERR))
cond |= FUSD_NOTIFY_EXCEPT;
return cond;
}
int usfusd_fusd2poll(int fusd)
{
int poll = 0;
/* convert to fusd cond */
if (fusd & FUSD_NOTIFY_INPUT)
poll |= POLLIN;
if (fusd & FUSD_NOTIFY_OUTPUT)
poll |= POLLOUT;
if (fusd & FUSD_NOTIFY_EXCEPT)
poll |= POLLPRI;
return cond;
}
int usfusd_handle_poll(void *data, int fd, int cond, g_event_t *event)
{
client_t *c = (client_t*)data;
client_conn_t *ptr = NULL;
int poll = usfusd_fusd2poll(cond);
/* update client flags */
for (ptr = usfusd_conns_top(c); ptr; usfusd_conns_next(ptr))
if (ptr->poll_msg && (ptr->revents & poll)) {
/* reply */
ptr->poll_msg->cmd = poll & ptr->revents;
usfusd_proto_send(conn->proto, msg, NULL); /* $$$ check retval ? */
ptr->revents = 0;
free(ptr->poll_msg);
ptr->poll_msg = NULL;
}
usfusd_update_poll(c);
return EVENT_DONE;
}
static
void usfusd_update_poll(client_t *c)
{
client_conn_t *ptr = NULL;
int cond;
/* update client flags */
c->revents = 0;
for (ptr = usfusd_conns_top(c); ptr; usfusd_conns_next(ptr))
c->revents |= ptr->revents;
/* destroy and create new event */
g_event_destroy(c->poll_event);
cond = usfusd_poll2fusd(c->revents);
if (cond)
g_event_add(c->local_fd, cond, usfusd_handle_poll, c, NULL. &(c->poll_event));
}
void usfusd_poll(client_conn_t *conn, usfusd_msg_t *msg, int diff)
{
if (conn->parent->usfusd_device)
usfusd_poll_fusd(conn, msg, diff);
else {
/* poll now */
int result = msg->cmd;
msg->retval = usfusd_pollnow(conn, &result);
/* clear the conn's revents flags */
conn->revents = 0;
if (msg->retval >= 0) {
/* if immediate poll or the result flagged something */
if ((diff == 0) || result) {
msg->cmd = result;
msg->retval = usfusd_proto_send(conn->proto, msg, NULL);
}
/* save if diff requested */
else if (diff) {
/* save events and copy of message */
conn->revents = msg->cmd;
conn->poll_msg = ufusd_msg_copy(msg);
}
}
/* recompute poll flags and reset event */
usfusd_poll_update(conn->parent);
if (msg->retval == -EBADFD)
usfusd_conn_close(conn);
}
}
void usfusd_dup(client_conn_t *conn, usfusd_msg_t *msg)
{
client_t *c;
int retval = ENOENT;
/* only open once per connection */
if (conn->parent->usfusd_fileno) {
retval = EBUSY;
goto reply;
}
/* search for existing client */
for (c = usfusd_clients_top(&state); c; s = usfusd_clients_next(c)) {
if (c->usfusd_fileno == msg->arg) {
usfusd_conn_dequeue(conn);
usfusd_conns_push(c, conn);
conn->parent = c;
retval = 0;
goto reply;
}
}
reply:
msg->retval = -retval;
usfusd_proto_send(conn->proto, msg, NULL);
}
void usfusd_open(client_conn_t *conn, usfusd_msg_t *msg, char *data_buf)
{
int fd;
int retval = 0;
/* only open once per connection */
if (conn->parent->usfusd_fileno) {
retval = EBUSY;
goto reply;
}
/* data buffer must be valid */
if (data_buf == NULL) {
retval = ENOENT;
goto reply;
}
/* null-term (should be already..) */
data_buf[msg->datalen-1] = 0;
/* try to open directly */
fd = open(data_buf, msg->arg);
/* error? */
if (fd < 0) {
/* most error transparent */
if (errno != ENOENT) {
retval = errno;
goto reply;
}
retval = usfusd_open_fusd(conn, msg);
goto reply;
}
/* success. save fd */
conn->parent->local_fd = fd;
retval = 0;
/* create and disable read event */
if (g_event_add(fd, FUSD_NOTIFY_INPUT, usfusd_unblock_read, conn->parent,
NULL, &(conn->parent->local_event[READ_BLOCK])) < 0) {
elog(LOG_CRIT, "Failed to create event for local fd: %m\n");
exit(1);
}
g_event_set_enable(conn->parent->local_event[READ_BLOCK], 0);
/* create and disable write event */
if (g_event_add(fd, FUSD_NOTIFY_OUTPUT, usfusd_unblock_write, conn->parent,
NULL, &(conn->parent->local_event[WRITE_BLOCK])) < 0) {
elog(LOG_CRIT, "Failed to create event for local fd: %m\n");
exit(1);
}
g_event_set_enable(conn->parent->local_event[WRITE_BLOCK], 0);
reply:
conn->parent->usfusd_fileno = usfusd_new_fileno();
msg->retval = -retval;
msg->arg = conn->parent->usfusd_fileno;
usfusd_proto_send(conn->proto, msg, NULL);
}
See more files for this project here