int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout) { int rc; size_t i, used; ssh_poll_handle p; socket_t fd; int revents; struct ssh_timestamp ts; if (ctx->polls_used == 0) { return SSH_ERROR; } ssh_timestamp_init(&ts); do { int tm = ssh_timeout_update(&ts, timeout); rc = ssh_poll(ctx->pollfds, ctx->polls_used, tm); } while (rc == -1 && errno == EINTR); if (rc < 0) { return SSH_ERROR; } if (rc == 0) { return SSH_AGAIN; } used = ctx->polls_used; for (i = 0; i < used && rc > 0; ) { if (!ctx->pollfds[i].revents || ctx->pollptrs[i]->lock) { i++; } else { int ret; p = ctx->pollptrs[i]; fd = ctx->pollfds[i].fd; revents = ctx->pollfds[i].revents; /* avoid having any event caught during callback */ ctx->pollfds[i].events = 0; p->lock = 1; if (p->cb && (ret = p->cb(p, fd, revents, p->cb_data)) < 0) { if (ret == -2) { return -1; } /* the poll was removed, reload the used counter and start again */ used = ctx->polls_used; i = 0; } else { ctx->pollfds[i].revents = 0; ctx->pollfds[i].events = p->events; p->lock = 0; i++; } rc--; } } return rc; }
static void torture_timeout_update(void **state){ struct ssh_timestamp ts; (void) state; ssh_timestamp_init(&ts); usleep(50000); assert_int_equal(ssh_timeout_update(&ts,25), 0); assert_in_range(ssh_timeout_update(&ts,30000),29000,29960); assert_in_range(ssh_timeout_update(&ts,75),1,40); assert_int_equal(ssh_timeout_update(&ts,0),0); assert_int_equal(ssh_timeout_update(&ts,-1),-1); }
static void torture_timeout_elapsed(void **state){ struct ssh_timestamp ts; (void) state; ssh_timestamp_init(&ts); usleep(50000); assert_true(ssh_timeout_elapsed(&ts,25)); assert_false(ssh_timeout_elapsed(&ts,30000)); assert_false(ssh_timeout_elapsed(&ts,75)); assert_true(ssh_timeout_elapsed(&ts,0)); assert_false(ssh_timeout_elapsed(&ts,-1)); }
/** * @brief A wrapper for the select syscall * * This functions acts more or less like the select(2) syscall.\n * There is no support for writing or exceptions.\n * * @param[in] channels Arrays of channels pointers terminated by a NULL. * It is never rewritten. * * @param[out] outchannels Arrays of same size that "channels", there is no need * to initialize it. * * @param[in] maxfd Maximum +1 file descriptor from readfds. * * @param[in] readfds A fd_set of file descriptors to be select'ed for * reading. * * @param[in] timeout A timeout for the select. * * @return SSH_OK on success, * SSH_ERROR on error, * SSH_EINTR if it was interrupted. In that case, * just restart it. * * @warning libssh is not reentrant here. That means that if a signal is caught * during the processing of this function, you cannot call libssh * functions on sessions that are busy with ssh_select(). * * @see select(2) */ int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socket_t maxfd, fd_set *readfds, struct timeval *timeout) { fd_set origfds; socket_t fd; int i,j; int rc; int base_tm, tm; struct ssh_timestamp ts; ssh_event event = ssh_event_new(); int firstround=1; base_tm = tm=timeout->tv_sec * 1000 + timeout->tv_usec/1000; for (i=0 ; channels[i] != NULL; ++i){ ssh_event_add_session(event, channels[i]->session); } FD_ZERO(&origfds); for (fd = 0; fd < maxfd ; fd++) { if (FD_ISSET(fd, readfds)) { ssh_event_add_fd(event, fd, POLLIN, ssh_select_cb, readfds); FD_SET(fd, &origfds); } } outchannels[0] = NULL; FD_ZERO(readfds); ssh_timestamp_init(&ts); do { /* Poll every channel */ j = 0; for (i = 0; channels[i]; i++) { if(ssh_channel_poll(channels[i], 0) != 0) { outchannels[j] = channels[i]; j++; } else if(ssh_channel_poll(channels[i], 1) != 0) { outchannels[j] = channels[i]; j++; } } outchannels[j] = NULL; if(j != 0) break; /* watch if a user socket was triggered */ for (fd = 0; fd < maxfd; fd++) { if (FD_ISSET(fd, readfds)) { goto out; } } /* If the timeout is elapsed, we should go out */ if(!firstround && ssh_timeout_elapsed(&ts, base_tm)) goto out; /* since there's nothing, let's fire the polling */ rc = ssh_event_dopoll(event,tm); if (rc == SSH_ERROR){ goto out; } tm = ssh_timeout_update(&ts, base_tm); firstround=0; } while (1); out: for (fd = 0; fd < maxfd; fd++) { if (FD_ISSET(fd, &origfds)) { ssh_event_remove_fd(event, fd); } } ssh_event_free(event); return SSH_OK; }