diff options
author | Lukas Larsson <[email protected]> | 2014-06-26 18:55:47 +0200 |
---|---|---|
committer | Lukas Larsson <[email protected]> | 2014-10-16 17:56:16 +0200 |
commit | 48083e54b502afb2768066394074d29423162dc8 (patch) | |
tree | 00edbcd4e486ac47a61a701d3f0cc6f2e3004156 /erts/emulator | |
parent | 5ab275d6554f4db877624a152dda90c0e3c4cddc (diff) | |
download | otp-48083e54b502afb2768066394074d29423162dc8.tar.gz otp-48083e54b502afb2768066394074d29423162dc8.tar.bz2 otp-48083e54b502afb2768066394074d29423162dc8.zip |
kernel,ssh: Add synchronous user_drv protocol
Added a put_chars_sync to the protocol that can be used to
talk to user_drv and made group use it. This is needed in order
to guarantee that bytes has been pushed to the tty port when
doing something like this:
io:format("halting\n"),erlang:halt(0).
Before this change the halting message could be lost in the message
queue of the user_drv process, this is no longer possible.
This commit also fixes ssh_cli as that plugs itself in as a user_drv
process.
OTP-12240
Diffstat (limited to 'erts/emulator')
-rw-r--r-- | erts/emulator/drivers/unix/ttsl_drv.c | 36 | ||||
-rw-r--r-- | erts/emulator/drivers/win32/ttsl_drv.c | 16 |
2 files changed, 52 insertions, 0 deletions
diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c index 73ce27c6ec..be2fee1f25 100644 --- a/erts/emulator/drivers/unix/ttsl_drv.c +++ b/erts/emulator/drivers/unix/ttsl_drv.c @@ -93,6 +93,7 @@ static volatile int cols_needs_update = FALSE; #define OP_INSC 2 #define OP_DELC 3 #define OP_BEEP 4 +#define OP_PUTC_SYNC 5 /* Control op */ #define CTRL_OP_GET_WINSIZE 100 #define CTRL_OP_GET_UNICODE_STATE 101 @@ -137,6 +138,7 @@ static Sint16 get_sint16(char*); static ErlDrvPort ttysl_port; static int ttysl_fd; static int ttysl_terminate = 0; +static int ttysl_send_ok = 0; static ErlDrvBinary *putcbuf; static int putcpos; static int putclen; @@ -683,6 +685,14 @@ static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT coun put_chars((byte*)"\n", 1); switch (buf[0]) { + case OP_PUTC_SYNC: + /* Using sync means that we have to send an ok to the + controlling process for each command call. We delay + sending ok if the driver queue exceeds a certain size. + We do not set ourselves as a busy port, as this + could be very bad for user_drv, if it gets blocked on + the port_command. */ + /* fall through */ case OP_PUTC: DEBUGLOG(("OP: Putc(%lu)",(unsigned long) count-1)); if (check_buf_size((byte*)buf+1, count-1) == 0) @@ -736,6 +746,22 @@ static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT coun } } + if (buf[0] == OP_PUTC_SYNC) { + if (driver_sizeq(ttysl_port) > TTY_BUFFSIZE && !ttysl_terminate) { + /* We delay sending the ack until the buffer has been consumed */ + ttysl_send_ok = 1; + } else { + ErlDrvTermData spec[] = { + ERL_DRV_PORT, driver_mk_port(ttysl_port), + ERL_DRV_ATOM, driver_mk_atom("ok"), + ERL_DRV_TUPLE, 2 + }; + ASSERT(ttysl_send_ok == 0); + erl_drv_output_term(driver_mk_port(ttysl_port), spec, + sizeof(spec) / sizeof(spec[0])); + } + } + return; /* TRUE; */ } @@ -758,6 +784,16 @@ static void ttysl_to_tty(ErlDrvData ttysl_data, ErlDrvEvent fd) { } } else { sz = driver_deq(ttysl_port, written); + if (sz < TTY_BUFFSIZE && ttysl_send_ok) { + ErlDrvTermData spec[] = { + ERL_DRV_PORT, driver_mk_port(ttysl_port), + ERL_DRV_ATOM, driver_mk_atom("ok"), + ERL_DRV_TUPLE, 2 + }; + ttysl_send_ok = 0; + erl_drv_output_term(driver_mk_port(ttysl_port), spec, + sizeof(spec) / sizeof(spec[0])); + } if (sz == 0) { driver_select(ttysl_port,(ErlDrvEvent)(long)ttysl_fd, ERL_DRV_WRITE,0); diff --git a/erts/emulator/drivers/win32/ttsl_drv.c b/erts/emulator/drivers/win32/ttsl_drv.c index 502cb58dfa..851c336a11 100644 --- a/erts/emulator/drivers/win32/ttsl_drv.c +++ b/erts/emulator/drivers/win32/ttsl_drv.c @@ -46,6 +46,7 @@ static int rows; /* Number of rows available. */ #define OP_INSC 2 #define OP_DELC 3 #define OP_BEEP 4 +#define OP_PUTC_SYNC 5 /* Control op */ #define CTRL_OP_GET_WINSIZE 100 @@ -458,6 +459,7 @@ static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT coun switch (buf[0]) { case OP_PUTC: + case OP_PUTC_SYNC: DEBUGLOG(("OP: Putc(%I64u)",(unsigned long long)count-1)); if (check_buf_size((byte*)buf+1, count-1) == 0) return; @@ -481,6 +483,20 @@ static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, ErlDrvSizeT coun /* Unknown op, just ignore. */ break; } + + if (buf[0] == OP_PUTC_SYNC) { + /* On windows we do a blocking write to the tty so we just + send the ack immidiately. If at some point in the future + someone has a problem with tty output being blocking + this has to be changed. */ + ErlDrvTermData spec[] = { + ERL_DRV_PORT, driver_mk_port(ttysl_port), + ERL_DRV_ATOM, driver_mk_atom("ok"), + ERL_DRV_TUPLE, 2 + }; + erl_drv_output_term(driver_mk_port(ttysl_port), spec, + sizeof(spec) / sizeof(spec[0])); + } return; } |