terom@154: #include "transport_fd.h" terom@117: terom@155: #include "log.h" terom@155: terom@117: #include terom@117: #include terom@117: #include terom@117: terom@154: /** terom@154: * Our libevent callback terom@154: */ terom@159: static void transport_fd_on_event (evutil_socket_t _fd, short ev_what, void *arg) terom@154: { terom@154: struct transport_fd *fd = arg; terom@117: terom@154: (void) _fd; terom@154: terom@159: short what = 0; terom@159: terom@159: // build flags terom@159: if (ev_what & EV_READ) terom@159: what |= TRANSPORT_READ; terom@159: terom@159: if (ev_what & EV_WRITE) terom@159: what |= TRANSPORT_WRITE; terom@159: terom@154: // invoke user callback terom@154: fd->cb_func(fd, what, fd->cb_arg); terom@117: } terom@117: terom@154: /** terom@154: * Our transport_methods implementations terom@154: */ terom@176: err_t transport_fd__read (transport_t *transport, void *buf, size_t *len, error_t *err) terom@117: { terom@154: struct transport_fd *fd = transport_check(transport, &transport_fd_type); terom@117: int ret; terom@159: terom@159: RESET_ERROR(err); terom@117: terom@117: // read(), and detect non-EAGAIN or EOF terom@154: if ((ret = read(fd->fd, buf, *len)) < 0 && errno != EAGAIN) terom@117: // unexpected error terom@118: RETURN_SET_ERROR_ERRNO(err, ERR_READ); terom@117: terom@117: else if (ret == 0) terom@117: // EOF terom@155: return SET_ERROR(err, ERR_EOF); terom@117: terom@117: terom@117: if (ret < 0) { terom@117: // EAGAIN -> zero bytes terom@117: *len = 0; terom@117: terom@117: } else { terom@117: // normal -> bytes read terom@117: *len = ret; terom@117: } terom@117: terom@117: // ok terom@117: return SUCCESS; terom@117: } terom@117: terom@176: err_t transport_fd__write (transport_t *transport, const void *buf, size_t *len, struct error_info *err) terom@117: { terom@155: struct transport_fd *fd = transport_check(transport, &transport_fd_type); terom@117: int ret; terom@117: terom@159: RESET_ERROR(err); terom@159: terom@117: // write(), and detect non-EAGAIN or EOF terom@154: if ((ret = write(fd->fd, buf, *len)) < 0 && errno != EAGAIN) terom@117: // unexpected error terom@118: RETURN_SET_ERROR_ERRNO(err, ERR_WRITE); terom@117: terom@117: else if (ret == 0) terom@117: // EOF terom@118: return SET_ERROR(err, ERR_WRITE_EOF); terom@117: terom@117: terom@117: if (ret < 0) { terom@117: // EAGAIN -> zero bytes terom@117: *len = 0; terom@117: terom@156: if (transport->info.ev_mask & TRANSPORT_WRITE) terom@156: // enable the write event terom@156: if ((ERROR_CODE(err) = transport_fd_enable(fd, TRANSPORT_WRITE))) terom@156: return ERROR_CODE(err); terom@156: terom@117: } else { terom@117: // normal -> bytes read terom@117: *len = ret; terom@117: } terom@117: terom@117: return SUCCESS; terom@117: } terom@117: terom@176: err_t transport_fd__events (transport_t *transport, short ev_mask, error_t *err) terom@156: { terom@156: struct transport_fd *fd = transport_check(transport, &transport_fd_type); terom@156: terom@159: short mask = 0; terom@156: terom@156: // enable read as requested terom@159: if (ev_mask & TRANSPORT_READ) terom@159: mask |= TRANSPORT_READ; terom@156: terom@156: // enable write if requested and it's currently enabled terom@159: if ((ev_mask & TRANSPORT_WRITE) && event_pending(fd->ev_write, EV_WRITE, NULL)) terom@159: mask |= TRANSPORT_WRITE; terom@156: terom@156: // set terom@156: return (ERROR_CODE(err) = transport_fd_events(fd, mask)); terom@156: } terom@156: terom@176: void transport_fd__deinit (transport_t *transport) terom@117: { terom@155: struct transport_fd *fd = transport_check(transport, &transport_fd_type); terom@155: terom@176: transport_fd_deinit(fd); terom@117: } terom@117: terom@159: const struct transport_type transport_fd_type = { terom@176: .base_type = { terom@182: .parent = &transport_type_type, terom@176: }, terom@159: .methods = { terom@176: .read = transport_fd__read, terom@176: .write = transport_fd__write, terom@176: .events = transport_fd__events, terom@176: .deinit = transport_fd__deinit terom@159: } terom@154: }; terom@154: terom@154: /** terom@156: * Dummy callbacks terom@156: */ terom@156: void transport_fd_callback_user (struct transport_fd *fd, short what, void *arg) terom@156: { terom@156: (void) arg; terom@156: terom@156: // proxy terom@156: transport_invoke(TRANSPORT_FD_BASE(fd), what); terom@156: } terom@156: terom@156: /** terom@154: * Function implementations terom@154: */ terom@154: void transport_fd_init (struct transport_fd *fd, struct event_base *ev_base, int _fd) terom@117: { terom@154: // sanity-check terom@154: assert(!fd->fd); terom@154: assert(!fd->ev_read && !fd->ev_write); terom@155: assert(_fd == TRANSPORT_FD_INVALID || _fd >= 0); terom@117: terom@117: // initialize terom@154: fd->ev_base = ev_base; terom@154: fd->fd = _fd; terom@154: fd->cb_func = fd->cb_arg = NULL; terom@117: } terom@117: terom@154: err_t transport_fd_nonblock (struct transport_fd *fd, bool nonblock) terom@117: { terom@155: assert(fd->fd != TRANSPORT_FD_INVALID); terom@155: terom@154: // XXX: maintain old flags? terom@118: terom@117: terom@154: // set new flags terom@154: if (fcntl(fd->fd, F_SETFL, nonblock ? O_NONBLOCK : 0) < 0) terom@154: return ERR_FCNTL; terom@117: terom@117: return SUCCESS; terom@117: } terom@117: terom@154: /** terom@154: * Install our internal event handler. terom@154: * terom@154: * The events should not already be set up. terom@154: * terom@154: * Cleans up partial events on errors terom@154: */ terom@154: err_t transport_fd_install (struct transport_fd *fd) terom@117: { terom@155: assert(fd->fd != TRANSPORT_FD_INVALID); terom@154: assert(!fd->ev_read && !fd->ev_write); terom@154: terom@154: // create new events terom@156: if ((fd->ev_read = event_new(fd->ev_base, fd->fd, EV_READ | EV_PERSIST, transport_fd_on_event, fd)) == NULL) terom@154: goto err_event_add; terom@154: terom@154: if ((fd->ev_write = event_new(fd->ev_base, fd->fd, EV_WRITE, transport_fd_on_event, fd)) == NULL) terom@154: goto err_event_add; terom@117: terom@154: // ok terom@154: return SUCCESS; terom@154: terom@154: err_event_add: terom@154: // remove partial events terom@154: transport_fd_clear(fd); terom@154: terom@154: return ERR_EVENT_NEW; terom@154: } terom@154: terom@154: err_t transport_fd_setup (struct transport_fd *fd, transport_fd_callback_func cb_func, void *cb_arg) terom@154: { terom@154: // requires a valid fd terom@155: assert(fd->fd != TRANSPORT_FD_INVALID); terom@154: terom@154: // store terom@155: fd->cb_func = cb_func; terom@155: fd->cb_arg = cb_arg; terom@154: terom@154: // install the event handlers? terom@154: if (!fd->ev_read || !fd->ev_write) terom@156: return transport_fd_install(fd); terom@156: else terom@156: return SUCCESS; terom@156: } terom@156: terom@156: err_t transport_fd_enable (struct transport_fd *fd, short mask) terom@156: { terom@156: // just add the appropriate events terom@156: if (mask & TRANSPORT_READ && event_add(fd->ev_read, NULL)) terom@156: return ERR_EVENT_ADD; terom@156: terom@156: if (mask & TRANSPORT_WRITE && event_add(fd->ev_write, NULL)) terom@156: return ERR_EVENT_ADD; terom@156: terom@156: terom@156: return SUCCESS; terom@156: } terom@156: terom@156: err_t transport_fd_disable (struct transport_fd *fd, short mask) terom@156: { terom@156: if (mask & TRANSPORT_READ && event_del(fd->ev_read)) terom@156: return ERR_EVENT_DEL; terom@156: terom@156: if (mask & TRANSPORT_WRITE && event_del(fd->ev_write)) terom@156: return ERR_EVENT_DEL; terom@154: terom@154: terom@117: return SUCCESS; terom@117: } terom@117: terom@156: err_t transport_fd_events (struct transport_fd *fd, short mask) terom@117: { terom@156: err_t err; terom@154: terom@156: // enable/disable read terom@156: if (mask & TRANSPORT_READ) terom@156: err = event_add(fd->ev_read, NULL); terom@156: else terom@156: err = event_del(fd->ev_read); terom@117: terom@156: if (err) terom@156: return err; terom@156: terom@156: // enable/disable write terom@156: if (mask & TRANSPORT_WRITE) terom@156: err = event_add(fd->ev_write, NULL); terom@156: else terom@156: err = event_del(fd->ev_write); terom@156: terom@156: if (err) terom@156: return err; terom@156: terom@156: // ok terom@154: return SUCCESS; terom@117: } terom@117: terom@154: /** terom@154: * Remove our current ev_* events, but leave the cb_* intact. terom@154: */ terom@154: static void transport_fd_remove (struct transport_fd *fd) terom@118: { terom@154: if (fd->ev_read) terom@154: event_free(fd->ev_read); terom@154: terom@155: if (fd->ev_write) terom@155: event_free(fd->ev_write); terom@154: terom@154: fd->ev_read = NULL; terom@154: fd->ev_write = NULL; terom@118: } terom@118: terom@155: void transport_fd_clear (struct transport_fd *fd) terom@118: { terom@154: // remove the events terom@154: transport_fd_remove(fd); terom@118: terom@154: // clear the callbacks terom@154: fd->cb_func = fd->cb_arg = NULL; terom@154: } terom@118: terom@157: err_t transport_fd_defaults (struct transport_fd *fd) terom@157: { terom@157: error_t err; terom@157: terom@157: // install the transport_invoke callback handler terom@157: if ((ERROR_CODE(&err) = transport_fd_setup(fd, transport_fd_callback_user, NULL))) terom@157: goto error; terom@157: terom@157: // enable read unless masked out terom@157: if (TRANSPORT_FD_BASE(fd)->info.ev_mask & TRANSPORT_READ) { terom@157: if ((ERROR_CODE(&err) = transport_fd_enable(fd, TRANSPORT_READ))) terom@157: goto error; terom@157: } terom@157: terom@157: // ok terom@157: return SUCCESS; terom@157: terom@157: error: terom@157: return ERROR_CODE(&err); terom@157: } terom@157: terom@154: err_t transport_fd_set (struct transport_fd *fd, int _fd) terom@154: { terom@155: assert(_fd == TRANSPORT_FD_INVALID || _fd >= 0); terom@155: terom@154: // close the old stuff terom@154: transport_fd_close(fd); terom@154: terom@118: // set the new one terom@155: fd->fd = _fd; terom@118: terom@154: // do we have callbacks that we need to setup? terom@155: if (fd->cb_func) terom@154: return transport_fd_install(fd); terom@154: else terom@154: return SUCCESS; terom@154: } terom@118: terom@155: void transport_fd_invoke (struct transport_fd *fd, short what) terom@155: { terom@155: // invoke terom@156: transport_invoke(TRANSPORT_FD_BASE(fd), what); terom@155: } terom@155: terom@154: err_t transport_fd_close (struct transport_fd *fd) terom@154: { terom@155: int _fd = fd->fd; terom@155: terom@154: // remove any installed events terom@154: transport_fd_remove(fd); terom@154: terom@155: // invalidate fd terom@155: fd->fd = TRANSPORT_FD_INVALID; terom@155: terom@155: // close the fd terom@155: if (_fd != TRANSPORT_FD_INVALID && close(_fd)) terom@154: return ERR_CLOSE; terom@154: terom@154: terom@118: return SUCCESS; terom@118: } terom@118: terom@176: void transport_fd_deinit (struct transport_fd *fd) terom@155: { terom@155: err_t tmp; terom@155: terom@155: // XXX: this might block terom@155: if ((tmp = transport_fd_close(fd))) terom@176: log_warn_err(tmp, "close"); terom@155: terom@155: } terom@155: