summaryrefslogtreecommitdiffstats
path: root/cpukit/libmisc/redirector/stdio-redirect.c
diff options
context:
space:
mode:
authorChris Johns <chrisj@rtems.org>2014-09-15 08:50:28 +1000
committerChris Johns <chrisj@rtems.org>2014-09-16 15:19:58 +1000
commitebf9a057b5095df43632752d327e7c297b9fd78b (patch)
tree2b09f2946fd8a78eef7d1f76ba0df9db029cdae3 /cpukit/libmisc/redirector/stdio-redirect.c
parentshell: Add a ping command. (diff)
downloadrtems-ebf9a057b5095df43632752d327e7c297b9fd78b.tar.bz2
libmisc: Add a stdio redirector helper.
This module makes it easy to redirect and capture stdout, stderr or any other fd in your application. The captured data can be sent off board, for example using syslog, or buffered and displayed in a web page.
Diffstat (limited to 'cpukit/libmisc/redirector/stdio-redirect.c')
-rw-r--r--cpukit/libmisc/redirector/stdio-redirect.c333
1 files changed, 333 insertions, 0 deletions
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;
+}