Index: cpukit/Makefile.am
===================================================================
--- cpukit/Makefile.am	(revision a8fa078f1efcc682f7155ab7423543d7d582f80a)
+++ cpukit/Makefile.am	(revision ebf9a057b5095df43632752d327e7c297b9fd78b)
@@ -176,4 +176,6 @@
 include_rtems_HEADERS += libqos/qreslib.h
 
+## redirector
+include_rtems_HEADERS += libmisc/redirector/stdio-redirect.h
 ## shell
 if LIBSHELL
Index: cpukit/libmisc/Makefile.am
===================================================================
--- cpukit/libmisc/Makefile.am	(revision a8fa078f1efcc682f7155ab7423543d7d582f80a)
+++ cpukit/libmisc/Makefile.am	(revision ebf9a057b5095df43632752d327e7c297b9fd78b)
@@ -71,4 +71,8 @@
 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
Index: cpukit/libmisc/redirector/stdio-redirect.c
===================================================================
--- cpukit/libmisc/redirector/stdio-redirect.c	(revision ebf9a057b5095df43632752d327e7c297b9fd78b)
+++ cpukit/libmisc/redirector/stdio-redirect.c	(revision ebf9a057b5095df43632752d327e7c297b9fd78b)
@@ -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;
+}
Index: cpukit/libmisc/redirector/stdio-redirect.h
===================================================================
--- cpukit/libmisc/redirector/stdio-redirect.h	(revision ebf9a057b5095df43632752d327e7c297b9fd78b)
+++ cpukit/libmisc/redirector/stdio-redirect.h	(revision ebf9a057b5095df43632752d327e7c297b9fd78b)
@@ -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
Index: cpukit/preinstall.am
===================================================================
--- cpukit/preinstall.am	(revision a8fa078f1efcc682f7155ab7423543d7d582f80a)
+++ cpukit/preinstall.am	(revision ebf9a057b5095df43632752d327e7c297b9fd78b)
@@ -375,4 +375,8 @@
 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)
