Add an option to enable support for pseudo terminals.

Pseudo terminals are very convenient for testing purposes, but they are
not fully compatible with real serial (or even usb-serial) hardware.
With the new option, some workarounds can be enabled to hide the
differences and increase compatibility. Although these workarounds
shouldn't cause any problems in production builds, the advise is to
disable this feature.

A few ioctl's are not supported for pseudo terminals. They fail with
EINVAL (Linux) or ENOTTY (Mac OS X). Since these specific error codes
should not occur under normal conditions, they are simply ignored when
pseudo terminal support is enabled.

The TIOCEXCL ioctl (exclusive access) is also problematic. The TIOCEXCL
setting is shared between the master and slave side of the pty. When the
setting is applied on the slave side, it persists for as long as the
master side remains open. The result is that re-opening the slave side
will fail with EBUSY, unless the process has root priviliges. Since this
is very inconvenient, the TIOCEXCL setting is not used when pseudo
terminal support is enabled.
This commit is contained in:
Jef Driesen 2012-08-02 23:58:02 +02:00
parent 6cb4bc6a06
commit fab606b00a
2 changed files with 23 additions and 6 deletions

View File

@ -37,6 +37,15 @@ AS_IF([test "x$enable_logging" = "xyes"], [
AC_DEFINE(ENABLE_LOGGING, [1], [Enable logging.])
])
# Pseudo terminal support.
AC_ARG_ENABLE([pty],
[AS_HELP_STRING([--enable-pty=@<:@yes/no@:>@],
[Enable pseudo terminal support @<:@default=no@:>@])],
[], [enable_pty=no])
AS_IF([test "x$enable_pty" = "xyes"], [
AC_DEFINE(ENABLE_PTY, [1], [Enable pseudo terminal support.])
])
# Checks for programs.
AC_PROG_CC
AC_PROG_CC_C99

View File

@ -43,6 +43,12 @@
#define TIOCINQ FIONREAD
#endif
#ifdef ENABLE_PTY
#define NOPTY (errno != EINVAL && errno != ENOTTY)
#else
#define NOPTY 1
#endif
#include "serial.h"
#include "context-private.h"
@ -103,6 +109,7 @@ serial_open (serial_t **out, dc_context_t *context, const char* name)
return -1; // Error during open call.
}
#ifndef ENABLE_PTY
// Enable exclusive access mode.
if (ioctl (device->fd, TIOCEXCL, NULL) != 0) {
SYSERROR (context, errno);
@ -110,6 +117,7 @@ serial_open (serial_t **out, dc_context_t *context, const char* name)
free (device);
return -1;
}
#endif
// Retrieve the current terminal attributes, to
// be able to restore them when closing the device.
@ -373,7 +381,7 @@ serial_configure (serial_t *device, int baudrate, int databits, int parity, int
#if defined(TIOCGSERIAL) && defined(TIOCSSERIAL)
// Get the current settings.
struct serial_struct ss;
if (ioctl (device->fd, TIOCGSERIAL, &ss) != 0) {
if (ioctl (device->fd, TIOCGSERIAL, &ss) != 0 && NOPTY) {
SYSERROR (device->context, errno);
return -1;
}
@ -384,13 +392,13 @@ serial_configure (serial_t *device, int baudrate, int databits, int parity, int
ss.flags |= ASYNC_SPD_CUST;
// Apply the new settings.
if (ioctl (device->fd, TIOCSSERIAL, &ss) != 0) {
if (ioctl (device->fd, TIOCSSERIAL, &ss) != 0 && NOPTY) {
SYSERROR (device->context, errno);
return -1;
}
#elif defined(IOSSIOSPEED)
speed_t speed = baudrate;
if (ioctl (device->fd, IOSSIOSPEED, &speed) != 0) {
if (ioctl (device->fd, IOSSIOSPEED, &speed) != 0 && NOPTY) {
SYSERROR (device->context, errno);
return -1;
}
@ -655,7 +663,7 @@ serial_set_break (serial_t *device, int level)
unsigned long action = (level ? TIOCSBRK : TIOCCBRK);
if (ioctl (device->fd, action, NULL) != 0) {
if (ioctl (device->fd, action, NULL) != 0 && NOPTY) {
SYSERROR (device->context, errno);
return -1;
}
@ -673,7 +681,7 @@ serial_set_dtr (serial_t *device, int level)
unsigned long action = (level ? TIOCMBIS : TIOCMBIC);
int value = TIOCM_DTR;
if (ioctl (device->fd, action, &value) != 0) {
if (ioctl (device->fd, action, &value) != 0 && NOPTY) {
SYSERROR (device->context, errno);
return -1;
}
@ -691,7 +699,7 @@ serial_set_rts (serial_t *device, int level)
unsigned long action = (level ? TIOCMBIS : TIOCMBIC);
int value = TIOCM_RTS;
if (ioctl (device->fd, action, &value) != 0) {
if (ioctl (device->fd, action, &value) != 0 && NOPTY) {
SYSERROR (device->context, errno);
return -1;
}