summaryrefslogtreecommitdiffstats
path: root/cpukit/libmisc/redirector/stdio-redirect.c
diff options
context:
space:
mode:
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;
+}