Improve channel_select().

git-svn-id: svn+ssh://svn.berlios.de/svnroot/repos/libssh/trunk@708 7dcaeef0-15fb-0310-b436-a5af3365683c
This commit is contained in:
Andreas Schneider
2009-05-04 10:42:07 +00:00
parent d9a0b90701
commit 0b7ae624a3

View File

@@ -1712,137 +1712,149 @@ static int count_ptrs(CHANNEL **ptrs) {
return c; return c;
} }
/** the list of pointers are then actualized and will only contain pointers to /**
* channels that are respectively readable, writable or have an exception to trap * @brief Act like the standard select(2) on channels.
* \brief act as the standard select(2) for channels *
* \param readchans a NULL pointer or an array of channel pointers, finished by a NULL * The list of pointers are then actualized and will only contain pointers to
* \param writechans a NULL pointer or an array of channel pointers, finished by a NULL * channels that are respectively readable, writable or have an exception to
* \param exceptchans a NULL pointer or an array of channel pointers, finished by a NULL * trap.
* \param timeout timeout as defined by select(2) *
* \return SSH_SUCCESS operation successful\n * @param readchans A NULL pointer or an array of channel pointers,
* SSH_EINTR select(2) syscall was interrupted, relaunch the function * terminated by a NULL.
*
* @param writechans A NULL pointer or an array of channel pointers,
* terminated by a NULL.
*
* @param exceptchans A NULL pointer or an array of channel pointers,
* terminated by a NULL.
*
* @param timeout Timeout as defined by select(2).
*
* @return SSH_SUCCESS operation successful\n
* SSH_EINTR select(2) syscall was interrupted, relaunch the function
*/ */
int channel_select(CHANNEL **readchans, CHANNEL **writechans, CHANNEL **exceptchans, struct int channel_select(CHANNEL **readchans, CHANNEL **writechans,
timeval * timeout){ CHANNEL **exceptchans, struct timeval * timeout) {
fd_set rset; CHANNEL **rchans, **wchans, **echans;
fd_set wset; CHANNEL *dummy = NULL;
fd_set eset; fd_set rset;
CHANNEL *dummy=NULL; fd_set wset;
CHANNEL **rchans, **wchans, **echans; fd_set eset;
int fdmax=-1; int fdmax = -1;
int i; int rc;
int r; int i;
/* don't allow NULL pointers */
if(!readchans) /* don't allow NULL pointers */
readchans=&dummy; if (readchans == NULL) {
if(!writechans) readchans = &dummy;
writechans=&dummy; }
if(!exceptchans)
exceptchans=&dummy; if (writechans == NULL) {
if(!readchans[0] && !writechans[0] && !exceptchans[0]){ writechans = &dummy;
/* no channel to poll ?? go away ! */ }
return 0;
} if (exceptchans == NULL) {
/* prepare the outgoing temporary arrays */ exceptchans = &dummy;
rchans = malloc(sizeof(CHANNEL *) * (count_ptrs(readchans) + 1)); }
if (rchans == NULL) {
return SSH_ERROR; if (readchans[0] == NULL && writechans[0] == NULL && exceptchans[0] == NULL) {
} /* No channel to poll?? Go away! */
wchans = malloc(sizeof(CHANNEL *) * (count_ptrs(writechans) + 1)); return 0;
if (wchans == NULL) { }
SAFE_FREE(rchans);
return SSH_ERROR; /* Prepare the outgoing temporary arrays */
} rchans = malloc(sizeof(CHANNEL *) * (count_ptrs(readchans) + 1));
echans = malloc(sizeof(CHANNEL *) * (count_ptrs(exceptchans) + 1)); if (rchans == NULL) {
if (echans == NULL) { return SSH_ERROR;
}
wchans = malloc(sizeof(CHANNEL *) * (count_ptrs(writechans) + 1));
if (wchans == NULL) {
SAFE_FREE(rchans);
return SSH_ERROR;
}
echans = malloc(sizeof(CHANNEL *) * (count_ptrs(exceptchans) + 1));
if (echans == NULL) {
SAFE_FREE(rchans);
SAFE_FREE(wchans);
return SSH_ERROR;
}
/*
* First, try without doing network stuff then, select and redo the
* networkless stuff
*/
do {
channel_protocol_select(readchans, writechans, exceptchans,
rchans, wchans, echans);
if (rchans[0] != NULL || wchans[0] != NULL || echans[0] != NULL) {
/* We've got one without doing any select overwrite the begining arrays */
memcpy(readchans, rchans, (count_ptrs(rchans) + 1) * sizeof(CHANNEL *));
memcpy(writechans, wchans, (count_ptrs(wchans) + 1) * sizeof(CHANNEL *));
memcpy(exceptchans, echans, (count_ptrs(echans) + 1) * sizeof(CHANNEL *));
SAFE_FREE(rchans); SAFE_FREE(rchans);
SAFE_FREE(wchans); SAFE_FREE(wchans);
return SSH_ERROR; SAFE_FREE(echans);
return 0;
}
/*
* Since we verified the invalid fd cases into the networkless select,
* we can be sure all fd are valid ones
*/
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_ZERO(&eset);
for (i = 0; readchans[i] != NULL; i++) {
if (!ssh_socket_fd_isset(readchans[i]->session->socket, &rset)) {
ssh_socket_fd_set(readchans[i]->session->socket, &rset, &fdmax);
}
} }
/* first, try without doing network stuff */ for (i = 0; writechans[i] != NULL; i++) {
/* then, select and redo the networkless stuff */ if (!ssh_socket_fd_isset(writechans[i]->session->socket, &wset)) {
do { ssh_socket_fd_set(writechans[i]->session->socket, &wset, &fdmax);
channel_protocol_select(readchans,writechans,exceptchans,rchans,wchans,echans); }
if(rchans[0]||wchans[0]||echans[0]){ }
/* we've got one without doing any select */
/* overwrite the begining arrays */
memcpy(readchans,rchans, (count_ptrs(rchans)+1)*sizeof(CHANNEL *));
memcpy(writechans,wchans, (count_ptrs(wchans)+1)*sizeof(CHANNEL *));
memcpy(exceptchans,echans, (count_ptrs(echans)+1)*sizeof(CHANNEL *));
free(rchans);
free(wchans);
free(echans);
return 0;
}
/* since we verified the invalid fd cases into the networkless select,
we can be sure all fd are valid ones */
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_ZERO(&eset);
for(i=0;readchans[i];++i){
if(!ssh_socket_fd_isset(readchans[i]->session->socket,&rset))
ssh_socket_fd_set(readchans[i]->session->socket, &rset, &fdmax);
}
for(i=0;writechans[i];++i){
if(!ssh_socket_fd_isset(writechans[i]->session->socket,&wset))
ssh_socket_fd_set(writechans[i]->session->socket,&wset, &fdmax);
}
for(i=0;exceptchans[i];++i){
if(!ssh_socket_fd_isset(exceptchans[i]->session->socket,&eset))
ssh_socket_fd_set(exceptchans[i]->session->socket,&eset,&fdmax);
}
/* fd=readchans[i]->session->fd; for (i = 0; exceptchans[i] != NULL; i++) {
if(!FD_ISSET(fd,&rset)){ if (!ssh_socket_fd_isset(exceptchans[i]->session->socket, &eset)) {
FD_SET(fd,&rset); ssh_socket_fd_set(exceptchans[i]->session->socket, &eset, &fdmax);
if(fd>=fdmax) }
fdmax=fd+1; }
}
} /* Here we go */
for(i=0;writechans[i];++i){ rc = select(fdmax, &rset, &wset, &eset, timeout);
fd=writechans[i]->session->fd; /* Leave if select was interrupted */
if(!FD_ISSET(fd,&wset)){ if (rc == EINTR) {
FD_SET(fd,&wset); SAFE_FREE(rchans);
if(fd>=fdmax) SAFE_FREE(wchans);
fdmax=fd+1; SAFE_FREE(echans);
} return SSH_EINTR;
} }
for(i=0;exceptchans[i];++i){ for (i = 0; readchans[i] != NULL; i++) {
fd=exceptchans[i]->session->fd; if (ssh_socket_fd_isset(readchans[i]->session->socket, &rset)) {
if(!FD_ISSET(fd,&eset)){ ssh_socket_set_toread(readchans[i]->session->socket);
FD_SET(fd,&eset); }
if(fd>=fdmax) }
fdmax=fd+1;
} for (i = 0; writechans[i] != NULL; i++) {
} if (ssh_socket_fd_isset(writechans[i]->session->socket, &wset)) {
*/ ssh_socket_set_towrite(writechans[i]->session->socket);
/* here we go */ }
r=select(fdmax,&rset,&wset,&eset,timeout); }
/* leave if select was interrupted */
if(r==EINTR){ for (i = 0; exceptchans[i] != NULL; i++) {
free(rchans); if (ssh_socket_fd_isset(exceptchans[i]->session->socket, &eset)) {
free(wchans); ssh_socket_set_except(exceptchans[i]->session->socket);
free(echans); }
return SSH_EINTR; }
} } while(1); /* Return to do loop */
for(i=0;readchans[i];++i){
if(ssh_socket_fd_isset(readchans[i]->session->socket,&rset)) /* not reached */
ssh_socket_set_toread(readchans[i]->session->socket); return 0;
}
for(i=0;writechans[i];++i){
if(ssh_socket_fd_isset(writechans[i]->session->socket,&wset))
ssh_socket_set_towrite(writechans[i]->session->socket);
}
for(i=0;exceptchans[i];++i){
if(ssh_socket_fd_isset(exceptchans[i]->session->socket,&eset))
ssh_socket_set_except(exceptchans[i]->session->socket);
}
} while(1); /* return to do loop */
/* not reached */
return 0;
} }
/** @} */ /** @} */