/* Copyright 2010 Nathan Phillip Brink This file is a part of DistRen. DistRen is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. DistRen is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with DistRen. If not, see . */ #include "common/config.h" #include "common/multiio.h" #include #include #include #include #include #include struct multiio_socket_info { /* the type of socket */ multiio_socket_type_t socket_type; /* to be passed to the socket handler */ void *socket_data; }; struct multiio_socket_type_info { /* type: struct multiio_socket_type_handler_info */ list_t type_handlers; }; /* The basic concept is that pollfds is an array. Thus, we have to have another array reflecting that array to hold socket-specific information. */ struct multiio_context { /* the array passed to poll() */ struct pollfd *pollfds; /* the number of entries pollfds could hold */ nfds_t pollfds_len; /* the number of entries that pollfds does hold */ nfds_t nfds; /* an array whose order mirrors pollfds containing information about the fd mentioned in pollfds. */ struct multiio_socket_info *socket_infos; /* the number of socket types registered (equivilent to the type ID of the next registered type) */ multiio_socket_type_t num_socket_types; /* the information about each individual socket type keyed by the type ID */ struct multiio_socket_type_info *socket_types; }; /* stores information about a handler associated with a socket type/class. */ struct multiio_socket_type_handler_info { /* the poll() event to which this handler responds */ short event; /* the handler function to call when the event is matched */ multiio_event_handler_func_t handler; /* the pointer passed to multiio_event_handler_register() */ void *handler_data; }; /** Finds the index in the context->pollfds array of a particular socket. @param context the multiio context @param fd the socket to search for @param index a pointer which will be filled with the index of the socket if found @return 0 if the pollfds entry is found, 1 if the entry is not found */ int multiio_pollfd_index_by_fd(const multiio_context_t context, int fd, size_t *index); multiio_context_t multiio_context_new() { struct multiio_context *context; context = malloc(sizeof(struct multiio_context)); if(!context) return NULL; context->pollfds = malloc(sizeof(struct pollfd) * 2); if(!context->pollfds) { free(context); return NULL; } context->pollfds_len = 2; context->nfds = 0; context->socket_infos = malloc(sizeof(struct multiio_socket_info) * 2); context->num_socket_types = 0; context->socket_types = NULL; if(!context->pollfds || !context->socket_infos) { free(context->pollfds); free(context->socket_infos); free(context); return NULL; } return context; } int multiio_context_free(multiio_context_t context) { size_t counter; /* clean up pollfds and socket_infos */ free(context->pollfds); free(context->socket_infos); /* wipe up the socket_types */ for(counter = 0; counter < context->num_socket_types; counter ++) list_free(context->socket_types[counter].type_handlers, LIST_DEALLOC); free(context->socket_types); /* bye! */ free(context); return 0; } struct multiio_poll_travinfo { multiio_context_t multiio; struct multiio_socket_info *socket_info; struct pollfd *pollfd; }; /** list traverser for multiio_poll() */ int multiio_poll_invoke_handlers(struct multiio_poll_travinfo *travinfo, struct multiio_socket_type_handler_info *handler_info) { short event; event = travinfo->pollfd->revents & handler_info->event; if(!event) return TRUE; handler_info->handler(travinfo->multiio, travinfo->pollfd->fd, event, handler_info->handler_data, travinfo->socket_info->socket_data); return TRUE; } int multiio_poll(multiio_context_t context, int timeout) { size_t counter; struct multiio_poll_travinfo travinfo; int ret; ret = poll(context->pollfds, context->nfds, timeout); if(ret == -1) { perror("poll"); } for(counter = 0; counter < context->nfds; counter ++) if(context->pollfds[counter].revents) { travinfo.multiio = context; travinfo.socket_info = &context->socket_infos[counter]; travinfo.pollfd = &context->pollfds[counter]; list_traverse(context->socket_types[context->socket_infos[counter].socket_type].type_handlers, &travinfo, (list_traverse_func_t)&multiio_poll_invoke_handlers, LIST_FRNT | LIST_SAVE); context->pollfds[counter].revents = 0; } return 0; } int multiio_socket_type_register(multiio_context_t context, multiio_socket_type_t *new_type) { struct multiio_socket_type_info *new_socket_types; new_socket_types = malloc(sizeof(struct multiio_socket_type_info) * (context->num_socket_types + 1)); if(!new_socket_types) return 1; *new_type = context->num_socket_types; new_socket_types[*new_type].type_handlers = list_init(); if(!new_socket_types[*new_type].type_handlers) { free(new_socket_types); return 2; } if(context->num_socket_types) { memcpy(new_socket_types, context->socket_types, sizeof(struct multiio_socket_type_info) * context->num_socket_types); free(context->socket_types); } context->socket_types = new_socket_types; context->num_socket_types ++; return 0; } int multiio_event_handler_register(multiio_context_t context, multiio_socket_type_t socket_type, short event, multiio_event_handler_func_t handler_func, void *handler_data) { struct multiio_socket_type_handler_info handler_info; if(socket_type >= context->num_socket_types) return 1; if(!event) return 2; handler_info.event = event; handler_info.handler = handler_func; handler_info.handler_data = handler_data; list_insert_after(context->socket_types[socket_type].type_handlers, &handler_info, sizeof(struct multiio_socket_type_handler_info)); return 0; } int multiio_socket_add(multiio_context_t context, int fd, multiio_socket_type_t socket_type, void *socket_data, short events) { struct pollfd *pollfds; struct multiio_socket_info *socket_infos; size_t new_pollfds_len; int fdstatus; int tmp; fdstatus = fcntl(fd, F_GETFL); fdstatus |= O_NONBLOCK; tmp = fcntl(fd, F_SETFL, fdstatus); if(tmp == -1) { perror("fcntl"); return 1; } if(socket_type >= context->num_socket_types) return 1; /** extend sockfds array */ if(context->nfds >= context->pollfds_len) { new_pollfds_len = (context->pollfds_len + 1) * 2; pollfds = malloc(sizeof(struct pollfd) * new_pollfds_len); socket_infos = malloc(sizeof(struct multiio_socket_info) * new_pollfds_len); if(!pollfds || !socket_infos) return 1; memcpy(pollfds, context->pollfds, sizeof(struct pollfd) * context->pollfds_len); memcpy(socket_infos, context->socket_infos, sizeof(struct multiio_socket_info) * context->pollfds_len); free(context->pollfds); free(context->socket_infos); context->pollfds = pollfds; context->socket_infos = socket_infos; context->pollfds_len = new_pollfds_len; } pollfds = context->pollfds; socket_infos = context->socket_infos; memset(&pollfds[context->nfds], 0, sizeof(struct pollfd)); pollfds[context->nfds].fd = fd; pollfds[context->nfds].events = events; socket_infos[context->nfds].socket_type = socket_type; socket_infos[context->nfds].socket_data = socket_data; context->nfds ++; return 0; } int multiio_socket_del(multiio_context_t context, int fd) { size_t index; int tmp; tmp = multiio_pollfd_index_by_fd(context, fd, &index); if(tmp) return 1; /** * Special case: one left, if we tried filling in holes... segfault ;-) */ if(context->nfds == 1) { context->nfds --; return 0; } /** * For now we'll unconditionally fill in holes. * In fact, it looks simple enough that this method * may be what we always use ;-). */ memcpy(&context->pollfds[index], &context->pollfds[context->nfds - 1], sizeof(struct pollfd)); memcpy(&context->socket_infos[index], &context->socket_infos[context->nfds - 1], sizeof(struct multiio_socket_info)); context->nfds --; return 0; } int multiio_socket_event_enable(multiio_context_t context, int fd, short event) { size_t index; int tmp; tmp = multiio_pollfd_index_by_fd(context, fd, &index); if(tmp) return 1; context->pollfds[index].events |= event; return 0; } int multiio_socket_event_disable(multiio_context_t context, int fd, short event) { size_t index; int tmp; tmp = multiio_pollfd_index_by_fd(context, fd, &index); if(tmp) return 1; context->pollfds[index].events &= ~event; return 0; } int multiio_pollfd_index_by_fd(const multiio_context_t context, int fd, size_t *index) { size_t counter; for(counter = 0; counter < context->nfds; counter ++) if(context->pollfds[counter].fd == fd) { *index = counter; return 0; } return 1; }