END_TEST START_TEST (test_to_rb_from_fd) { int fds[2]; ck_assert(pipe(fds) == 0); ck_assert(write(fds[1], sample16, 16) == 16); ck_assert(rb_is_empty(rb)); ck_assert_int_eq(rb_used(rb), 0); ck_assert_int_eq(rb_space(rb), 16); ssize_t result = rb_read(rb, fds[0], 10); ck_assert_int_eq(result, 10); ck_assert_int_eq(rb_used(rb), 10); ck_assert_int_eq(rb_space(rb), 6); ck_assert(!rb_is_empty(rb)); ck_assert(!rb_is_full(rb)); result = rb_read(rb, fds[0], 10); ck_assert_int_eq(result, 6); ck_assert_int_eq(rb_used(rb), 16); ck_assert_int_eq(rb_space(rb), 0); ck_assert(!rb_is_empty(rb)); ck_assert(rb_is_full(rb)); ck_assert(memcmp(rb->buffer, "0123456789ABCDEF", 16) == 0); }
END_TEST START_TEST (test_to_fd_from_rb) { int fds[2]; ck_assert(pipe(fds) == 0); rb_append(rb, sample16, 16); ck_assert(rb_is_full(rb)); ssize_t result = rb_write(rb, fds[1], 10); ck_assert_int_eq(result, 10); ck_assert_int_eq(rb_used(rb), 6); ck_assert_int_eq(rb_space(rb), 10); char outbuf[32]; ck_assert_int_eq(read(fds[0], outbuf, sizeof(outbuf)), 10); ck_assert(memcmp(outbuf, "0123456789", 10) == 0); result = rb_write(rb, fds[1], 10); ck_assert_int_eq(result, 6); ck_assert(rb_is_empty(rb)); ck_assert_int_eq(read(fds[0], outbuf, sizeof(outbuf)), 6); ck_assert(memcmp(outbuf, "ABCDEF", 6) == 0); }
END_TEST START_TEST (test_to_scattered_memory_from_rb) { rb_append(rb, sample16, 4); rb_append(rb, sample16, 8); rb_discard(rb, 4); rb_append(rb, sample16+8, 8); char outbuf[32]; struct iovec iov[3]; iov[0].iov_base = outbuf+12; iov[0].iov_len = 2; iov[1].iov_base = outbuf+7; iov[1].iov_len = 5; iov[2].iov_base = outbuf; iov[2].iov_len = 7; size_t result = rb_extractv(rb, iov, 3); ck_assert_int_eq(result, 14); ck_assert_int_eq(rb_space(rb), 14); ck_assert(!rb_is_empty(rb)); ck_assert(!rb_is_full(rb)); ck_assert(memcmp(outbuf, "789ABCD2345601", 14) == 0); }
/* read from fd and write to buffer memory */ static ssize_t rb_read(struct ring_buffer *b, int fd) { ssize_t n = read(fd, b->wptr, rb_space(b)); if (n <= 0) return n; b->wptr += n; b->count += n; if (b->buf + b->size <= b->wptr) b->wptr = b->buf; return n; }
END_TEST START_TEST (test_to_rb_from_memory_in_center) { ck_assert_int_eq(rb_append(rb, sample16, 8), 8); ck_assert_int_eq(rb_discard(rb, 7), 7); ck_assert_int_eq(rb_append(rb, sample16, 11), 11); ck_assert_int_eq(rb_used(rb), 12); ck_assert_int_eq(rb_space(rb), 4); size_t result = rb_append(rb, sample16, 16); ck_assert_int_eq(result, 4); ck_assert(!rb_is_empty(rb)); ck_assert(rb_is_full(rb)); ck_assert(memcmp(rb->buffer, "89A0123701234567", 16) == 0); }
END_TEST START_TEST (test_to_memory_from_rb_wrapped_around) { rb_append(rb, sample16, 16); rb_discard(rb, 10); rb_append(rb, sample16, 6); ck_assert_int_eq(rb_space(rb), 4); char outbuf[32]; size_t result = rb_extract(rb, outbuf, 16); ck_assert_int_eq(result, 12); ck_assert(rb_is_empty(rb)); ck_assert(!rb_is_full(rb)); ck_assert(memcmp(outbuf, "ABCDEF012345", 12) == 0); }
END_TEST START_TEST (test_to_memory_from_rb) { rb_append(rb, sample16, 16); char outbuf[32]; size_t result = rb_extract(rb, outbuf, 10); ck_assert_int_eq(result, 10); ck_assert_int_eq(rb_space(rb), 10); ck_assert(!rb_is_empty(rb)); ck_assert(!rb_is_full(rb)); result = rb_extract(rb, outbuf + 10, 10); ck_assert_int_eq(result, 6); ck_assert(rb_is_empty(rb)); ck_assert(!rb_is_full(rb)); ck_assert(memcmp(outbuf, "0123456789ABCDEF", 16) == 0); }
ssize_t buffering_writev(neo4j_iostream_t *stream, const struct iovec *iov, unsigned int iovcnt) { struct buffering_iostream *ios = container_of(stream, struct buffering_iostream, _iostream); if (ios->delegate == NULL) { errno = EPIPE; return -1; } if (ios->sndbuf == NULL) { return neo4j_ios_writev(ios->delegate, iov, iovcnt); } if (iovcnt > IOV_MAX-2) { iovcnt = IOV_MAX-2; } size_t nbyte = iovlen(iov, iovcnt); if (nbyte <= rb_space(ios->sndbuf)) { return rb_appendv(ios->sndbuf, iov, iovcnt); } size_t buffered = rb_used(ios->sndbuf); ALLOC_IOVEC(diov, iovcnt+2); if (diov == NULL) { return -1; } unsigned int diovcnt = rb_data_iovec(ios->sndbuf, diov, rb_size(ios->sndbuf)); memcpy(diov+diovcnt, iov, iovcnt * sizeof(struct iovec)); ssize_t written = neo4j_ios_writev(ios->delegate, diov, diovcnt + iovcnt); if (written < 0) { goto cleanup; } if ((size_t)written < buffered) { rb_discard(ios->sndbuf, written); memcpy(diov, iov, iovcnt * sizeof(struct iovec)); diovcnt = iovcnt; written = 0; } else { rb_clear(ios->sndbuf); written -= buffered; assert((size_t)written <= nbyte); diovcnt = iov_skip(diov, iov, iovcnt, written); } if (diovcnt == 0) { written = nbyte; goto cleanup; } written += rb_appendv(ios->sndbuf, diov, diovcnt); int errsv; cleanup: errsv = errno; FREE_IOVEC(diov); errno = errsv; return written; }
ssize_t buffering_write(neo4j_iostream_t *stream, const void *buf, size_t nbyte) { struct buffering_iostream *ios = container_of(stream, struct buffering_iostream, _iostream); if (ios->delegate == NULL) { errno = EPIPE; return -1; } if (ios->sndbuf == NULL) { return neo4j_ios_write(ios->delegate, buf, nbyte); } if (nbyte > SSIZE_MAX) { nbyte = SSIZE_MAX; } if (nbyte <= rb_space(ios->sndbuf)) { return rb_append(ios->sndbuf, buf, nbyte); } size_t buffered = rb_used(ios->sndbuf); struct iovec iov[3]; unsigned int iovcnt = rb_data_iovec(ios->sndbuf, iov, rb_size(ios->sndbuf)); iov[iovcnt].iov_base = (uint8_t *)(intptr_t)buf; iov[iovcnt].iov_len = nbyte; ++iovcnt; ssize_t written = neo4j_ios_writev(ios->delegate, iov, iovcnt); if (written < 0) { return -1; } const uint8_t *rbytes; size_t remaining; if ((size_t)written < buffered) { rb_discard(ios->sndbuf, written); rbytes = buf; remaining = nbyte; written = 0; } else { rb_clear(ios->sndbuf); written -= buffered; assert((size_t)written <= nbyte); rbytes = (const uint8_t *)buf + written; remaining = nbyte - written; } if (remaining == 0) { return nbyte; } size_t appended = rb_append(ios->sndbuf, rbytes, remaining); return (size_t)written + appended; }
int main(int argc, char *argv[]) { pid_t child_pid; int child_exit_status; struct termios tty_attr; struct winsize window_size; int pty_master; /* for select */ fd_set readfds; fd_set writefds; unsigned err_n_rpty = 0; unsigned err_n_wpty = 0; unsigned err_n_stdin = 0; unsigned err_n_stdout = 0; int done = 0; /* the ring buffers */ char inbuf_mem[BUFSIZE]; char outbuf_mem[BUFSIZE]; struct ring_buffer inbuf; struct ring_buffer outbuf; rb_init(&inbuf, inbuf_mem, sizeof(inbuf_mem)); rb_init(&outbuf, outbuf_mem, sizeof(outbuf_mem)); if (argc == 1) { printf("usage: %s PROGRAM [ARGS]...\n", argv[0]); exit(1); } /* We need I/O calls to fail with EINTR on SIGCHLD... */ if (signal(SIGCHLD, sigchld_handler) == SIG_ERR) { perror("signal(SIGCHLD,...)"); exit(EX_OSERR); } if (isatty(STDIN_FILENO)) { /* get terminal parameters associated with stdout */ if (tcgetattr(STDOUT_FILENO, &tty_attr) < 0) { perror("tcgetattr(stdout,...)"); exit(EX_OSERR); } /* get window size */ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &window_size) < 0) { perror("ioctl(stdout,...)"); exit(1); } child_pid = forkpty(&pty_master, NULL, &tty_attr, &window_size); } else { /* not interactive */ child_pid = forkpty(&pty_master, NULL, NULL, NULL); } if (child_pid < 0) { perror("forkpty()"); exit(EX_OSERR); } if (child_pid == 0) { /* in the child */ struct termios s_tty_attr; if (tcgetattr(STDIN_FILENO, &s_tty_attr)) { perror("tcgetattr(stdin,...)"); exit(EXIT_FAILURE); } /* Turn off echo */ s_tty_attr.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); /* Also turn of NL to CR?LF on output */ s_tty_attr.c_oflag &= ~(ONLCR); if (tcsetattr(STDIN_FILENO, TCSANOW, &s_tty_attr)) { perror("tcsetattr(stdin,...)"); exit(EXIT_FAILURE); } if (execvp(argv[1], argv + 1)) { perror("execvp()"); exit(EXIT_FAILURE); } } /* Non blocking mode for all file descriptors. */ setfd_nonblock(pty_master); setfd_nonblock(STDIN_FILENO); setfd_nonblock(STDOUT_FILENO); if (isatty(STDIN_FILENO)) { if (tty_semi_raw(STDIN_FILENO) < 0) { perror("tty_semi_raw(stdin)"); } if (atexit(tty_atexit) < 0) { perror("atexit()"); } } do { /* Accept events only on fds, that we can handle now. */ int do_select = 0; FD_ZERO(&readfds); FD_ZERO(&writefds); if (rb_space(&outbuf) > 0 && err_n_rpty < MAXRETR) { FD_SET(pty_master, &readfds); do_select = 1; } if (!rb_isempty(&inbuf) && err_n_wpty < MAXRETR) { FD_SET(pty_master, &writefds); do_select = 1; } if (rb_space(&inbuf) > 0 && err_n_stdin < MAXRETR) { FD_SET(STDIN_FILENO, &readfds); do_select = 1; } if (!rb_isempty(&outbuf) && err_n_stdout < MAXRETR) { FD_SET(STDOUT_FILENO, &writefds); do_select = 1; } if (!do_select) { #ifdef DEBUG fprintf(stderr, "No I/O job for us, calling waitpid()...\n"); #endif while (waitpid(child_pid, &child_exit_status, 0) < 0) { /* nothing */ } break; } errno = 0; int select_rc = select(pty_master + 1, &readfds, &writefds, NULL, NULL); if (select_rc < 0 && errno != EINTR) { perror("select()"); exit(EX_IOERR); } #ifdef DEBUG fprintf(stderr, "select() returned %d\n", select_rc); #endif if (FD_ISSET(STDOUT_FILENO, &writefds)) { #ifdef DEBUG fprintf(stderr, "stdout can be written\n"); #endif ssize_t n = rb_write(&outbuf, STDOUT_FILENO); if (n <= 0 && n != EINTR && n != EAGAIN) err_n_stdout++; #ifdef DEBUG if (n >= 0) fprintf(stderr, "%d bytes written into stdout\n", n); else perror("write(stdout,...)"); #endif } if (FD_ISSET(pty_master, &writefds)) { #ifdef DEBUG fprintf(stderr, "pty_master can be written\n"); #endif ssize_t n = rb_write(&inbuf, pty_master); if (n <= 0 && n != EINTR && n != EAGAIN) err_n_wpty++; #ifdef DEBUG if (n >= 0) fprintf(stderr, "%d bytes written into pty_master\n", n); else perror("write(pty_master,...)"); #endif } if (FD_ISSET(STDIN_FILENO, &readfds)) { #ifdef DEBUG fprintf(stderr, "stdin can be read\n"); #endif ssize_t n = rb_read(&inbuf, STDIN_FILENO); if (n <= 0 && n != EINTR && n != EAGAIN) err_n_stdin++; #ifdef DEBUG if (n >= 0) fprintf(stderr, "%d bytes read from stdin\n", n); else perror("read(stdin,...)"); #endif } if (FD_ISSET(pty_master, &readfds)) { #ifdef DEBUG fprintf(stderr, "pty_master can be read\n"); #endif ssize_t n = rb_read(&outbuf, pty_master); if (n <= 0 && n != EINTR && n != EAGAIN) err_n_rpty++; #ifdef DEBUG if (n >= 0) fprintf(stderr, "%d bytes read from pty_master\n", n); else perror("read(pty_master,...)"); #endif } if (!done && waitpid(child_pid, &child_exit_status, WNOHANG) > 0) done = 1; } while (!done || !(rb_isempty(&inbuf) || err_n_wpty >= MAXRETR) || !(rb_isempty(&outbuf) || err_n_stdout >= MAXRETR)); #ifdef DEBUG fprintf(stderr, "inbuf: %u bytes left, outbuf: %u bytes left\n", inbuf.count, outbuf.count); fprintf(stderr, "err_n_rpty=%u, err_n_wpty=%u, err_n_stdin=%u, err_n_stdout=%u\n", err_n_rpty, err_n_wpty, err_n_stdin, err_n_stdout); #endif if (WIFEXITED(child_exit_status)) exit(WEXITSTATUS(child_exit_status)); else if (WIFSIGNALED(child_exit_status)) exit(128 + WTERMSIG(child_exit_status)); exit(EXIT_FAILURE); }