summaryrefslogtreecommitdiffstats
path: root/cpukit/libcsupport
diff options
context:
space:
mode:
authorJoel Sherrill <joel@rtems.org>2019-10-02 16:49:00 -0500
committerJoel Sherrill <joel@rtems.org>2019-10-08 14:25:41 -0500
commit667501a314ba75f80f1c13c6b43dd35d0a00efd1 (patch)
treeec786d34150d19f7aac04b2653fa356c6a696de2 /cpukit/libcsupport
parenttermios04/init.c: Remove dead copy of change_iflag() (diff)
downloadrtems-667501a314ba75f80f1c13c6b43dd35d0a00efd1.tar.bz2
termios: Add Capability to Generate SIGINTR and SIGQUIT
This patch adds the ability for termios to send SIGINTR on receipt of VINTR and SIGQUIT for VKILL and return -1/EINTR from read() on a termios channel. Importantly, this patch does not alter the default behavior or force POSIX signal code in just because termios is used. The application must explicitly enable the POSIX behavior of generating a signal upon receipt of these characters. This is discussed in the POSIX standard: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap11.html Closes #3800.
Diffstat (limited to '')
-rw-r--r--cpukit/libcsupport/src/termios.c225
-rw-r--r--cpukit/libcsupport/src/termios_posix_isig_handler.c39
2 files changed, 225 insertions, 39 deletions
diff --git a/cpukit/libcsupport/src/termios.c b/cpukit/libcsupport/src/termios.c
index 0fc9de452b..fedaa80ba0 100644
--- a/cpukit/libcsupport/src/termios.c
+++ b/cpukit/libcsupport/src/termios.c
@@ -1301,76 +1301,169 @@ iprocEarly (unsigned char c, rtems_termios_tty *tty)
}
/*
+ * This points to the currently registered method to perform
+ * ISIG processing of VKILL and VQUIT characters.
+ */
+static rtems_termios_isig_handler termios_isig_handler =
+ rtems_termios_default_isig_handler;
+
+/*
+ * This is the default method to process VKILL or VQUIT characters if
+ * ISIG processing is enabled. Note that it does nothing.
+ */
+rtems_termios_isig_status_code rtems_termios_default_isig_handler(
+ unsigned char c,
+ struct rtems_termios_tty *tty
+)
+{
+ return RTEMS_TERMIOS_ISIG_WAS_NOT_PROCESSED;
+}
+
+/*
+ * Register a method to process VKILL or VQUIT characters if
+ * ISIG processing is enabled.
+ */
+rtems_status_code rtems_termios_register_isig_handler(
+ rtems_termios_isig_handler handler
+)
+{
+ if (handler == NULL) {
+ return RTEMS_INVALID_ADDRESS;
+ }
+
+ termios_isig_handler = handler;
+ return RTEMS_SUCCESSFUL;
+}
+
+/**
+ * @brief Type returned by all input processing (iproc) methods
+ */
+typedef enum {
+ /**
+ * This indicates that the input character was processed
+ * and the results were placed into the buffer.
+ */
+ RTEMS_TERMIOS_IPROC_IN_BUFFER,
+ /**
+ * This indicates that the input character was processed
+ * and the result did not go into the buffer.
+ */
+ RTEMS_TERMIOS_IPROC_WAS_PROCESSED,
+ /**
+ * This indicates that the input character was not processed and
+ * subsequent processing is required.
+ */
+ RTEMS_TERMIOS_IPROC_WAS_NOT_PROCESSED,
+ /**
+ * This indicates that the character was processed and determined
+ * to be one that requires a signal to be raised (e.g. VINTR or
+ * VKILL). The tty must be in the right termios mode for this to
+ * occur. There is no further processing of this character required and
+ * the pending read() operation should be interrupted.
+ */
+ RTEMS_TERMIOS_IPROC_INTERRUPT_READ
+} rtems_termios_iproc_status_code;
+
+/*
* Process a single input character
*/
-static int
+static rtems_termios_iproc_status_code
iproc (unsigned char c, struct rtems_termios_tty *tty)
{
+ /*
+ * If signals are enabled, then allow possibility of VINTR causing
+ * SIGINTR and VQUIT causing SIGQUIT. Invoke user provided isig handler.
+ */
+ if ((tty->termios.c_lflag & ISIG)) {
+ if ((c == tty->termios.c_cc[VINTR]) || (c == tty->termios.c_cc[VQUIT])) {
+ rtems_termios_isig_status_code rc;
+ rc = (*termios_isig_handler)(c, tty);
+ if (rc == RTEMS_TERMIOS_ISIG_INTERRUPT_READ) {
+ return RTEMS_TERMIOS_IPROC_INTERRUPT_READ;
+ }
+ if (rc == RTEMS_TERMIOS_ISIG_WAS_PROCESSED) {
+ return RTEMS_TERMIOS_IPROC_IN_BUFFER;
+ }
+ _Assert(rc == RTEMS_TERMIOS_ISIG_WAS_NOT_PROCESSED);
+ }
+ }
+
+ /*
+ * Perform canonical character processing.
+ */
if ((c != '\0') && (tty->termios.c_lflag & ICANON)) {
if (c == tty->termios.c_cc[VERASE]) {
erase (tty, 0);
- return 0;
+ return RTEMS_TERMIOS_IPROC_WAS_PROCESSED;
}
else if (c == tty->termios.c_cc[VKILL]) {
erase (tty, 1);
- return 0;
+ return RTEMS_TERMIOS_IPROC_WAS_PROCESSED;
}
else if (c == tty->termios.c_cc[VEOF]) {
- return 1;
+ return RTEMS_TERMIOS_IPROC_IN_BUFFER;
} else if (c == '\n') {
if (tty->termios.c_lflag & (ECHO | ECHONL))
echo (c, tty);
tty->cbuf[tty->ccount++] = c;
- return 1;
+ return RTEMS_TERMIOS_IPROC_IN_BUFFER;
} else if ((c == tty->termios.c_cc[VEOL]) ||
(c == tty->termios.c_cc[VEOL2])) {
if (tty->termios.c_lflag & ECHO)
echo (c, tty);
tty->cbuf[tty->ccount++] = c;
- return 1;
+ return RTEMS_TERMIOS_IPROC_IN_BUFFER;
}
}
/*
+ * Perform non-canonical character processing.
+ *
* FIXME: Should do IMAXBEL handling somehow
*/
if (tty->ccount < (CBUFSIZE-1)) {
if (tty->termios.c_lflag & ECHO)
echo (c, tty);
tty->cbuf[tty->ccount++] = c;
+ return RTEMS_TERMIOS_IPROC_IN_BUFFER;
}
- return 0;
+ return RTEMS_TERMIOS_IPROC_WAS_NOT_PROCESSED;
}
/*
* Process input character, with semaphore.
*/
-static int
+static rtems_termios_iproc_status_code
siproc (unsigned char c, struct rtems_termios_tty *tty)
{
- int i;
+ rtems_termios_iproc_status_code rc;
/*
* Obtain output semaphore if character will be echoed
*/
if (tty->termios.c_lflag & (ECHO|ECHOE|ECHOK|ECHONL|ECHOPRT|ECHOCTL|ECHOKE)) {
rtems_mutex_lock (&tty->osem);
- i = iproc (c, tty);
+ rc = iproc (c, tty);
rtems_mutex_unlock (&tty->osem);
}
else {
- i = iproc (c, tty);
+ rc = iproc (c, tty);
}
- return i;
+ return rc;
}
-static int
+static rtems_termios_iproc_status_code
siprocPoll (unsigned char c, rtems_termios_tty *tty)
{
if (c == '\r' && (tty->termios.c_iflag & IGNCR) != 0) {
- return 0;
+ return RTEMS_TERMIOS_IPROC_WAS_NOT_PROCESSED;
}
+ /*
+ * iprocEarly is done at the interrupt level for interrupt driven
+ * devices so we need to perform those actions before processing
+ * the character.
+ */
c = iprocEarly (c, tty);
return siproc (c, tty);
}
@@ -1378,10 +1471,12 @@ siprocPoll (unsigned char c, rtems_termios_tty *tty)
/*
* Fill the input buffer by polling the device
*/
-static void
+static rtems_termios_iproc_status_code
fillBufferPoll (struct rtems_termios_tty *tty)
{
- int n;
+ int n;
+ rtems_termios_iproc_status_code rc;
+ rtems_termios_iproc_status_code retval = RTEMS_TERMIOS_IPROC_WAS_PROCESSED;
if (tty->termios.c_lflag & ICANON) {
for (;;) {
@@ -1389,8 +1484,14 @@ fillBufferPoll (struct rtems_termios_tty *tty)
if (n < 0) {
rtems_task_wake_after (1);
} else {
- if (siprocPoll (n, tty))
+ rc = siprocPoll (n, tty);
+ if (rc == RTEMS_TERMIOS_IPROC_IN_BUFFER) {
+ break;
+ }
+ if (rc == RTEMS_TERMIOS_IPROC_INTERRUPT_READ) {
+ retval = RTEMS_TERMIOS_IPROC_INTERRUPT_READ;
break;
+ }
}
}
} else {
@@ -1417,7 +1518,11 @@ fillBufferPoll (struct rtems_termios_tty *tty)
}
rtems_task_wake_after (1);
} else {
- siprocPoll (n, tty);
+ rc = siprocPoll (n, tty);
+ if (rc == RTEMS_TERMIOS_IPROC_INTERRUPT_READ) {
+ retval = RTEMS_TERMIOS_IPROC_INTERRUPT_READ;
+ break;
+ }
if (tty->ccount >= tty->termios.c_cc[VMIN])
break;
if (tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
@@ -1425,12 +1530,13 @@ fillBufferPoll (struct rtems_termios_tty *tty)
}
}
}
+ return retval;
}
/*
* Fill the input buffer from the raw input queue
*/
-static void
+static rtems_termios_iproc_status_code
fillBufferQueue (struct rtems_termios_tty *tty)
{
rtems_termios_device_context *ctx = tty->device_context;
@@ -1448,8 +1554,9 @@ fillBufferQueue (struct rtems_termios_tty *tty)
while ((tty->rawInBuf.Head != tty->rawInBuf.Tail) &&
(tty->ccount < (CBUFSIZE-1))) {
- unsigned char c;
- unsigned int newHead;
+ unsigned char c;
+ unsigned int newHead;
+ rtems_termios_iproc_status_code rc;
newHead = (tty->rawInBuf.Head + 1) % tty->rawInBuf.Size;
c = tty->rawInBuf.theBuf[newHead];
@@ -1479,12 +1586,21 @@ fillBufferQueue (struct rtems_termios_tty *tty)
/* continue processing new character */
if (tty->termios.c_lflag & ICANON) {
- if (siproc (c, tty)) {
- /* In canonical mode, input is made available line by line */
- return;
+ rc = siproc (c, tty);
+ /* If the read() should be interrupted, return */
+ if (rc == RTEMS_TERMIOS_IPROC_INTERRUPT_READ) {
+ return RTEMS_TERMIOS_IPROC_INTERRUPT_READ;
+ }
+ /* In canonical mode, input is made available line by line */
+ if (rc == RTEMS_TERMIOS_IPROC_IN_BUFFER) {
+ return RTEMS_TERMIOS_IPROC_IN_BUFFER;
}
} else {
- siproc (c, tty);
+ rc = siproc (c, tty);
+ /* If the read() should be interrupted, return */
+ if (rc == RTEMS_TERMIOS_IPROC_INTERRUPT_READ) {
+ return RTEMS_TERMIOS_IPROC_INTERRUPT_READ;
+ }
if (tty->ccount >= tty->termios.c_cc[VMIN])
wait = false;
}
@@ -1519,13 +1635,19 @@ fillBufferQueue (struct rtems_termios_tty *tty)
}
}
}
+ return RTEMS_TERMIOS_IPROC_WAS_PROCESSED;
}
-static uint32_t
-rtems_termios_read_tty (struct rtems_termios_tty *tty, char *buffer,
- uint32_t initial_count)
+static rtems_status_code
+rtems_termios_read_tty (
+ struct rtems_termios_tty *tty,
+ char *buffer,
+ uint32_t initial_count,
+ uint32_t *count_read
+)
{
- uint32_t count;
+ uint32_t count;
+ rtems_termios_iproc_status_code rc = RTEMS_TERMIOS_IPROC_WAS_PROCESSED;
count = initial_count;
@@ -1533,28 +1655,43 @@ rtems_termios_read_tty (struct rtems_termios_tty *tty, char *buffer,
tty->cindex = tty->ccount = 0;
tty->read_start_column = tty->column;
if (tty->handler.poll_read != NULL && tty->handler.mode == TERMIOS_POLLED)
- fillBufferPoll (tty);
+ rc = fillBufferPoll (tty);
else
- fillBufferQueue (tty);
+ rc = fillBufferQueue (tty);
}
+
+ /*
+ * If there are characters in the buffer, then copy them to the caller.
+ */
while (count && (tty->cindex < tty->ccount)) {
*buffer++ = tty->cbuf[tty->cindex++];
count--;
- }
+ }
tty->tty_rcvwakeup = false;
- return initial_count - count;
+ *count_read = initial_count - count;
+
+ /*
+ * fillBufferPoll and fillBufferQueue can indicate that the operation
+ * was interrupted. The isig handler can do nothing, have POSIX behavior
+ * and cause a signal, or a user defined action.
+ */
+ if (rc == RTEMS_TERMIOS_IPROC_INTERRUPT_READ) {
+ return RTEMS_INTERRUPTED;
+ }
+
+ return RTEMS_SUCCESSFUL;
}
rtems_status_code
rtems_termios_read (void *arg)
{
- rtems_libio_rw_args_t *args = arg;
+ rtems_libio_rw_args_t *args = arg;
struct rtems_termios_tty *tty = args->iop->data1;
+ rtems_status_code sc;
rtems_mutex_lock (&tty->isem);
if (rtems_termios_linesw[tty->t_line].l_read != NULL) {
- rtems_status_code sc;
sc = rtems_termios_linesw[tty->t_line].l_read(tty,args);
tty->tty_rcvwakeup = false;
@@ -1562,9 +1699,14 @@ rtems_termios_read (void *arg)
return sc;
}
- args->bytes_moved = rtems_termios_read_tty (tty, args->buffer, args->count);
+ sc = rtems_termios_read_tty(
+ tty,
+ args->buffer,
+ args->count,
+ &args->bytes_moved
+ );
rtems_mutex_unlock (&tty->isem);
- return RTEMS_SUCCESSFUL;
+ return sc;
}
/*
@@ -2022,7 +2164,8 @@ static ssize_t
rtems_termios_imfs_read (rtems_libio_t *iop, void *buffer, size_t count)
{
struct rtems_termios_tty *tty;
- uint32_t bytes_moved;
+ uint32_t bytes_moved;
+ rtems_status_code sc;
tty = iop->data1;
@@ -2049,9 +2192,13 @@ rtems_termios_imfs_read (rtems_libio_t *iop, void *buffer, size_t count)
return (ssize_t) args.bytes_moved;
}
- bytes_moved = rtems_termios_read_tty (tty, buffer, count);
+ sc = rtems_termios_read_tty(tty, buffer, count, &bytes_moved);
rtems_mutex_unlock (&tty->isem);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return rtems_status_code_to_errno (sc);
+ }
return (ssize_t) bytes_moved;
+
}
static ssize_t
diff --git a/cpukit/libcsupport/src/termios_posix_isig_handler.c b/cpukit/libcsupport/src/termios_posix_isig_handler.c
new file mode 100644
index 0000000000..105e7f463d
--- /dev/null
+++ b/cpukit/libcsupport/src/termios_posix_isig_handler.c
@@ -0,0 +1,39 @@
+/**
+ * @file
+ * TERMIOS POSIX behavior on INTR and QUIT characters
+ */
+
+/*
+ * COPYRIGHT (c) 1989-2012,2019.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/termiostypes.h>
+
+#include <signal.h>
+
+rtems_termios_isig_status_code rtems_termios_posix_isig_handler(
+ unsigned char c,
+ struct rtems_termios_tty *tty
+)
+{
+ if (c == tty->termios.c_cc[VINTR]) {
+ raise(SIGINT);
+ return RTEMS_TERMIOS_ISIG_INTERRUPT_READ;
+ }
+
+ if (c == tty->termios.c_cc[VQUIT]) {
+ raise(SIGQUIT);
+ return RTEMS_TERMIOS_ISIG_INTERRUPT_READ;
+ }
+
+ return RTEMS_TERMIOS_ISIG_WAS_NOT_PROCESSED;
+}