#include #include #include #include #include #include #include #include "buffio.h" #include "main.h" #define BUFSIZE 10240 static void* buffio_read_cb(oop_source *source, int fd, oop_event event, void *user); static void* buffio_write_cb(oop_source *source, int fd, oop_event event, void *user); static void buffio_set_input_callbacks(struct buffio *b); static void buffio_set_output_callbacks(struct buffio *b); struct buffio* buffio_new(int ifd, int ofd) { struct buffio *b; assert(b >= 0); b = malloc(sizeof(struct buffio)); assert(b); memset(b, 0, sizeof(struct buffio)); b->ifd = ifd; b->ofd = ofd; b->input_buf = malloc(b->input_max_length = BUFSIZE); assert(b->input_buf); b->input_length = b->input_index = 0; b->input_watermark = b->input_max_length; /* Read some more data if possible */ b->output_buf = malloc(b->output_max_length = BUFSIZE); assert(b->output_buf); b->output_length = b->output_index = 0; b->output_watermark = 1; /* Write some data if 1 or more bytes are in the output buffer */ buffio_set_input_callbacks(b); buffio_set_output_callbacks(b); return b; } static void buffio_close_fd(struct buffio *b) { assert(b); if (b->b_read_cb) event_source->cancel_fd(event_source, b->ifd, OOP_READ); if (b->b_write_cb) event_source->cancel_fd(event_source, b->ofd, OOP_WRITE); if (b->ifd >= 0) close(b->ifd); if (b->ofd >= 0 && b->ofd != b->ifd) close(b->ofd); b->b_read_cb = b->b_write_cb = 0; b->ifd = b->ofd = -1; } void buffio_free(struct buffio *b) { buffio_close_fd(b); free(b->output_buf); free(b->input_buf); free(b); } #ifdef IODEBUG static void esc_print(const char*c, const uint8_t *b, size_t l) { size_t i; fprintf(stderr, "%s", c); for (i = 0; i < l; i++, b++) fputc(*b < 32 ? '.' : *b, stderr); fprintf(stderr, "\n"); }; #endif static void buffio_normalize(struct buffio *b) { assert(b); assert(b->input_index < b->input_max_length); assert(b->input_length <= b->input_max_length); if (!b->input_length) /* This will optimize throughput a bit */ b->input_index = 0; if (!b->output_length) /* This will optimize throughput a bit */ b->output_index = 0; } static void buffio_set_input_callbacks(struct buffio *b) { assert(b && b->ifd >= 0 && event_source); if (b->input_length <= b->input_watermark) { if (!b->b_read_cb) { /* Enable the callback */ event_source->on_fd(event_source, b->ifd, OOP_READ, buffio_read_cb, b); b->b_read_cb = 1; } } else { if (b->b_read_cb) { /* Disable the callback */ event_source->cancel_fd(event_source, b->ifd, OOP_READ); b->b_read_cb = 0; } } } static void buffio_set_output_callbacks(struct buffio *b) { assert(b && b->ofd >= 0 && event_source); if (b->output_length >= b->output_watermark) { if (!b->b_write_cb) { /* Enable the callback */ event_source->on_fd(event_source, b->ofd, OOP_WRITE, buffio_write_cb, b); b->b_write_cb = 1; } } else { if (b->b_write_cb) { /* Disable the callback */ event_source->cancel_fd(event_source, b->ofd, OOP_WRITE); b->b_write_cb = 0; } } } static void* buffio_read_cb(oop_source *source, int fd, oop_event event, void *user) { struct buffio *b = user; assert(source && b && b->ifd == fd && event == OOP_READ); if (b->input_length < b->input_max_length) { ssize_t s; size_t m, i; i = (b->input_index + b->input_length) % b->input_max_length; m = b->input_max_length-b->input_length; if (m > b->input_max_length-i) m = b->input_max_length-i; if ((s = read(fd, b->input_buf+i, m)) < 0) { daemon_log(LOG_ERR, "Failed to read from file descriptor: %s", strerror(errno)); return OOP_HALT; } #ifdef IODEBUG esc_print("INPUT: ", b->input_buf+i, s); #endif if (!s) { buffio_close_fd(b); if (b->eof_cb) b->eof_cb(b, b->user); return OOP_CONTINUE; } assert(s <= m); b->input_length += s; } buffio_normalize(b); buffio_set_input_callbacks(b); if (b->input_length && b->input_ready_cb) b->input_ready_cb(b, b->user); return OOP_CONTINUE; } static void* buffio_write_cb(oop_source *source, int fd, oop_event event, void *user) { struct buffio *b = user; assert(source && b && b->ofd == fd && event == OOP_WRITE); if (b->output_length > 0) { ssize_t s; size_t m; m = b->output_length; if (m > b->output_max_length-b->output_index) m = b->output_max_length-b->output_index; if ((s = write(fd, b->output_buf+b->output_index, m)) < 0) { daemon_log(LOG_ERR, "Failed to write to file descriptor: %s", strerror(errno)); return OOP_HALT; } #ifdef IODEBUG esc_print("OUTPUT: ", b->output_buf+b->output_index, s); #endif if (!s) { buffio_close_fd(b); if (b->eof_cb) b->eof_cb(b, b->user); return OOP_CONTINUE; } assert(s <= m); b->output_index = (b->output_index + s) % b->output_max_length; b->output_length -= s; } buffio_normalize(b); buffio_set_output_callbacks(b); if (!b->output_length && b->output_empty_cb) b->output_empty_cb(b, b->user); return OOP_CONTINUE; } int buffio_write(struct buffio *b, const uint8_t *d, size_t l) { assert(b && d && l); if (l > b->output_max_length - b->output_length) { daemon_log(LOG_ERR, "buffio_write() with too much data called"); return -1; } while (l > 0) { size_t m, i; i = (b->output_index + b->output_length) % b->output_max_length; m = l; if (m > b->output_max_length-b->output_length) m = b->output_max_length-b->output_length; if (m > b->output_max_length-i) m = b->output_max_length-i; memcpy(b->output_buf+i, d, m); l -= m; b->output_length += m; } buffio_set_output_callbacks(b); buffio_normalize(b); return 0; } int buffio_print(struct buffio *b, const char *s) { assert(b && s); return buffio_write(b, (uint8_t*) s, strlen(s)); } int buffio_command(struct buffio *b, const char *c) { assert(b && c); buffio_flush_input(b); return buffio_print(b, c); } void buffio_flush_input(struct buffio *b) { assert(b); b->input_length = b->input_index = 0; buffio_set_input_callbacks(b); } int buffio_find_input(struct buffio *b, const char *c) { size_t l, cl, i; assert(b && c && *c); cl = strlen(c); for (i = b->input_index, l = b->input_length; l >= cl; i = (i+1) % b->input_max_length, l--) { const char *p; size_t j; for (j = i, p = c; *p && *p == b->input_buf[j]; p++, j = (j+1) % b->input_max_length); if (!*p) { /* Found! */ b->input_index = j; b->input_length = l-cl; buffio_normalize(b); buffio_set_input_callbacks(b); return 1; } } return 0; } char *buffio_read_line(struct buffio *b, char *c, size_t l) { size_t i; assert(b && c && l); /* Look for a \n */ for (i = 0; i < b->input_length && i < l-1; i++) { if (b->input_buf[(b->input_index+i) % b->input_max_length] == '\n') { size_t j; /* Once again, now copy */ for (j = 0;; j++) { c[j] = b->input_buf[(b->input_index+j) % b->input_max_length]; /* Finished? */ if (c[j] == '\n') { c[j+1] = 0; b->input_index = (b->input_index+j+1) % b->input_max_length; b->input_length -= j+1; buffio_set_input_callbacks(b); buffio_normalize(b); return c; } } } } return NULL; } void buffio_dump(struct buffio *b) { assert(b); size_t i; fprintf(stderr, "%p INPUT_BUFFER: [", b); for (i = 0; i < b->input_length; i++) { char c = b->input_buf[(i + b->input_index) % b->input_max_length]; fputc(c < 32 ? '.' : c, stderr); } fprintf(stderr, "]\n"); fprintf(stderr, "%p OUTPUT_BUFFER: [", b); for (i = 0; i < b->output_length; i++) { char c = b->output_buf[(i + b->output_index) % b->output_max_length]; fputc(c < 32 ? '.' : c, stderr); } fprintf(stderr, "]\n"); }