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);
}
Beispiel #4
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;
}
Beispiel #10
0
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);
}