poll.c | poll.c | |||
---|---|---|---|---|
/* | /* | |||
* poll.c - poll wrapper | * poll.c - poll wrapper | |||
* | * | |||
* This file is part of the SSH Library | * This file is part of the SSH Library | |||
* | * | |||
* Copyright (c) 2003-2008 by Aris Adamantiadis | * Copyright (c) 2003-2009 by Aris Adamantiadis | |||
* Copyright (c) 2009 Aleksandar Kanchev | ||||
* | * | |||
* The SSH Library is free software; you can redistribute it and/or modify | * The SSH 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 | * it under the terms of the GNU Lesser General Public License as published by | |||
* the Free Software Foundation; either version 2.1 of the License, or (at your | * the Free Software Foundation; either version 2.1 of the License, or (at your | |||
* option) any later version. | * option) any later version. | |||
* | * | |||
* The SSH Library is distributed in the hope that it will be useful, but | * The SSH Library is distributed in the hope that it will be useful, but | |||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILI TY | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILI TY | |||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public | |||
* License for more details. | * License for more details. | |||
skipping to change at line 28 | skipping to change at line 29 | |||
* You should have received a copy of the GNU Lesser General Public License | * You should have received a copy of the GNU Lesser General Public License | |||
* along with the SSH Library; see the file COPYING. If not, write to | * along with the SSH Library; see the file COPYING. If not, write to | |||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, | * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, | |||
* MA 02111-1307, USA. | * MA 02111-1307, USA. | |||
* | * | |||
* vim: ts=2 sw=2 et cindent | * vim: ts=2 sw=2 et cindent | |||
*/ | */ | |||
/* This code is based on glib's gpoll */ | /* This code is based on glib's gpoll */ | |||
#include "config.h" | ||||
#include <errno.h> | #include <errno.h> | |||
#include "config.h" | ||||
#include "libssh/priv.h" | #include "libssh/priv.h" | |||
#include "libssh/libssh.h" | ||||
#include "libssh/poll.h" | ||||
#ifndef SSH_POLL_CTX_CHUNK | ||||
#define SSH_POLL_CTX_CHUNK 5 | ||||
#endif | ||||
struct ssh_poll_handle_struct { | ||||
ssh_poll_ctx ctx; | ||||
union { | ||||
socket_t fd; | ||||
size_t idx; | ||||
} x; | ||||
short events; | ||||
ssh_poll_callback cb; | ||||
void *cb_data; | ||||
}; | ||||
struct ssh_poll_ctx_struct { | ||||
ssh_poll_handle *pollptrs; | ||||
ssh_pollfd_t *pollfds; | ||||
size_t polls_allocated; | ||||
size_t polls_used; | ||||
size_t chunk_size; | ||||
}; | ||||
#ifdef HAVE_POLL | #ifdef HAVE_POLL | |||
#include <poll.h> | #include <poll.h> | |||
int ssh_poll(pollfd_t *fds, nfds_t nfds, int timeout) { | int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) { | |||
return poll((struct pollfd *) fds, nfds, timeout); | return poll((struct pollfd *) fds, nfds, timeout); | |||
} | } | |||
#else /* HAVE_POLL */ | #else /* HAVE_POLL */ | |||
#ifdef _WIN32 | #ifdef _WIN32 | |||
#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) | #if 0 | |||
/* defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) */ | ||||
#include <winsock2.h> | #include <winsock2.h> | |||
int ssh_poll(pollfd_t *fds, nfds_t nfds, int timeout) { | int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) { | |||
return WSAPoll(fds, nfds, timeout); | return WSAPoll(fds, nfds, timeout); | |||
} | } | |||
#else /* _WIN32_WINNT */ | #else /* _WIN32_WINNT */ | |||
#ifndef STRICT | #ifndef STRICT | |||
#define STRICT | #define STRICT | |||
#endif | #endif | |||
#include <stdio.h> | #include <stdio.h> | |||
#include <windows.h> | #include <windows.h> | |||
#include <errno.h> | ||||
static int poll_rest (HANDLE *handles, int nhandles, | static int poll_rest (HANDLE *handles, int nhandles, | |||
pollfd_t *fds, nfds_t nfds, int timeout) { | ssh_pollfd_t *fds, nfds_t nfds, int timeout) { | |||
DWORD ready; | DWORD ready; | |||
pollfd_t *f; | ssh_pollfd_t *f; | |||
int recursed_result; | int recursed_result; | |||
if (nhandles == 0) { | if (nhandles == 0) { | |||
/* No handles to wait for, just the timeout */ | /* No handles to wait for, just the timeout */ | |||
if (timeout == INFINITE) { | if (timeout == INFINITE) { | |||
ready = WAIT_FAILED; | ready = WAIT_FAILED; | |||
} else { | } else { | |||
SleepEx(timeout, 1); | SleepEx(timeout, 1); | |||
ready = WAIT_TIMEOUT; | ready = WAIT_TIMEOUT; | |||
} | } | |||
skipping to change at line 120 | skipping to change at line 149 | |||
return -1; | return -1; | |||
} | } | |||
return recursed_result + 1; | return recursed_result + 1; | |||
} | } | |||
return 1; | return 1; | |||
} | } | |||
return 0; | return 0; | |||
} | } | |||
int ssh_poll(pollfd_t *fds, nfds_t nfds, int timeout) { | int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) { | |||
HANDLE handles[MAXIMUM_WAIT_OBJECTS]; | HANDLE handles[MAXIMUM_WAIT_OBJECTS]; | |||
pollfd_t *f; | ssh_pollfd_t *f; | |||
int nhandles = 0; | int nhandles = 0; | |||
int rc = -1; | int rc = -1; | |||
if (fds == NULL) { | if (fds == NULL) { | |||
errno = EFAULT; | errno = EFAULT; | |||
return -1; | return -1; | |||
} | } | |||
if (nfds >= MAXIMUM_WAIT_OBJECTS) { | if (nfds >= MAXIMUM_WAIT_OBJECTS) { | |||
errno = EINVAL; | errno = EINVAL; | |||
skipping to change at line 206 | skipping to change at line 235 | |||
} | } | |||
return rc; | return rc; | |||
} | } | |||
#endif /* _WIN32_WINNT */ | #endif /* _WIN32_WINNT */ | |||
#endif /* _WIN32 */ | #endif /* _WIN32 */ | |||
#endif /* HAVE_POLL */ | #endif /* HAVE_POLL */ | |||
/** | ||||
* @brief Allocate a new poll object, which could be used within a poll co | ||||
ntext. | ||||
* | ||||
* @param fd Socket that will be polled. | ||||
* @param events Poll events that will be monitored for the socket. | ||||
i.e. | ||||
* POLLIN, POLLPRI, POLLOUT, POLLERR, POLLHUP, POLLNVA | ||||
L | ||||
* @param cb Function to be called if any of the events are set. | ||||
* @param userdata Userdata to be passed to the callback function. NUL | ||||
L if | ||||
* not needed. | ||||
* | ||||
* @return A new poll object, NULL on error | ||||
*/ | ||||
ssh_poll_handle ssh_poll_new(socket_t fd, short events, ssh_poll_callback c | ||||
b, | ||||
void *userdata) { | ||||
ssh_poll_handle p; | ||||
p = malloc(sizeof(struct ssh_poll_handle_struct)); | ||||
if (p != NULL) { | ||||
p->ctx = NULL; | ||||
p->x.fd = fd; | ||||
p->events = events; | ||||
p->cb = cb; | ||||
p->cb_data = userdata; | ||||
} | ||||
return p; | ||||
} | ||||
/** | ||||
* @brief Free a poll object. | ||||
* | ||||
* @param p Pointer to an already allocated poll object. | ||||
*/ | ||||
void ssh_poll_free(ssh_poll_handle p) { | ||||
SAFE_FREE(p); | ||||
} | ||||
/** | ||||
* @brief Get the poll context of a poll object. | ||||
* | ||||
* @param p Pointer to an already allocated poll object. | ||||
* | ||||
* @return Poll context or NULL if the poll object isn't attac | ||||
hed. | ||||
*/ | ||||
ssh_poll_ctx ssh_poll_get_ctx(ssh_poll_handle p) { | ||||
return p->ctx; | ||||
} | ||||
/** | ||||
* @brief Get the events of a poll object. | ||||
* | ||||
* @param p Pointer to an already allocated poll object. | ||||
* | ||||
* @return Poll events. | ||||
*/ | ||||
short ssh_poll_get_events(ssh_poll_handle p) { | ||||
return p->events; | ||||
} | ||||
/** | ||||
* @brief Set the events of a poll object. The events will also be propaga | ||||
ted | ||||
* to an associated poll context. | ||||
* | ||||
* @param p Pointer to an already allocated poll object. | ||||
* @param events Poll events. | ||||
*/ | ||||
void ssh_poll_set_events(ssh_poll_handle p, short events) { | ||||
p->events = events; | ||||
if (p->ctx != NULL) { | ||||
p->ctx->pollfds[p->x.idx].events = events; | ||||
} | ||||
} | ||||
/** | ||||
* @brief Add extra events to a poll object. Duplicates are ignored. | ||||
* The events will also be propagated to an associated poll context | ||||
. | ||||
* | ||||
* @param p Pointer to an already allocated poll object. | ||||
* @param events Poll events. | ||||
*/ | ||||
void ssh_poll_add_events(ssh_poll_handle p, short events) { | ||||
ssh_poll_set_events(p, ssh_poll_get_events(p) | events); | ||||
} | ||||
/** | ||||
* @brief Remove events from a poll object. Non-existent are ignored. | ||||
* The events will also be propagated to an associated poll context | ||||
. | ||||
* | ||||
* @param p Pointer to an already allocated poll object. | ||||
* @param events Poll events. | ||||
*/ | ||||
void ssh_poll_remove_events(ssh_poll_handle p, short events) { | ||||
ssh_poll_set_events(p, ssh_poll_get_events(p) & ~events); | ||||
} | ||||
/** | ||||
* @brief Get the raw socket of a poll object. | ||||
* | ||||
* @param p Pointer to an already allocated poll object. | ||||
* | ||||
* @return Raw socket. | ||||
*/ | ||||
socket_t ssh_poll_get_fd(ssh_poll_handle p) { | ||||
if (p->ctx != NULL) { | ||||
return p->ctx->pollfds[p->x.idx].fd; | ||||
} | ||||
return p->x.fd; | ||||
} | ||||
/** | ||||
* @brief Set the callback of a poll object. | ||||
* | ||||
* @param p Pointer to an already allocated poll object. | ||||
* @param cb Function to be called if any of the events are set. | ||||
* @param userdata Userdata to be passed to the callback function. NUL | ||||
L if | ||||
* not needed. | ||||
*/ | ||||
void ssh_poll_set_callback(ssh_poll_handle p, ssh_poll_callback cb, void *u | ||||
serdata) { | ||||
if (cb != NULL) { | ||||
p->cb = cb; | ||||
p->cb_data = userdata; | ||||
} | ||||
} | ||||
/** | ||||
* @brief Create a new poll context. It could be associated with many poll | ||||
object | ||||
* which are going to be polled at the same time as the poll contex | ||||
t. You | ||||
* would need a single poll context per thread. | ||||
* | ||||
* @param chunk_size The size of the memory chunk that will be allocated | ||||
, when | ||||
* more memory is needed. This is for efficiency reaso | ||||
ns, | ||||
* i.e. don't allocate memory for each new poll object | ||||
, but | ||||
* for the next 5. Set it to 0 if you want to use the | ||||
* library's default value. | ||||
*/ | ||||
ssh_poll_ctx ssh_poll_ctx_new(size_t chunk_size) { | ||||
ssh_poll_ctx ctx; | ||||
ctx = malloc(sizeof(struct ssh_poll_ctx_struct)); | ||||
if (ctx != NULL) { | ||||
if (!chunk_size) { | ||||
chunk_size = SSH_POLL_CTX_CHUNK; | ||||
} | ||||
ctx->chunk_size = chunk_size; | ||||
ctx->pollptrs = NULL; | ||||
ctx->pollfds = NULL; | ||||
ctx->polls_allocated = 0; | ||||
ctx->polls_used = 0; | ||||
} | ||||
return ctx; | ||||
} | ||||
/** | ||||
* @brief Free a poll context. | ||||
* | ||||
* @param ctx Pointer to an already allocated poll context. | ||||
*/ | ||||
void ssh_poll_ctx_free(ssh_poll_ctx ctx) { | ||||
if (ctx->polls_allocated > 0) { | ||||
register size_t i, used; | ||||
used = ctx->polls_used; | ||||
for (i = 0; i < used; ) { | ||||
ssh_poll_handle p = ctx->pollptrs[i]; | ||||
int fd = ctx->pollfds[i].fd; | ||||
/* force poll object removal */ | ||||
if (p->cb(p, fd, POLLERR, p->cb_data) < 0) { | ||||
used = ctx->polls_used; | ||||
} else { | ||||
i++; | ||||
} | ||||
} | ||||
SAFE_FREE(ctx->pollptrs); | ||||
SAFE_FREE(ctx->pollfds); | ||||
} | ||||
SAFE_FREE(ctx); | ||||
} | ||||
static int ssh_poll_ctx_resize(ssh_poll_ctx ctx, size_t new_size) { | ||||
ssh_poll_handle *pollptrs; | ||||
ssh_pollfd_t *pollfds; | ||||
pollptrs = realloc(ctx->pollptrs, sizeof(ssh_poll_handle *) * new_size); | ||||
if (pollptrs == NULL) { | ||||
return -1; | ||||
} | ||||
pollfds = realloc(ctx->pollfds, sizeof(ssh_pollfd_t) * new_size); | ||||
if (pollfds == NULL) { | ||||
ctx->pollptrs = realloc(pollptrs, sizeof(ssh_poll_handle *) * ctx->poll | ||||
s_allocated); | ||||
return -1; | ||||
} | ||||
ctx->pollptrs = pollptrs; | ||||
ctx->pollfds = pollfds; | ||||
ctx->polls_allocated = new_size; | ||||
return 0; | ||||
} | ||||
/** | ||||
* @brief Add a poll object to a poll context. | ||||
* | ||||
* @param ctx Pointer to an already allocated poll context. | ||||
* @param p Pointer to an already allocated poll object. | ||||
* | ||||
* @return 0 on success, < 0 on error | ||||
*/ | ||||
int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p) { | ||||
int fd; | ||||
if (p->ctx != NULL) { | ||||
/* already attached to a context */ | ||||
return -1; | ||||
} | ||||
if (ctx->polls_used == ctx->polls_allocated && | ||||
ssh_poll_ctx_resize(ctx, ctx->polls_allocated + ctx->chunk_size) < 0) | ||||
{ | ||||
return -1; | ||||
} | ||||
fd = p->x.fd; | ||||
p->x.idx = ctx->polls_used++; | ||||
ctx->pollptrs[p->x.idx] = p; | ||||
ctx->pollfds[p->x.idx].fd = fd; | ||||
ctx->pollfds[p->x.idx].events = p->events; | ||||
ctx->pollfds[p->x.idx].revents = 0; | ||||
p->ctx = ctx; | ||||
return 0; | ||||
} | ||||
/** | ||||
* @brief Remove a poll object from a poll context. | ||||
* | ||||
* @param ctx Pointer to an already allocated poll context. | ||||
* @param p Pointer to an already allocated poll object. | ||||
*/ | ||||
void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p) { | ||||
size_t i; | ||||
i = p->x.idx; | ||||
p->x.fd = ctx->pollfds[i].fd; | ||||
p->ctx = NULL; | ||||
ctx->polls_used--; | ||||
/* fill the empty poll slot with the last one */ | ||||
if (ctx->polls_used > 0 && ctx->polls_used != i) { | ||||
ctx->pollfds[i] = ctx->pollfds[ctx->polls_used]; | ||||
ctx->pollptrs[i] = ctx->pollptrs[ctx->polls_used]; | ||||
} | ||||
/* this will always leave at least chunk_size polls allocated */ | ||||
if (ctx->polls_allocated - ctx->polls_used > ctx->chunk_size) { | ||||
ssh_poll_ctx_resize(ctx, ctx->polls_allocated - ctx->chunk_size); | ||||
} | ||||
} | ||||
/** | ||||
* @brief Poll all the sockets associated through a poll object with a | ||||
* poll context. If any of the events are set after the poll, the | ||||
* call back function of the socket will be called. | ||||
* This function should be called once within the programs main loo | ||||
p. | ||||
* | ||||
* @param ctx Pointer to an already allocated poll context. | ||||
* @param timeout An upper limit on the time for which ssh_poll_ctx() | ||||
will | ||||
* block, in milliseconds. Specifying a negative value | ||||
* means an infinite timeout. This parameter is passed | ||||
to | ||||
* the poll() function. | ||||
*/ | ||||
int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout) { | ||||
int rc; | ||||
if (!ctx->polls_used) | ||||
return 0; | ||||
rc = ssh_poll(ctx->pollfds, ctx->polls_used, timeout); | ||||
if (rc > 0) { | ||||
register size_t i, used; | ||||
used = ctx->polls_used; | ||||
for (i = 0; i < used && rc > 0; ) { | ||||
if (!ctx->pollfds[i].revents) { | ||||
i++; | ||||
} else { | ||||
ssh_poll_handle p = ctx->pollptrs[i]; | ||||
int fd = ctx->pollfds[i].fd; | ||||
int revents = ctx->pollfds[i].revents; | ||||
if (p->cb(p, fd, revents, p->cb_data) < 0) { | ||||
/* the poll was removed, reload the used counter and stall the lo | ||||
op */ | ||||
used = ctx->polls_used; | ||||
} else { | ||||
ctx->pollfds[i].revents = 0; | ||||
i++; | ||||
} | ||||
rc--; | ||||
} | ||||
} | ||||
} | ||||
return rc; | ||||
} | ||||
End of changes. 13 change blocks. | ||||
9 lines changed or deleted | 38 lines changed or added | |||
This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/ |