comparison common-channel.c @ 375:8d149b812669 channel-fix

- Add some extra tracing. - Be clearer about errfd be used for read versus write with ERRFD_IS_READ and ERRFD_IS_WRITE macros
author Matt Johnston <matt@ucc.asn.au>
date Tue, 05 Dec 2006 14:42:03 +0000
parents 49fcc9875045
children 4f2dbd1c3685
comparison
equal deleted inserted replaced
374:87ae4565679d 375:8d149b812669
54 static void close_chan_fd(struct Channel *channel, int fd, int how); 54 static void close_chan_fd(struct Channel *channel, int fd, int how);
55 55
56 #define FD_UNINIT (-2) 56 #define FD_UNINIT (-2)
57 #define FD_CLOSED (-1) 57 #define FD_CLOSED (-1)
58 58
59 #define ERRFD_IS_READ(channel) ((channel)->extrabuf == NULL)
60 #define ERRFD_IS_WRITE(channel) (!ERRFD_IS_READ(channel))
61
59 /* Initialise all the channels */ 62 /* Initialise all the channels */
60 void chaninitialise(const struct ChanType *chantypes[]) { 63 void chaninitialise(const struct ChanType *chantypes[]) {
61 64
62 /* may as well create space for a single channel */ 65 /* may as well create space for a single channel */
63 ses.channels = (struct Channel**)m_malloc(sizeof(struct Channel*)); 66 ses.channels = (struct Channel**)m_malloc(sizeof(struct Channel*));
202 if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) { 205 if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) {
203 send_msg_channel_data(channel, 0); 206 send_msg_channel_data(channel, 0);
204 } 207 }
205 208
206 /* read stderr data and send it over the wire */ 209 /* read stderr data and send it over the wire */
207 if (channel->extrabuf == NULL && 210 if (ERRFD_IS_READ(channel) &&
208 channel->errfd >= 0 && FD_ISSET(channel->errfd, readfds)) { 211 channel->errfd >= 0 && FD_ISSET(channel->errfd, readfds)) {
209 send_msg_channel_data(channel, 1); 212 send_msg_channel_data(channel, 1);
210 } 213 }
211 214
212 /* write to program/pipe stdin */ 215 /* write to program/pipe stdin */
219 } 222 }
220 writechannel(channel, channel->writefd, channel->writebuf); 223 writechannel(channel, channel->writefd, channel->writebuf);
221 } 224 }
222 225
223 /* stderr for client mode */ 226 /* stderr for client mode */
224 if (channel->extrabuf != NULL 227 if (ERRFD_IS_WRITE(channel)
225 && channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) { 228 && channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) {
226 writechannel(channel, channel->errfd, channel->extrabuf); 229 writechannel(channel, channel->errfd, channel->extrabuf);
227 } 230 }
228 231
229 /* handle any channel closing etc */ 232 /* handle any channel closing etc */
243 static unsigned int write_pending(struct Channel * channel) { 246 static unsigned int write_pending(struct Channel * channel) {
244 247
245 if (channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0) { 248 if (channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0) {
246 return 1; 249 return 1;
247 } else if (channel->errfd >= 0 && channel->extrabuf && 250 } else if (channel->errfd >= 0 && channel->extrabuf &&
248 cbuf_getused(channel->writebuf) > 0) { 251 cbuf_getused(channel->extrabuf) > 0) {
249 return 1; 252 return 1;
250 } 253 }
251 return 0; 254 return 0;
252 } 255 }
253 256
256 static void check_close(struct Channel *channel) { 259 static void check_close(struct Channel *channel) {
257 260
258 TRACE(("check_close: writefd %d, readfd %d, errfd %d, sent_close %d, recv_close %d", 261 TRACE(("check_close: writefd %d, readfd %d, errfd %d, sent_close %d, recv_close %d",
259 channel->writefd, channel->readfd, 262 channel->writefd, channel->readfd,
260 channel->errfd, channel->sent_close, channel->recv_close)) 263 channel->errfd, channel->sent_close, channel->recv_close))
261 TRACE(("writebuf size %d extrabuf ptr 0x%x extrabuf size %d", 264 TRACE(("writebuf size %d extrabuf size %d",
262 cbuf_getused(channel->writebuf), 265 cbuf_getused(channel->writebuf),
263 channel->writebuf, 266 channel->extrabuf ? cbuf_getused(channel->extrabuf) : 0))
264 channel->writebuf ? 0 : cbuf_getused(channel->extrabuf)))
265
266 /* A bit of a hack for closing up server session channels */
267 if (channel->writefd >= 0
268 && channel->type->check_close
269 && channel->type->check_close(channel)) {
270 TRACE(("channel->type->check_close got hit"))
271 close_chan_fd(channel, channel->writefd, SHUT_WR);
272 }
273 267
274 if (channel->recv_close && !write_pending(channel)) { 268 if (channel->recv_close && !write_pending(channel)) {
275 if (! channel->sent_close) { 269 if (! channel->sent_close) {
276 TRACE(("Sending MSG_CHANNEL_CLOSE in response to same.")) 270 TRACE(("Sending MSG_CHANNEL_CLOSE in response to same."))
277 send_msg_channel_close(channel); 271 send_msg_channel_close(channel);
282 276
283 if (channel->recv_eof && !write_pending(channel)) { 277 if (channel->recv_eof && !write_pending(channel)) {
284 close_chan_fd(channel, channel->writefd, SHUT_WR); 278 close_chan_fd(channel, channel->writefd, SHUT_WR);
285 } 279 }
286 280
281 /* If we're not going to send any more data, send EOF */
287 if (!channel->sent_eof 282 if (!channel->sent_eof
288 && channel->readfd == FD_CLOSED 283 && channel->readfd == FD_CLOSED
289 && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) { 284 && (ERRFD_IS_WRITE(channel) || channel->errfd == FD_CLOSED)) {
290 send_msg_channel_eof(channel); 285 send_msg_channel_eof(channel);
291 } 286 }
292 287
288 /* And if we can't receive any more data from them either, close up */
293 if (!channel->sent_close 289 if (!channel->sent_close
294 && channel->writefd == FD_CLOSED 290 && channel->writefd == FD_CLOSED
295 && channel->readfd == FD_CLOSED 291 && channel->readfd == FD_CLOSED
296 && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) { 292 && channel->errfd == FD_CLOSED) {
297 send_msg_channel_close(channel); 293 send_msg_channel_close(channel);
298 } 294 }
299 295
300 } 296 }
301 297
369 * possible */ 365 * possible */
370 static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf) { 366 static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf) {
371 367
372 int len, maxlen; 368 int len, maxlen;
373 369
374 TRACE(("enter writechannel")) 370 TRACE(("enter writechannel fd %d", fd))
375 371
376 maxlen = cbuf_readlen(cbuf); 372 maxlen = cbuf_readlen(cbuf);
377 373
378 /* Write the data out */ 374 /* Write the data out */
379 len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen); 375 len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen);
380 if (len <= 0) { 376 if (len <= 0) {
377 TRACE(("errno %d len %d", errno, len))
381 if (len < 0 && errno != EINTR) { 378 if (len < 0 && errno != EINTR) {
382 close_chan_fd(channel, fd, SHUT_WR); 379 close_chan_fd(channel, fd, SHUT_WR);
383 } 380 }
384 TRACE(("leave writechannel: len <= 0")) 381 TRACE(("leave writechannel: len <= 0"))
385 return; 382 return;
386 } 383 }
384 TRACE(("writechannel wrote %d", len))
387 385
388 cbuf_incrread(cbuf, len); 386 cbuf_incrread(cbuf, len);
389 channel->recvdonelen += len; 387 channel->recvdonelen += len;
390 388
391 /* Window adjust handling */ 389 /* Window adjust handling */
423 421
424 if (channel->readfd >= 0) { 422 if (channel->readfd >= 0) {
425 FD_SET(channel->readfd, readfds); 423 FD_SET(channel->readfd, readfds);
426 } 424 }
427 425
428 if (channel->extrabuf == NULL && channel->errfd >= 0) { 426 if (ERRFD_IS_READ(channel) && channel->errfd >= 0) {
429 FD_SET(channel->errfd, readfds); 427 FD_SET(channel->errfd, readfds);
430 } 428 }
431 } 429 }
432 430
433 /* Stuff from the wire */ 431 /* Stuff from the wire */
434 if ((channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0 ) 432 if ((channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0 )
435 || channel->initconn) { 433 || channel->initconn) {
436 FD_SET(channel->writefd, writefds); 434 FD_SET(channel->writefd, writefds);
437 } 435 }
438 436
439 if (channel->extrabuf != NULL && channel->errfd >= 0 437 if (ERRFD_IS_WRITE(channel) != NULL && channel->errfd >= 0
440 && cbuf_getused(channel->extrabuf) > 0 ) { 438 && cbuf_getused(channel->extrabuf) > 0 ) {
441 FD_SET(channel->errfd, writefds); 439 FD_SET(channel->errfd, writefds);
442 } 440 }
443 441
444 } /* foreach channel */ 442 } /* foreach channel */
499 } 497 }
500 498
501 499
502 /* close the FDs in case they haven't been done 500 /* close the FDs in case they haven't been done
503 * yet (they might have been shutdown etc) */ 501 * yet (they might have been shutdown etc) */
502 TRACE(("CLOSE writefd %d", channel->writefd))
504 close(channel->writefd); 503 close(channel->writefd);
504 TRACE(("CLOSE readfd %d", channel->readfd))
505 close(channel->readfd); 505 close(channel->readfd);
506 TRACE(("CLOSE errfd %d", channel->errfd))
506 close(channel->errfd); 507 close(channel->errfd);
507 508
508 channel->typedata = NULL; 509 channel->typedata = NULL;
509 510
510 delete_channel(channel); 511 delete_channel(channel);
560 if (isextended) { 561 if (isextended) {
561 fd = channel->errfd; 562 fd = channel->errfd;
562 } else { 563 } else {
563 fd = channel->readfd; 564 fd = channel->readfd;
564 } 565 }
566 TRACE(("enter send_msg_channel_data isextended %d fd %d", isextended, fd))
565 dropbear_assert(fd >= 0); 567 dropbear_assert(fd >= 0);
566 568
567 maxlen = MIN(channel->transwindow, channel->transmaxpacket); 569 maxlen = MIN(channel->transwindow, channel->transmaxpacket);
568 /* -(1+4+4) is SSH_MSG_CHANNEL_DATA, channel number, string length, and 570 /* -(1+4+4) is SSH_MSG_CHANNEL_DATA, channel number, string length, and
569 * exttype if is extended */ 571 * exttype if is extended */
590 if (len <= 0) { 592 if (len <= 0) {
591 if (len == 0 || errno != EINTR) { 593 if (len == 0 || errno != EINTR) {
592 close_chan_fd(channel, fd, SHUT_RD); 594 close_chan_fd(channel, fd, SHUT_RD);
593 } 595 }
594 ses.writepayload->len = ses.writepayload->pos = 0; 596 ses.writepayload->len = ses.writepayload->pos = 0;
595 TRACE(("leave send_msg_channel_data: read err or EOF for fd %d", 597 TRACE(("leave send_msg_channel_data: len %d read err or EOF for fd %d",
596 channel->index)); 598 len, channel->index));
597 return; 599 return;
598 } 600 }
599 buf_incrwritepos(ses.writepayload, len); 601 buf_incrwritepos(ses.writepayload, len);
600 /* ... real size here */ 602 /* ... real size here */
601 buf_setpos(ses.writepayload, size_pos); 603 buf_setpos(ses.writepayload, size_pos);
738 } 740 }
739 741
740 /* Get the channel type. Client and server style invokation will set up a 742 /* Get the channel type. Client and server style invokation will set up a
741 * different list for ses.chantypes at startup. We just iterate through 743 * different list for ses.chantypes at startup. We just iterate through
742 * this list and find the matching name */ 744 * this list and find the matching name */
743 /* XXX fugly */
744 for (cp = &ses.chantypes[0], chantype = (*cp); 745 for (cp = &ses.chantypes[0], chantype = (*cp);
745 chantype != NULL; 746 chantype != NULL;
746 cp++, chantype = (*cp)) { 747 cp++, chantype = (*cp)) {
747 if (strcmp(type, chantype->name) == 0) { 748 if (strcmp(type, chantype->name) == 0) {
748 break; 749 break;
860 static void close_chan_fd(struct Channel *channel, int fd, int how) { 861 static void close_chan_fd(struct Channel *channel, int fd, int how) {
861 862
862 int closein = 0, closeout = 0; 863 int closein = 0, closeout = 0;
863 864
864 if (channel->type->sepfds) { 865 if (channel->type->sepfds) {
865 TRACE(("shutdown((%d), %d)", fd, how)) 866 TRACE(("SHUTDOWN(%d, %d)", fd, how))
866 shutdown(fd, how); 867 shutdown(fd, how);
867 if (how == 0) { 868 if (how == 0) {
868 closeout = 1; 869 closeout = 1;
869 } else { 870 } else {
870 closein = 1; 871 closein = 1;
871 } 872 }
872 } else { 873 } else {
874 TRACE(("CLOSE some fd %d", fd))
873 close(fd); 875 close(fd);
874 closein = closeout = 1; 876 closein = closeout = 1;
875 } 877 }
876 878
877 if (closeout && fd == channel->readfd) { 879 if (closeout && (fd == channel->readfd)) {
878 channel->readfd = FD_CLOSED; 880 channel->readfd = FD_CLOSED;
879 } 881 }
880 if (closeout && (channel->extrabuf == NULL) && (fd == channel->errfd)) { 882 if (closeout && ERRFD_IS_READ(channel) && (fd == channel->errfd)) {
881 channel->errfd = FD_CLOSED; 883 channel->errfd = FD_CLOSED;
882 } 884 }
883 885
884 if (closein && fd == channel->writefd) { 886 if (closein && fd == channel->writefd) {
885 channel->writefd = FD_CLOSED; 887 channel->writefd = FD_CLOSED;
886 } 888 }
887 if (closein && (channel->extrabuf != NULL) && (fd == channel->errfd)) { 889 if (closein && ERRFD_IS_WRITE(channel) && (fd == channel->errfd)) {
888 channel->errfd = FD_CLOSED; 890 channel->errfd = FD_CLOSED;
889 } 891 }
890 892
891 /* if we called shutdown on it and all references are gone, then we 893 /* if we called shutdown on it and all references are gone, then we
892 * need to close() it to stop it lingering */ 894 * need to close() it to stop it lingering */
893 if (channel->type->sepfds && channel->readfd == FD_CLOSED 895 if (channel->type->sepfds && channel->readfd == FD_CLOSED
894 && channel->writefd == FD_CLOSED && channel->errfd == FD_CLOSED) { 896 && channel->writefd == FD_CLOSED && channel->errfd == FD_CLOSED) {
897 TRACE(("CLOSE (finally) of %d", fd))
895 close(fd); 898 close(fd);
896 } 899 }
897 } 900 }
898 901
899 902