X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=lib%2Fasync_req%2Fasync_sock.c;h=9b2a625bbe86a3c4ac2268202ffe3e866d20b3b3;hb=c4b18bd860bc18529249a8c54c7db6aba2347591;hp=46ad29d053473ce952b19dd7800b4f6ac1e17bef;hpb=b8cafc5ad21fa999ce52facf605b3c315edde322;p=ira%2Fwip.git diff --git a/lib/async_req/async_sock.c b/lib/async_req/async_sock.c index 46ad29d0534..9b2a625bbe8 100644 --- a/lib/async_req/async_sock.c +++ b/lib/async_req/async_sock.c @@ -3,610 +3,193 @@ async socket syscalls Copyright (C) Volker Lendecke 2008 - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. + ** NOTE! The following LGPL license applies to the async_sock + ** library. This does NOT imply that all of Samba is released + ** under the LGPL - This program is distributed in the hope that it will be useful, + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 General Public License for more details. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ -#include "includes.h" -#include "lib/talloc/talloc.h" -#include "lib/tevent/tevent.h" -#include "lib/async_req/async_req.h" +#include "replace.h" +#include "system/network.h" +#include "system/filesys.h" +#include +#include #include "lib/async_req/async_sock.h" -#include + +/* Note: lib/util/ is currently GPL */ +#include "lib/util/tevent_unix.h" +#include "lib/util/util.h" #ifndef TALLOC_FREE #define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx=NULL; } while(0) #endif -/** - * Discriminator for async_syscall_state - */ -enum async_syscall_type { - ASYNC_SYSCALL_SEND, - ASYNC_SYSCALL_SENDALL, - ASYNC_SYSCALL_RECV, - ASYNC_SYSCALL_RECVALL, - ASYNC_SYSCALL_CONNECT -}; - -/** - * Holder for syscall arguments and the result - */ - -struct async_syscall_state { - enum async_syscall_type syscall_type; - struct tevent_fd *fde; - - union { - struct param_send { - int fd; - const void *buffer; - size_t length; - int flags; - } param_send; - struct param_sendall { - int fd; - const void *buffer; - size_t length; - int flags; - size_t sent; - } param_sendall; - struct param_recv { - int fd; - void *buffer; - size_t length; - int flags; - } param_recv; - struct param_recvall { - int fd; - void *buffer; - size_t length; - int flags; - size_t received; - } param_recvall; - struct param_connect { - /** - * connect needs to be done on a nonblocking - * socket. Keep the old flags around - */ - long old_sockflags; - int fd; - const struct sockaddr *address; - socklen_t address_len; - } param_connect; - } param; - - union { - ssize_t result_ssize_t; - size_t result_size_t; - int result_int; - } result; - int sys_errno; +struct sendto_state { + int fd; + const void *buf; + size_t len; + int flags; + const struct sockaddr *addr; + socklen_t addr_len; + ssize_t sent; }; -/** - * @brief Map async_req states to unix-style errnos - * @param[in] req The async req to get the state from - * @param[out] err Pointer to take the unix-style errno - * - * @return true if the async_req is in an error state, false otherwise - */ - -bool async_req_is_errno(struct async_req *req, int *err) -{ - enum async_req_state state; - uint64_t error; - - if (!async_req_is_error(req, &state, &error)) { - return false; - } - - switch (state) { - case ASYNC_REQ_USER_ERROR: - *err = (int)error; - break; - case ASYNC_REQ_TIMED_OUT: -#ifdef HAVE_ETIMEDOUT - *err = ETIMEDOUT; -#else - *err = EIO; -#endif - break; - case ASYNC_REQ_NO_MEMORY: - *err = ENOMEM; - break; - default: - *err = EIO; - break; - } - return true; -} - -int async_req_simple_recv_errno(struct async_req *req) -{ - int err; - - if (async_req_is_errno(req, &err)) { - return err; - } - - return 0; -} - -/** - * @brief Create a new async syscall req - * @param[in] mem_ctx The memory context to hang the result off - * @param[in] ev The event context to work from - * @param[in] type Which syscall will this be - * @param[in] pstate Where to put the newly created private_data state - * @retval The new request - * - * This is a helper function to prepare a new struct async_req with an - * associated struct async_syscall_state. The async_syscall_state will be put - * into the async_req as private_data. - */ - -static struct async_req *async_syscall_new(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - enum async_syscall_type type, - struct async_syscall_state **pstate) -{ - struct async_req *result; - struct async_syscall_state *state; - - if (!async_req_setup(mem_ctx, &result, &state, - struct async_syscall_state)) { - return NULL; - } - state->syscall_type = type; - - result->private_data = state; - - *pstate = state; - - return result; -} - -/** - * @brief Create a new async syscall req based on a fd - * @param[in] mem_ctx The memory context to hang the result off - * @param[in] ev The event context to work from - * @param[in] type Which syscall will this be - * @param[in] fd The file descriptor we work on - * @param[in] fde_flags TEVENT_FD_READ/WRITE -- what are we interested in? - * @param[in] fde_cb The callback function for the file descriptor event - * @param[in] pstate Where to put the newly created private_data state - * @retval The new request - * - * This is a helper function to prepare a new struct async_req with an - * associated struct async_syscall_state and an associated file descriptor - * event. - */ +static void sendto_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *private_data); -static struct async_req *async_fde_syscall_new( - TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - enum async_syscall_type type, - int fd, - uint16_t fde_flags, - void (*fde_cb)(struct tevent_context *ev, - struct tevent_fd *fde, uint16_t flags, - void *priv), - struct async_syscall_state **pstate) +struct tevent_req *sendto_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + int fd, const void *buf, size_t len, int flags, + const struct sockaddr *addr, + socklen_t addr_len) { - struct async_req *result; - struct async_syscall_state *state; + struct tevent_req *result; + struct sendto_state *state; + struct tevent_fd *fde; - result = async_syscall_new(mem_ctx, ev, type, &state); + result = tevent_req_create(mem_ctx, &state, struct sendto_state); if (result == NULL) { - return NULL; + return result; } - - state->fde = tevent_add_fd(ev, state, fd, fde_flags, fde_cb, result); - if (state->fde == NULL) { + state->fd = fd; + state->buf = buf; + state->len = len; + state->flags = flags; + state->addr = addr; + state->addr_len = addr_len; + + fde = tevent_add_fd(ev, state, fd, TEVENT_FD_WRITE, sendto_handler, + result); + if (fde == NULL) { TALLOC_FREE(result); return NULL; } - *pstate = state; - return result; -} - -/** - * Retrieve a ssize_t typed result from an async syscall - * @param[in] req The syscall that has just finished - * @param[out] perrno Where to put the syscall's errno - * @retval The return value from the asynchronously called syscall - */ - -ssize_t async_syscall_result_ssize_t(struct async_req *req, int *perrno) -{ - struct async_syscall_state *state = talloc_get_type_abort( - req->private_data, struct async_syscall_state); - - *perrno = state->sys_errno; - return state->result.result_ssize_t; -} - -/** - * Retrieve a size_t typed result from an async syscall - * @param[in] req The syscall that has just finished - * @param[out] perrno Where to put the syscall's errno - * @retval The return value from the asynchronously called syscall - */ - -size_t async_syscall_result_size_t(struct async_req *req, int *perrno) -{ - struct async_syscall_state *state = talloc_get_type_abort( - req->private_data, struct async_syscall_state); - - *perrno = state->sys_errno; - return state->result.result_size_t; -} - -/** - * Retrieve a int typed result from an async syscall - * @param[in] req The syscall that has just finished - * @param[out] perrno Where to put the syscall's errno - * @retval The return value from the asynchronously called syscall - */ - -int async_syscall_result_int(struct async_req *req, int *perrno) -{ - struct async_syscall_state *state = talloc_get_type_abort( - req->private_data, struct async_syscall_state); - - *perrno = state->sys_errno; - return state->result.result_int; -} - -/** - * fde event handler for the "send" syscall - * @param[in] ev The event context that sent us here - * @param[in] fde The file descriptor event associated with the send - * @param[in] flags Can only be TEVENT_FD_WRITE here - * @param[in] priv private data, "struct async_req *" in this case - */ - -static void async_send_callback(struct tevent_context *ev, - struct tevent_fd *fde, uint16_t flags, - void *priv) -{ - struct async_req *req = talloc_get_type_abort( - priv, struct async_req); - struct async_syscall_state *state = talloc_get_type_abort( - req->private_data, struct async_syscall_state); - struct param_send *p = &state->param.param_send; - - if (state->syscall_type != ASYNC_SYSCALL_SEND) { - async_req_error(req, EIO); - return; - } - - state->result.result_ssize_t = send(p->fd, p->buffer, p->length, - p->flags); - state->sys_errno = errno; - - TALLOC_FREE(state->fde); - - async_req_done(req); -} - -/** - * Async version of send(2) - * @param[in] mem_ctx The memory context to hang the result off - * @param[in] ev The event context to work from - * @param[in] fd The socket to send to - * @param[in] buffer The buffer to send - * @param[in] length How many bytes to send - * @param[in] flags flags passed to send(2) - * - * This function is a direct counterpart of send(2) - */ - -struct async_req *async_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - int fd, const void *buffer, size_t length, - int flags) -{ - struct async_req *result; - struct async_syscall_state *state; - - result = async_fde_syscall_new( - mem_ctx, ev, ASYNC_SYSCALL_SEND, - fd, TEVENT_FD_WRITE, async_send_callback, - &state); - if (result == NULL) { - return NULL; - } - - state->param.param_send.fd = fd; - state->param.param_send.buffer = buffer; - state->param.param_send.length = length; - state->param.param_send.flags = flags; - return result; } -/** - * fde event handler for the "sendall" syscall group - * @param[in] ev The event context that sent us here - * @param[in] fde The file descriptor event associated with the send - * @param[in] flags Can only be TEVENT_FD_WRITE here - * @param[in] priv private data, "struct async_req *" in this case - */ - -static void async_sendall_callback(struct tevent_context *ev, - struct tevent_fd *fde, uint16_t flags, - void *priv) +static void sendto_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *private_data) { - struct async_req *req = talloc_get_type_abort( - priv, struct async_req); - struct async_syscall_state *state = talloc_get_type_abort( - req->private_data, struct async_syscall_state); - struct param_sendall *p = &state->param.param_sendall; - - if (state->syscall_type != ASYNC_SYSCALL_SENDALL) { - async_req_error(req, EIO); + struct tevent_req *req = talloc_get_type_abort( + private_data, struct tevent_req); + struct sendto_state *state = + tevent_req_data(req, struct sendto_state); + + state->sent = sendto(state->fd, state->buf, state->len, state->flags, + state->addr, state->addr_len); + if ((state->sent == -1) && (errno == EINTR)) { + /* retry */ return; } - - state->result.result_ssize_t = send(p->fd, - (const char *)p->buffer + p->sent, - p->length - p->sent, p->flags); - state->sys_errno = errno; - - if (state->result.result_ssize_t == -1) { - async_req_error(req, state->sys_errno); + if (state->sent == -1) { + tevent_req_error(req, errno); return; } - - if (state->result.result_ssize_t == 0) { - async_req_error(req, EOF); - return; - } - - p->sent += state->result.result_ssize_t; - if (p->sent > p->length) { - async_req_error(req, EIO); - return; - } - - if (p->sent == p->length) { - TALLOC_FREE(state->fde); - async_req_done(req); - } -} - -/** - * @brief Send all bytes to a socket - * @param[in] mem_ctx The memory context to hang the result off - * @param[in] ev The event context to work from - * @param[in] fd The socket to send to - * @param[in] buffer The buffer to send - * @param[in] length How many bytes to send - * @param[in] flags flags passed to send(2) - * - * async_sendall calls send(2) as long as it is necessary to send all of the - * "length" bytes - */ - -struct async_req *sendall_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - int fd, const void *buffer, size_t length, - int flags) -{ - struct async_req *result; - struct async_syscall_state *state; - - result = async_fde_syscall_new( - mem_ctx, ev, ASYNC_SYSCALL_SENDALL, - fd, TEVENT_FD_WRITE, async_sendall_callback, - &state); - if (result == NULL) { - return NULL; - } - - state->param.param_sendall.fd = fd; - state->param.param_sendall.buffer = buffer; - state->param.param_sendall.length = length; - state->param.param_sendall.flags = flags; - state->param.param_sendall.sent = 0; - - return result; + tevent_req_done(req); } -ssize_t sendall_recv(struct async_req *req, int *perr) +ssize_t sendto_recv(struct tevent_req *req, int *perrno) { - struct async_syscall_state *state = talloc_get_type_abort( - req->private_data, struct async_syscall_state); - int err; - - err = async_req_simple_recv_errno(req); + struct sendto_state *state = + tevent_req_data(req, struct sendto_state); - if (err != 0) { - *perr = err; + if (tevent_req_is_unix_error(req, perrno)) { return -1; } - - return state->result.result_ssize_t; + return state->sent; } -/** - * fde event handler for the "recv" syscall - * @param[in] ev The event context that sent us here - * @param[in] fde The file descriptor event associated with the recv - * @param[in] flags Can only be TEVENT_FD_READ here - * @param[in] priv private data, "struct async_req *" in this case - */ - -static void async_recv_callback(struct tevent_context *ev, - struct tevent_fd *fde, uint16_t flags, - void *priv) -{ - struct async_req *req = talloc_get_type_abort( - priv, struct async_req); - struct async_syscall_state *state = talloc_get_type_abort( - req->private_data, struct async_syscall_state); - struct param_recv *p = &state->param.param_recv; - - if (state->syscall_type != ASYNC_SYSCALL_RECV) { - async_req_error(req, EIO); - return; - } - - state->result.result_ssize_t = recv(p->fd, p->buffer, p->length, - p->flags); - state->sys_errno = errno; +struct recvfrom_state { + int fd; + void *buf; + size_t len; + int flags; + struct sockaddr *addr; + socklen_t *addr_len; + ssize_t received; +}; - TALLOC_FREE(state->fde); +static void recvfrom_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *private_data); - async_req_done(req); -} - -/** - * Async version of recv(2) - * @param[in] mem_ctx The memory context to hang the result off - * @param[in] ev The event context to work from - * @param[in] fd The socket to recv from - * @param[in] buffer The buffer to recv into - * @param[in] length How many bytes to recv - * @param[in] flags flags passed to recv(2) - * - * This function is a direct counterpart of recv(2) - */ - -struct async_req *async_recv(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - int fd, void *buffer, size_t length, - int flags) +struct tevent_req *recvfrom_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + int fd, void *buf, size_t len, int flags, + struct sockaddr *addr, socklen_t *addr_len) { - struct async_req *result; - struct async_syscall_state *state; - - result = async_fde_syscall_new( - mem_ctx, ev, ASYNC_SYSCALL_RECV, - fd, TEVENT_FD_READ, async_recv_callback, - &state); + struct tevent_req *result; + struct recvfrom_state *state; + struct tevent_fd *fde; + result = tevent_req_create(mem_ctx, &state, struct recvfrom_state); if (result == NULL) { + return result; + } + state->fd = fd; + state->buf = buf; + state->len = len; + state->flags = flags; + state->addr = addr; + state->addr_len = addr_len; + + fde = tevent_add_fd(ev, state, fd, TEVENT_FD_READ, recvfrom_handler, + result); + if (fde == NULL) { + TALLOC_FREE(result); return NULL; } - - state->param.param_recv.fd = fd; - state->param.param_recv.buffer = buffer; - state->param.param_recv.length = length; - state->param.param_recv.flags = flags; - return result; } -/** - * fde event handler for the "recvall" syscall group - * @param[in] ev The event context that sent us here - * @param[in] fde The file descriptor event associated with the recv - * @param[in] flags Can only be TEVENT_FD_READ here - * @param[in] priv private data, "struct async_req *" in this case - */ - -static void async_recvall_callback(struct tevent_context *ev, - struct tevent_fd *fde, uint16_t flags, - void *priv) +static void recvfrom_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *private_data) { - struct async_req *req = talloc_get_type_abort( - priv, struct async_req); - struct async_syscall_state *state = talloc_get_type_abort( - req->private_data, struct async_syscall_state); - struct param_recvall *p = &state->param.param_recvall; - - if (state->syscall_type != ASYNC_SYSCALL_RECVALL) { - async_req_error(req, EIO); + struct tevent_req *req = talloc_get_type_abort( + private_data, struct tevent_req); + struct recvfrom_state *state = + tevent_req_data(req, struct recvfrom_state); + + state->received = recvfrom(state->fd, state->buf, state->len, + state->flags, state->addr, state->addr_len); + if ((state->received == -1) && (errno == EINTR)) { + /* retry */ return; } - - state->result.result_ssize_t = recv(p->fd, - (char *)p->buffer + p->received, - p->length - p->received, p->flags); - state->sys_errno = errno; - - if (state->result.result_ssize_t == -1) { - async_req_error(req, state->sys_errno); + if (state->received == 0) { + tevent_req_error(req, EPIPE); return; } - - if (state->result.result_ssize_t == 0) { - async_req_error(req, EIO); + if (state->received == -1) { + tevent_req_error(req, errno); return; } - - p->received += state->result.result_ssize_t; - if (p->received > p->length) { - async_req_error(req, EIO); - return; - } - - if (p->received == p->length) { - TALLOC_FREE(state->fde); - async_req_done(req); - } -} - -/** - * Receive a specified number of bytes from a socket - * @param[in] mem_ctx The memory context to hang the result off - * @param[in] ev The event context to work from - * @param[in] fd The socket to recv from - * @param[in] buffer The buffer to recv into - * @param[in] length How many bytes to recv - * @param[in] flags flags passed to recv(2) - * - * async_recvall will call recv(2) until "length" bytes are received - */ - -struct async_req *recvall_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, - int fd, void *buffer, size_t length, - int flags) -{ - struct async_req *result; - struct async_syscall_state *state; - - result = async_fde_syscall_new( - mem_ctx, ev, ASYNC_SYSCALL_RECVALL, - fd, TEVENT_FD_READ, async_recvall_callback, - &state); - if (result == NULL) { - return NULL; - } - - state->param.param_recvall.fd = fd; - state->param.param_recvall.buffer = buffer; - state->param.param_recvall.length = length; - state->param.param_recvall.flags = flags; - state->param.param_recvall.received = 0; - - return result; + tevent_req_done(req); } -ssize_t recvall_recv(struct async_req *req, int *perr) +ssize_t recvfrom_recv(struct tevent_req *req, int *perrno) { - struct async_syscall_state *state = talloc_get_type_abort( - req->private_data, struct async_syscall_state); - int err; + struct recvfrom_state *state = + tevent_req_data(req, struct recvfrom_state); - err = async_req_simple_recv_errno(req); - - if (err != 0) { - *perr = err; + if (tevent_req_is_unix_error(req, perrno)) { return -1; } - - return state->result.result_ssize_t; + return state->received; } struct async_connect_state { @@ -614,6 +197,8 @@ struct async_connect_state { int result; int sys_errno; long old_sockflags; + socklen_t address_len; + struct sockaddr_storage address; }; static void async_connect_connected(struct tevent_context *ev, @@ -633,17 +218,18 @@ static void async_connect_connected(struct tevent_context *ev, * connect in an async state. This will be reset when the request is finished. */ -struct async_req *async_connect_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - int fd, const struct sockaddr *address, - socklen_t address_len) +struct tevent_req *async_connect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + int fd, const struct sockaddr *address, + socklen_t address_len) { - struct async_req *result; + struct tevent_req *result; struct async_connect_state *state; struct tevent_fd *fde; - if (!async_req_setup(mem_ctx, &result, &state, - struct async_connect_state)) { + result = tevent_req_create( + mem_ctx, &state, struct async_connect_state); + if (result == NULL) { return NULL; } @@ -660,12 +246,19 @@ struct async_req *async_connect_send(TALLOC_CTX *mem_ctx, goto post_errno; } + state->address_len = address_len; + if (address_len > sizeof(state->address)) { + errno = EINVAL; + goto post_errno; + } + memcpy(&state->address, address, address_len); + set_blocking(fd, false); state->result = connect(fd, address, address_len); if (state->result == 0) { - state->sys_errno = 0; - goto post_status; + tevent_req_done(result); + goto done; } /** @@ -680,6 +273,7 @@ struct async_req *async_connect_send(TALLOC_CTX *mem_ctx, errno == EISCONN || #endif errno == EAGAIN || errno == EINTR)) { + state->sys_errno = errno; goto post_errno; } @@ -687,21 +281,15 @@ struct async_req *async_connect_send(TALLOC_CTX *mem_ctx, async_connect_connected, result); if (fde == NULL) { state->sys_errno = ENOMEM; - goto post_status; + goto post_errno; } return result; post_errno: - state->sys_errno = errno; - post_status: + tevent_req_error(result, state->sys_errno); + done: fcntl(fd, F_SETFL, state->old_sockflags); - if (!async_post_error(result, ev, state->sys_errno)) { - goto fail; - } - return result; - fail: - TALLOC_FREE(result); - return NULL; + return tevent_req_post(result, ev); } /** @@ -716,12 +304,10 @@ static void async_connect_connected(struct tevent_context *ev, struct tevent_fd *fde, uint16_t flags, void *priv) { - struct async_req *req = talloc_get_type_abort( - priv, struct async_req); - struct async_connect_state *state = talloc_get_type_abort( - req->private_data, struct async_connect_state); - - TALLOC_FREE(fde); + struct tevent_req *req = talloc_get_type_abort( + priv, struct tevent_req); + struct async_connect_state *state = + tevent_req_data(req, struct async_connect_state); /* * Stevens, Network Programming says that if there's a @@ -730,40 +316,43 @@ static void async_connect_connected(struct tevent_context *ev, */ if ((flags & (TEVENT_FD_READ|TEVENT_FD_WRITE)) == (TEVENT_FD_READ|TEVENT_FD_WRITE)) { - int sockerr; - socklen_t err_len = sizeof(sockerr); - - if (getsockopt(state->fd, SOL_SOCKET, SO_ERROR, - (void *)&sockerr, &err_len) == 0) { - errno = sockerr; + int ret; + + ret = connect(state->fd, + (struct sockaddr *)(void *)&state->address, + state->address_len); + if (ret == 0) { + TALLOC_FREE(fde); + tevent_req_done(req); + return; } - state->sys_errno = errno; - - DEBUG(10, ("connect returned %s\n", strerror(errno))); - - fcntl(state->fd, F_SETFL, state->old_sockflags); - async_req_error(req, state->sys_errno); + if (errno == EINPROGRESS) { + /* Try again later, leave the fde around */ + return; + } + TALLOC_FREE(fde); + tevent_req_error(req, errno); return; } state->sys_errno = 0; - async_req_done(req); + tevent_req_done(req); } -int async_connect_recv(struct async_req *req, int *perrno) +int async_connect_recv(struct tevent_req *req, int *perrno) { - struct async_connect_state *state = talloc_get_type_abort( - req->private_data, struct async_connect_state); + struct async_connect_state *state = + tevent_req_data(req, struct async_connect_state); int err; fcntl(state->fd, F_SETFL, state->old_sockflags); - - if (async_req_is_errno(req, &err)) { + if (tevent_req_is_unix_error(req, &err)) { *perrno = err; return -1; } + if (state->sys_errno == 0) { return 0; } @@ -771,3 +360,266 @@ int async_connect_recv(struct async_req *req, int *perrno) *perrno = state->sys_errno; return -1; } + +struct writev_state { + struct tevent_context *ev; + int fd; + struct iovec *iov; + int count; + size_t total_size; + uint16_t flags; +}; + +static void writev_trigger(struct tevent_req *req, void *private_data); +static void writev_handler(struct tevent_context *ev, struct tevent_fd *fde, + uint16_t flags, void *private_data); + +struct tevent_req *writev_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct tevent_queue *queue, int fd, + bool err_on_readability, + struct iovec *iov, int count) +{ + struct tevent_req *req; + struct writev_state *state; + + req = tevent_req_create(mem_ctx, &state, struct writev_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->fd = fd; + state->total_size = 0; + state->count = count; + state->iov = (struct iovec *)talloc_memdup( + state, iov, sizeof(struct iovec) * count); + if (state->iov == NULL) { + goto fail; + } + state->flags = TEVENT_FD_WRITE; + if (err_on_readability) { + state->flags |= TEVENT_FD_READ; + } + + if (queue == NULL) { + struct tevent_fd *fde; + fde = tevent_add_fd(state->ev, state, state->fd, + state->flags, writev_handler, req); + if (tevent_req_nomem(fde, req)) { + return tevent_req_post(req, ev); + } + return req; + } + + if (!tevent_queue_add(queue, ev, req, writev_trigger, NULL)) { + goto fail; + } + return req; + fail: + TALLOC_FREE(req); + return NULL; +} + +static void writev_trigger(struct tevent_req *req, void *private_data) +{ + struct writev_state *state = tevent_req_data(req, struct writev_state); + struct tevent_fd *fde; + + fde = tevent_add_fd(state->ev, state, state->fd, state->flags, + writev_handler, req); + if (fde == NULL) { + tevent_req_error(req, ENOMEM); + } +} + +static void writev_handler(struct tevent_context *ev, struct tevent_fd *fde, + uint16_t flags, void *private_data) +{ + struct tevent_req *req = talloc_get_type_abort( + private_data, struct tevent_req); + struct writev_state *state = + tevent_req_data(req, struct writev_state); + size_t to_write, written; + int i; + + to_write = 0; + + if ((state->flags & TEVENT_FD_READ) && (flags & TEVENT_FD_READ)) { + tevent_req_error(req, EPIPE); + return; + } + + for (i=0; icount; i++) { + to_write += state->iov[i].iov_len; + } + + written = writev(state->fd, state->iov, state->count); + if ((written == -1) && (errno == EINTR)) { + /* retry */ + return; + } + if (written == -1) { + tevent_req_error(req, errno); + return; + } + if (written == 0) { + tevent_req_error(req, EPIPE); + return; + } + state->total_size += written; + + if (written == to_write) { + tevent_req_done(req); + return; + } + + /* + * We've written less than we were asked to, drop stuff from + * state->iov. + */ + + while (written > 0) { + if (written < state->iov[0].iov_len) { + state->iov[0].iov_base = + (char *)state->iov[0].iov_base + written; + state->iov[0].iov_len -= written; + break; + } + written -= state->iov[0].iov_len; + state->iov += 1; + state->count -= 1; + } +} + +ssize_t writev_recv(struct tevent_req *req, int *perrno) +{ + struct writev_state *state = + tevent_req_data(req, struct writev_state); + + if (tevent_req_is_unix_error(req, perrno)) { + return -1; + } + return state->total_size; +} + +struct read_packet_state { + int fd; + uint8_t *buf; + size_t nread; + ssize_t (*more)(uint8_t *buf, size_t buflen, void *private_data); + void *private_data; +}; + +static void read_packet_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *private_data); + +struct tevent_req *read_packet_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + int fd, size_t initial, + ssize_t (*more)(uint8_t *buf, + size_t buflen, + void *private_data), + void *private_data) +{ + struct tevent_req *result; + struct read_packet_state *state; + struct tevent_fd *fde; + + result = tevent_req_create(mem_ctx, &state, struct read_packet_state); + if (result == NULL) { + return NULL; + } + state->fd = fd; + state->nread = 0; + state->more = more; + state->private_data = private_data; + + state->buf = talloc_array(state, uint8_t, initial); + if (state->buf == NULL) { + goto fail; + } + + fde = tevent_add_fd(ev, state, fd, TEVENT_FD_READ, read_packet_handler, + result); + if (fde == NULL) { + goto fail; + } + return result; + fail: + TALLOC_FREE(result); + return NULL; +} + +static void read_packet_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *private_data) +{ + struct tevent_req *req = talloc_get_type_abort( + private_data, struct tevent_req); + struct read_packet_state *state = + tevent_req_data(req, struct read_packet_state); + size_t total = talloc_get_size(state->buf); + ssize_t nread, more; + uint8_t *tmp; + + nread = recv(state->fd, state->buf+state->nread, total-state->nread, + 0); + if ((nread == -1) && (errno == EINTR)) { + /* retry */ + return; + } + if (nread == -1) { + tevent_req_error(req, errno); + return; + } + if (nread == 0) { + tevent_req_error(req, EPIPE); + return; + } + + state->nread += nread; + if (state->nread < total) { + /* Come back later */ + return; + } + + /* + * We got what was initially requested. See if "more" asks for -- more. + */ + if (state->more == NULL) { + /* Nobody to ask, this is a async read_data */ + tevent_req_done(req); + return; + } + + more = state->more(state->buf, total, state->private_data); + if (more == -1) { + /* We got an invalid packet, tell the caller */ + tevent_req_error(req, EIO); + return; + } + if (more == 0) { + /* We're done, full packet received */ + tevent_req_done(req); + return; + } + + tmp = talloc_realloc(state, state->buf, uint8_t, total+more); + if (tevent_req_nomem(tmp, req)) { + return; + } + state->buf = tmp; +} + +ssize_t read_packet_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint8_t **pbuf, int *perrno) +{ + struct read_packet_state *state = + tevent_req_data(req, struct read_packet_state); + + if (tevent_req_is_unix_error(req, perrno)) { + return -1; + } + *pbuf = talloc_move(mem_ctx, &state->buf); + return talloc_get_size(*pbuf); +}