From 2916fa1404c5e3661440f174c3d3fa06c3482aad Mon Sep 17 00:00:00 2001 From: simon Date: Thu, 23 Feb 2006 13:38:44 +0000 Subject: [PATCH] Do proper select-for-write on ptys. Currently, pasting a sufficiently large string into pterm in any circumstances in which it's echoed back to the terminal will cause a deadlock once the pty's write buffer fills up. git-svn-id: svn://svn.tartarus.org/sgt/putty@6582 cda61777-01e9-0310-a592-d414129be87e --- unix/uxpty.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/unix/uxpty.c b/unix/uxpty.c index c3174373..b54ed417 100644 --- a/unix/uxpty.c +++ b/unix/uxpty.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -83,6 +84,7 @@ struct pty_tag { int term_width, term_height; int child_dead, finished; int exit_code; + bufchain output_data; }; /* @@ -178,6 +180,7 @@ static struct utmpx utmp_entry; char **pty_argv; static void pty_close(Pty pty); +static void pty_try_write(Pty pty); #ifndef OMIT_UTMP static void setup_utmp(char *ttyname, char *location) @@ -347,6 +350,14 @@ static void pty_open_master(Pty pty) strncpy(pty->name, ptsname(pty->master_fd), FILENAME_MAX-1); #endif + { + /* + * Set the pty master into non-blocking mode. + */ + int i = 1; + ioctl(pty->master_fd, FIONBIO, &i); + } + if (!ptys_by_fd) ptys_by_fd = newtree234(pty_compare_by_fd); add234(ptys_by_fd, pty); @@ -375,6 +386,7 @@ void pty_pre_init(void) #endif pty = single_pty = snew(struct pty_tag); + bufchain_init(&pty->output_data); /* set the child signal handler straight away; it needs to be set * before we ever fork. */ @@ -555,7 +567,12 @@ int pty_real_select_result(Pty pty, int event, int status) } else if (ret > 0) { from_backend(pty->frontend, 0, buf, ret); } - } + } else if (event == 2) { + /* + * Attempt to send data down the pty. + */ + pty_try_write(pty); + } } if (finished && !pty->finished) { @@ -629,7 +646,12 @@ int pty_select_result(int fd, int event) static void pty_uxsel_setup(Pty pty) { - uxsel_set(pty->master_fd, 1, pty_select_result); + int rwx; + + rwx = 1; /* always want to read from pty */ + if (bufchain_size(&pty->output_data)) + rwx |= 2; /* might also want to write to it */ + uxsel_set(pty->master_fd, rwx, pty_select_result); /* * In principle this only needs calling once for all pty @@ -871,6 +893,33 @@ static void pty_free(void *handle) sfree(pty); } +static void pty_try_write(Pty pty) +{ + void *data; + int len, ret; + + assert(pty->master_fd >= 0); + + while (bufchain_size(&pty->output_data) > 0) { + bufchain_prefix(&pty->output_data, &data, &len); + ret = write(pty->master_fd, data, len); + + if (ret < 0 && (errno == EWOULDBLOCK)) { + /* + * We've sent all we can for the moment. + */ + break; + } + if (ret < 0) { + perror("write pty master"); + exit(1); + } + bufchain_consume(&pty->output_data, ret); + } + + pty_uxsel_setup(pty); +} + /* * Called to send data down the pty. */ @@ -879,18 +928,12 @@ static int pty_send(void *handle, char *buf, int len) Pty pty = (Pty)handle; if (pty->master_fd < 0) - return 0; /* ignore all writes if fd closed */ + return 0; /* ignore all writes if fd closed */ - while (len > 0) { - int ret = write(pty->master_fd, buf, len); - if (ret < 0) { - perror("write pty master"); - exit(1); - } - buf += ret; - len -= ret; - } - return 0; + bufchain_add(&pty->output_data, buf, len); + pty_try_write(pty); + + return bufchain_size(&pty->output_data); } static void pty_close(Pty pty) -- 2.11.0