diff options
-rw-r--r-- | cpukit/Makefile.am | 2 | ||||
-rw-r--r-- | cpukit/libmisc/Makefile.am | 4 | ||||
-rw-r--r-- | cpukit/libmisc/redirector/stdio-redirect.c | 333 | ||||
-rw-r--r-- | cpukit/libmisc/redirector/stdio-redirect.h | 115 | ||||
-rw-r--r-- | cpukit/preinstall.am | 4 |
5 files changed, 458 insertions, 0 deletions
diff --git a/cpukit/Makefile.am b/cpukit/Makefile.am index 2693d46f68..93b07c8a5d 100644 --- a/cpukit/Makefile.am +++ b/cpukit/Makefile.am @@ -175,6 +175,8 @@ include_rtems_HEADERS += libmisc/mouse/serial_mouse.h ## libqos include_rtems_HEADERS += libqos/qreslib.h +## redirector +include_rtems_HEADERS += libmisc/redirector/stdio-redirect.h ## shell if LIBSHELL include_rtems_HEADERS += libmisc/shell/shell.h diff --git a/cpukit/libmisc/Makefile.am b/cpukit/libmisc/Makefile.am index bcf8ff018f..1091109748 100644 --- a/cpukit/libmisc/Makefile.am +++ b/cpukit/libmisc/Makefile.am @@ -71,6 +71,10 @@ noinst_LIBRARIES += libmouse.a libmouse_a_SOURCES = mouse/mouse_parser.c mouse/serial_mouse.c EXTRA_DIST += mouse/README +## redirector +noinst_LIBRARIES += libredirector.a +libredirector_a_SOURCES = redirector/stdio-redirect.c redirector/stdio-redirect.h + ## shell if LIBSHELL noinst_LIBRARIES += libshell.a diff --git a/cpukit/libmisc/redirector/stdio-redirect.c b/cpukit/libmisc/redirector/stdio-redirect.c new file mode 100644 index 0000000000..e6610cbba1 --- /dev/null +++ b/cpukit/libmisc/redirector/stdio-redirect.c @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2014 Chris Johns (chrisj@rtems.org) + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution. + * + * This software with is provided ``as is'' and with NO WARRANTY. + */ + +/* + * RTEMS std redirector. + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <sys/types.h> +#include <unistd.h> + +#include <rtems.h> +#include <rtems/error.h> + +#include "stdio-redirect.h" + +#define RTEMS_STDIO_REDIRECT_LOCK_ATTRIBS \ + (RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE | \ + RTEMS_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL) + +#define RTEMS_STDIO_REDIRECT_RUNNING (1 << 0) +#define RTEMS_STDIO_REDIRECT_FINISHED (1 << 1) + +static bool +rtems_stdio_redirect_lock(rtems_stdio_redirect_t* sr) +{ + rtems_status_code sc = rtems_semaphore_obtain (sr->lock, + RTEMS_WAIT, + RTEMS_NO_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + { + fprintf(stderr, "error: stdio-redirect: lock failed: %s\n", rtems_status_text(sc)); + return false; + } + return true; +} + +static bool +rtems_stdio_redirect_unlock(rtems_stdio_redirect_t* sr) +{ + rtems_status_code sc = rtems_semaphore_release (sr->lock); + if (sc != RTEMS_SUCCESSFUL) + { + fprintf(stderr, "error: stdio-redirect: unlock failed: %s\n", rtems_status_text(sc)); + return false; + } + return true; +} + +static bool +rtems_stdio_redirect_write (rtems_stdio_redirect_t* sr, const char* buf, ssize_t len) +{ + if (!rtems_stdio_redirect_lock(sr)) + return false; + + if (sr->buffer) + { + if (len >= sr->buffer_size) + { + ssize_t offset = len - sr->buffer_size; + memcpy (sr->buffer, buf + offset, sr->buffer_size); + sr->in = 0; + sr->full = true; + } + else + { + if ((sr->in + len) > sr->buffer_size) + { + ssize_t bytes = sr->buffer_size - sr->in; + memcpy (sr->buffer + sr->in, buf, bytes); + buf += bytes; + len -= bytes; + sr->in = 0; + sr->full = true; + } + else + { + memcpy (sr->buffer + sr->in, buf, len); + sr->in += len; + } + } + } + + if (sr->handler) + sr->handler(buf, len); + + return rtems_stdio_redirect_lock(sr); +} + +static rtems_task +rtems_stdio_redirect_reader(rtems_task_argument arg) +{ + rtems_stdio_redirect_t* sr = (rtems_stdio_redirect_t*) arg; + + while (sr->state & RTEMS_STDIO_REDIRECT_RUNNING) + { + ssize_t r = read (sr->pipe[0], sr->input, sr->input_size); + + if (r <= 0) + break; + + if (sr->echo) + write (sr->fd_dup, sr->input, r); + + if (!rtems_stdio_redirect_write (sr, sr->input, r)) + break; + } + + sr->state |= RTEMS_STDIO_REDIRECT_FINISHED; + + rtems_task_delete(RTEMS_SELF); +} + +rtems_stdio_redirect_t* +rtems_stdio_redirect_open(int fd, + rtems_task_priority priority, + size_t stack_size, + ssize_t input_size, + ssize_t buffer_size, + bool echo, + rtems_stdio_redirect_handler handler) +{ + rtems_stdio_redirect_t* sr; + rtems_name name; + rtems_status_code sc; + + sr = malloc(sizeof(rtems_stdio_redirect_t)); + if (!sr) + { + fprintf(stderr, "error: stdio-redirect: no memory\n"); + return NULL; + } + + memset(sr, 0, sizeof(*sr)); + + sr->input_size = input_size; + sr->input = malloc(input_size); + if (!sr->input) + { + fprintf(stderr, "error: stdio-redirect: no memory for input\n"); + free(sr); + return NULL; + } + + if (buffer_size) + { + sr->buffer_size = buffer_size; + sr->buffer = malloc(buffer_size); + if (!sr->buffer) + { + fprintf(stderr, "error: stdio-redirect: no memory for buffer\n"); + free(sr->input); + free(sr); + return NULL; + } + } + + sr->fd = fd; + + sr->fd_dup = dup(fd); + if (sr->fd_dup < 0) + { + fprintf(stderr, "error: stdio-redirect: dup: %s\n", strerror(errno)); + free(sr->buffer); + free(sr->input); + free(sr); + return NULL; + } + + if (pipe(sr->pipe) < 0) + { + fprintf(stderr, "error: stdio-redirect: pipe create: %s\n", strerror(errno)); + free(sr->buffer); + free(sr->input); + free(sr); + return NULL; + + } + + sr->echo = echo; + sr->handler = handler; + + sc = rtems_semaphore_create (rtems_build_name ('R', 'S', 'R', 'l'), + 1, RTEMS_STDIO_REDIRECT_LOCK_ATTRIBS, 0, + &sr->lock); + if (sc != RTEMS_SUCCESSFUL) + { + fprintf(stderr, "error: stdio-redirect: lock create: %s\n", rtems_status_text(sc)); + free(sr->buffer); + free(sr->input); + free(sr); + return NULL; + } + + name = rtems_build_name ('S', 'R', '0' + (fd / 10), '0' + (fd % 10)); + sc = rtems_task_create (name, + priority, + stack_size, + RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR, + RTEMS_LOCAL | RTEMS_NO_FLOATING_POINT, + &sr->reader); + if (sc != RTEMS_SUCCESSFUL) + { + fprintf(stderr, "error: stdio-redirect: reader create: %s\n", rtems_status_text(sc)); + rtems_semaphore_delete(sr->lock); + free(sr->buffer); + free(sr->input); + free(sr); + return NULL; + } + + sr->state |= RTEMS_STDIO_REDIRECT_RUNNING; + + if (dup2 (sr->pipe[1], fd) < 0) + { + fprintf(stderr, "error: stdio-redirect: dup2: %s\n", strerror(errno)); + free(sr->buffer); + free(sr->input); + free(sr); + return NULL; + } + + sc = rtems_task_start (sr->reader, + rtems_stdio_redirect_reader, + (rtems_task_argument) sr); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf(stderr, "error: stdio-redirect: reader start: %s\n", rtems_status_text(sc)); + rtems_task_delete(sr->reader); + rtems_semaphore_delete(sr->lock); + free(sr->buffer); + free(sr->input); + free(sr); + return NULL; + } + + return sr; +} + +void +rtems_stdio_redirect_close(rtems_stdio_redirect_t* sr) +{ + if (rtems_stdio_redirect_lock(sr)) + { + sr->state &= ~RTEMS_STDIO_REDIRECT_RUNNING; + close(sr->pipe[0]); + + rtems_stdio_redirect_unlock(sr); + + while (sr->state & RTEMS_STDIO_REDIRECT_FINISHED) + { + usleep(250 * 1000 * 1000); + } + + rtems_stdio_redirect_lock(sr); + + dup2(sr->fd, sr->fd_dup); + + free(sr->buffer); + free(sr->input); + + rtems_stdio_redirect_unlock(sr); + + rtems_semaphore_delete(sr->lock); + + free(sr); + } +} + +ssize_t +rtems_stdio_redirect_read(rtems_stdio_redirect_t* sr, + char* buffer, + ssize_t length) +{ + ssize_t written = 0; + + if (!rtems_stdio_redirect_lock(sr)) + return written; + + if (sr->buffer) + { + ssize_t rem = 0; + + if (sr->full) + { + if (length < sr->buffer_size) + { + if (length > sr->in) + { + rem = length - sr->in; + memcpy (buffer, sr->buffer + sr->buffer_size - rem, rem); + } + + memcpy (buffer + rem, sr->buffer, sr->in); + written = length; + } + else + { + rem = sr->buffer_size - sr->in; + memcpy (buffer, sr->buffer + sr->in, rem); + memcpy (buffer + rem, sr->buffer, sr->in); + written = sr->buffer_size; + } + + sr->full = false; + } + else if (length < sr->in) + { + rem = sr->in - length; + memcpy (buffer, sr->buffer + rem, length); + written = length; + } + else + { + memcpy (buffer, sr->buffer, sr->in); + written = sr->in; + } + } + + rtems_stdio_redirect_unlock(sr); + + return written; +} diff --git a/cpukit/libmisc/redirector/stdio-redirect.h b/cpukit/libmisc/redirector/stdio-redirect.h new file mode 100644 index 0000000000..cf3d0cfcd4 --- /dev/null +++ b/cpukit/libmisc/redirector/stdio-redirect.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2014 Chris Johns (chrisj@rtems.org) + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution. + * + * This software with is provided ``as is'' and with NO WARRANTY. + */ + +/* + * @brief Redirect an stdio file decriptor. + * + * The is a helper module of code design to redirect an stdio file + * descriptor. You can optionally have the data buffered and/or you can provide + * a handler function that is called when data arrives. + * + * The module uses standard POSIX calls to implement the redirection and if the + * threading was POSIX based the code would be portable. Currently the code + * uses RTEMS threads. + * + * Redirecting stderr and stdout is useful in embedded system because you can + * transport the output off your device or create a web interface that can + * display the output. This can be a very powerful diagnostic and support tool. + * + * The implementation does: + * + * 1. Duplicate the file descriptor (fd) to redirect using the dup call. The + * duplicated desciptor is used to echo the output out the existing path. + * + * 2. Create a pipe using the pipe call. + * + * 3. Duplicate the pipe's writer file descriptor to user's file + * descriptor. This results in any writes to the user's fd being written to + * the pipe. + * + * 4. Create a reader task that blocks on the pipe. It optionally calls a + * handler and if configured buffers the data. + */ + +#if !defined(RTEMS_STDIO_REDIRECT_H) +#define RTEMS_STDIO_REDIRECT_H + +#include <stdbool.h> +#include <rtems.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Handler called whenever redirected data arrives. + * + * @param buffer The data. + * @param length The amount of data in the buffer. + */ +typedef void (*rtems_stdio_redirect_handler)(const char* buffer, + ssize_t length); + +/* + * Redirector data. + */ +typedef struct +{ + volatile uint32_t state; /**< The state. */ + rtems_id reader; /**< The reader thread. */ + rtems_id lock; /**< Lock for this struct. */ + int fd; /**< The file descriptor to redirect. */ + int fd_dup; /**< Duplicated fd to write to. */ + int pipe[2]; /**< The pipe to the reader thread. */ + char* input; /**< The input buffer the reader uses. */ + ssize_t input_size; /**< The input buffer size. */ + char* buffer; /**< Captured redirected data. */ + ssize_t buffer_size; /**< Capture buffer size. */ + ssize_t in; /**< Buffer in index. */ + bool full; /**< The buffer is full. */ + bool echo; /**< Echo the data out the existing path. */ + rtems_stdio_redirect_handler handler; /**< Redirected data handler. */ +} rtems_stdio_redirect_t; + +/* + * Open a redirector returning the handle to it. + * + * @param fd The file descriptor to redirect. + * @param priority The priority of the reader thread. + */ +rtems_stdio_redirect_t* rtems_stdio_redirect_open(int fd, + rtems_task_priority priority, + size_t stack_size, + ssize_t input_size, + ssize_t buffer_size, + bool echo, + rtems_stdio_redirect_handler handler); + +/* + * Close the redirector. + */ +void rtems_stdio_redirect_close(rtems_stdio_redirect_t* sr); + +/* + * Get data from the capture buffer. Data read is removed from the buffer. + * + * @param sr The stdio redirection handle. + * @param buffer The buffer data is written into. + * @param length The size of the buffer. + * @return ssize_t The amount of data written and -1 or an error. + */ +ssize_t rtems_stdio_redirect_read(rtems_stdio_redirect_t* sr, + char* buffer, + ssize_t length); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/cpukit/preinstall.am b/cpukit/preinstall.am index ccbb47fc14..1d9cd5366e 100644 --- a/cpukit/preinstall.am +++ b/cpukit/preinstall.am @@ -374,6 +374,10 @@ $(PROJECT_INCLUDE)/rtems/qreslib.h: libqos/qreslib.h $(PROJECT_INCLUDE)/rtems/$( $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/qreslib.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/qreslib.h +$(PROJECT_INCLUDE)/rtems/stdio-redirect.h: libmisc/redirector/stdio-redirect.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/stdio-redirect.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/stdio-redirect.h + if LIBSHELL $(PROJECT_INCLUDE)/rtems/shell.h: libmisc/shell/shell.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/shell.h |