+static void
+chan_write_failed2(Channel *c)
+{
+ debug("channel %d: write failed", c->self);
+ switch (c->ostate) {
+ case CHAN_OUTPUT_OPEN:
+ debug("channel %d: output open -> closed", c->self);
+ chan_shutdown_write(c); /* ?? */
+ c->ostate = CHAN_OUTPUT_CLOSED;
+ break;
+ case CHAN_OUTPUT_WAIT_DRAIN:
+ debug("channel %d: output drain -> closed", c->self);
+ chan_shutdown_write(c);
+ c->ostate = CHAN_OUTPUT_CLOSED;
+ break;
+ default:
+ error("channel %d: chan_write_failed for ostate %d",
+ c->self, c->ostate);
+ break;
+ }
+}
+static void
+chan_obuf_empty2(Channel *c)
+{
+ debug("channel %d: obuf empty", c->self);
+ if (buffer_len(&c->output)) {
+ error("channel %d: chan_obuf_empty for non empty buffer",
+ c->self);
+ return;
+ }
+ switch (c->ostate) {
+ case CHAN_OUTPUT_WAIT_DRAIN:
+ debug("channel %d: output drain -> closed", c->self);
+ chan_shutdown_write(c);
+ c->ostate = CHAN_OUTPUT_CLOSED;
+ break;
+ default:
+ error("channel %d: chan_obuf_empty for ostate %d",
+ c->self, c->ostate);
+ break;
+ }
+}
+static void
+chan_send_eof2(Channel *c)
+{
+ debug("channel %d: send eof", c->self);
+ switch (c->istate) {
+ case CHAN_INPUT_WAIT_DRAIN:
+ packet_start(SSH2_MSG_CHANNEL_EOF);
+ packet_put_int(c->remote_id);
+ packet_send();
+ break;
+ default:
+ error("channel %d: cannot send eof for istate %d",
+ c->self, c->istate);
+ break;
+ }
+}
+static void
+chan_send_close2(Channel *c)
+{
+ debug("channel %d: send close", c->self);
+ if (c->ostate != CHAN_OUTPUT_CLOSED ||
+ c->istate != CHAN_INPUT_CLOSED) {
+ error("channel %d: cannot send close for istate/ostate %d/%d",
+ c->self, c->istate, c->ostate);
+ } else if (c->flags & CHAN_CLOSE_SENT) {
+ error("channel %d: already sent close", c->self);
+ } else {
+ packet_start(SSH2_MSG_CHANNEL_CLOSE);
+ packet_put_int(c->remote_id);
+ packet_send();
+ c->flags |= CHAN_CLOSE_SENT;
+ }
+}
+
+/* shared */
+
+void
+chan_mark_dead(Channel *c)
+{
+ c->type = SSH_CHANNEL_ZOMBIE;
+}
+
+int
+chan_is_dead(Channel *c, int send)
+{
+ if (c->type == SSH_CHANNEL_ZOMBIE) {
+ debug("channel %d: zombie", c->self);
+ return 1;
+ }
+ if (c->istate != CHAN_INPUT_CLOSED || c->ostate != CHAN_OUTPUT_CLOSED)
+ return 0;
+ if (!compat20) {
+ debug("channel %d: is dead", c->self);
+ return 1;
+ }
+ /*
+ * we have to delay the close message if the efd (for stderr) is
+ * still active
+ */
+ if (((c->extended_usage != CHAN_EXTENDED_IGNORE) &&
+ buffer_len(&c->extended) > 0)
+#if 0
+ || ((c->extended_usage == CHAN_EXTENDED_READ) &&
+ c->efd != -1)
+#endif
+ ) {
+ debug2("channel %d: active efd: %d len %d type %s",
+ c->self, c->efd, buffer_len(&c->extended),
+ c->extended_usage==CHAN_EXTENDED_READ ?
+ "read": "write");
+ } else {
+ if (!(c->flags & CHAN_CLOSE_SENT)) {
+ if (send) {
+ chan_send_close2(c);
+ } else {
+ /* channel would be dead if we sent a close */
+ if (c->flags & CHAN_CLOSE_RCVD) {
+ debug("channel %d: almost dead",
+ c->self);
+ return 1;
+ }
+ }
+ }
+ if ((c->flags & CHAN_CLOSE_SENT) &&
+ (c->flags & CHAN_CLOSE_RCVD)) {
+ debug("channel %d: is dead", c->self);
+ return 1;
+ }
+ }
+ return 0;
+}
+