enum evcom_stream_state evcom_stream_state (evcom_stream *stream) { if (stream->recvfd < 0 && stream->sendfd && stream->flags == 0) { return EVCOM_INITIALIZED; } if (stream->recvfd < 0 && stream->sendfd < 0) return EVCOM_CLOSED; if (!CONNECTED(stream)) return EVCOM_CONNECTING; if (GOT_CLOSE(stream)) { if (READABLE(stream)) { return EVCOM_CONNECTED_RO; } else { return EVCOM_CLOSING; } } if (READABLE(stream) && WRITABLE(stream)) return EVCOM_CONNECTED_RW; if (WRITABLE(stream)) return EVCOM_CONNECTED_WO; if (READABLE(stream)) return EVCOM_CONNECTED_RO; return EVCOM_CLOSING; }
static int stream_recv__wait_for_close (evcom_stream *stream) { assert(!READABLE(stream)); if (!WRITABLE(stream)) { stream->recv_action = stream_recv__close; return OKAY; } ev_io_stop(D_LOOP_(stream) &stream->read_watcher); return AGAIN; }
int fix_gmode(sccs *s, int gflags) { ser_t d; /* * Do not fix mode of symlink target, the file may not be * under BK control. */ if (S_ISLNK(s->mode)) return (0); if ((gflags&GET_EDIT) && WRITABLE(s)) return (0); if (!(gflags&GET_EDIT) && !WRITABLE(s)) return (0); d = sccs_top(s); if (MODE(s, d)) s->mode = MODE(s, d); unless (gflags&GET_EDIT) { s->mode &= ~0222; /* turn off write mode */ } if (chmod(s->gfile, s->mode)) return (1); return (0); }
void evcom_stream_attach (EV_P_ evcom_stream *stream) { D_LOOP_SET(stream, EV_A); stream->flags |= EVCOM_ATTACHED; ev_timer_again(EV_A_ &stream->timeout_watcher); if (READABLE(stream)) { ev_io_start(EV_A_ &stream->read_watcher); } if (WRITABLE(stream)) { ev_io_start(EV_A_ &stream->write_watcher); } }
static int stream_send__data (evcom_stream *stream) { ssize_t sent; while (!evcom_queue_empty(&stream->out)) { assert(WRITABLE(stream)); evcom_queue *q = evcom_queue_last(&stream->out); evcom_buf *buf = evcom_queue_data(q, evcom_buf, queue); #if EVCOM_HAVE_GNUTLS if (SECURE(stream)) { sent = gnutls_record_send(stream->session, buf->base + buf->written, buf->len - buf->written); if (sent == GNUTLS_E_INTERRUPTED || sent == GNUTLS_E_AGAIN) { if (0 == gnutls_record_get_direction((stream)->session)) { fprintf(stderr, "(evcom) gnutls send: unexpected switch direction!\n"); ev_io_start(D_LOOP_(stream) &(stream)->read_watcher); ev_io_stop(D_LOOP_(stream) &(stream)->write_watcher); } return AGAIN; } if (gnutls_error_is_fatal(sent)) { stream->gnutls_errorno = sent; stream->send_action = stream_send__close; return OKAY; } } else #endif // EVCOM_HAVE_GNUTLS { /* TODO use writev() here? */ sent = nosigpipe_stream_send(stream, buf->base + buf->written, buf->len - buf->written); } if (sent <= 0) { switch (errno) { case EAGAIN: case EINTR: assert(stream->send_action == stream_send__data); return AGAIN; default: stream->errorno = errno; evcom_perror("send()", errno); /* pass through */ case EPIPE: stream->send_action = stream_send__close; return OKAY; } } ev_timer_again(D_LOOP_(stream) &stream->timeout_watcher); assert(sent >= 0); buf->written += sent; if (buf->written == buf->len) { evcom_queue_remove(q); if (buf->release) buf->release(buf); } } assert(evcom_queue_empty(&stream->out)); stream->send_action = stream_send__drain; return OKAY; }
static int stream_recv__data (evcom_stream *stream) { char buf[EVCOM_CHUNKSIZE]; size_t buf_size = EVCOM_CHUNKSIZE; ssize_t recved; while (READABLE(stream)) { assert(CONNECTED(stream)); if (PAUSED(stream)) { stream->recv_action = stream_recv__wait_for_resume; return OKAY; } #if EVCOM_HAVE_GNUTLS if (SECURE(stream)) { recved = gnutls_record_recv(stream->session, buf, buf_size); if (gnutls_error_is_fatal(recved)) { stream->gnutls_errorno = recved; stream->recv_action = stream_recv__close; return OKAY; } if (recved == GNUTLS_E_INTERRUPTED || recved == GNUTLS_E_AGAIN) { if (1 == gnutls_record_get_direction((stream)->session)) { fprintf(stderr, "(evcom) gnutls recv: unexpected switch direction!\n"); ev_io_stop(D_LOOP_(stream) &(stream)->read_watcher); ev_io_start(D_LOOP_(stream) &(stream)->write_watcher); } return AGAIN; } /* A server may also receive GNUTLS_E_REHANDSHAKE when a client has * initiated a andshake. In that case the server can only initiate a * handshake or terminate the connection. */ if (recved == GNUTLS_E_REHANDSHAKE) { assert(WRITABLE(stream)); stream->recv_action = stream__handshake; stream->send_action = stream__handshake; return OKAY; } } else #endif /* EVCOM_HAVE_GNUTLS */ { recved = read(stream->recvfd, buf, buf_size); } if (recved < 0) { if (errno == EAGAIN || errno == EINTR) { assert(stream->recv_action == stream_recv__data); return AGAIN; } if (errno != ECONNRESET) { evcom_perror("recv()", stream->errorno); } stream->errorno = errno; stream->recv_action = stream_recv__close; return OKAY; } ev_timer_again(D_LOOP_(stream) &stream->timeout_watcher); assert(recved >= 0); if (recved == 0) { stream->flags &= ~EVCOM_READABLE; ev_io_stop(D_LOOP_(stream) &stream->read_watcher); stream->recv_action = stream_recv__wait_for_close; } /* NOTE: EOF is signaled with recved == 0 on callback */ if (stream->on_read) stream->on_read(stream, buf, recved); if (recved == 0) { return OKAY; } } return AGAIN; }
/* Returns the number of bytes flushed to the buffer */ ssize_t evcom_stream_write (evcom_stream *stream, const char *str, size_t len) { if (!WRITABLE(stream) || GOT_CLOSE(stream)) { assert(0 && "Do not write to a closed stream"); return -1; } ssize_t sent = 0; if ( stream->send_action == stream_send__wait_for_buf && evcom_queue_empty(&stream->out) ) { assert(CONNECTED(stream)); #if EVCOM_HAVE_GNUTLS if (SECURE(stream)) { sent = gnutls_record_send(stream->session, str, len); if (gnutls_error_is_fatal(sent)) { stream->gnutls_errorno = sent; goto close; } } else #endif // EVCOM_HAVE_GNUTLS { /* TODO use writev() here? */ sent = nosigpipe_stream_send(stream, str, len); } if (sent < 0) { switch (errno) { case EPIPE: goto close; case EINTR: case EAGAIN: sent = 0; break; default: stream->errorno = errno; evcom_perror("send()", stream->errorno); goto close; } } } /* TODO else { memcpy to last buffer on head } */ assert(sent >= 0); if ((size_t)sent == len) return sent; /* sent the whole buffer */ len -= sent; str += sent; evcom_buf *b = evcom_buf_new(str, len); evcom_queue_insert_head(&stream->out, &b->queue); b->written = 0; assert(stream->sendfd >= 0); if (ATTACHED(stream)) { ev_io_start(D_LOOP_(stream) &stream->write_watcher); } return sent; close: stream->send_action = stream_send__close; stream->recv_action = stream_recv__close; if (ATTACHED(stream)) { ev_io_start(D_LOOP_(stream) &stream->write_watcher); } return -1; }