summaryrefslogtreecommitdiffstats
path: root/cpukit/libmisc
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libmisc')
-rw-r--r--cpukit/libmisc/Makefile.am4
-rw-r--r--cpukit/libmisc/redirector/stdio-redirect.c333
-rw-r--r--cpukit/libmisc/redirector/stdio-redirect.h115
3 files changed, 452 insertions, 0 deletions
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