1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-04-21 13:05:04 -05:00

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.

[originally from svn r6582]
This commit is contained in:
Simon Tatham 2006-02-23 13:38:44 +00:00
parent 18509f8422
commit be93024bf2

View File

@ -9,6 +9,7 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h> #include <signal.h>
#include <assert.h>
#include <fcntl.h> #include <fcntl.h>
#include <termios.h> #include <termios.h>
#include <grp.h> #include <grp.h>
@ -83,6 +84,7 @@ struct pty_tag {
int term_width, term_height; int term_width, term_height;
int child_dead, finished; int child_dead, finished;
int exit_code; int exit_code;
bufchain output_data;
}; };
/* /*
@ -178,6 +180,7 @@ static struct utmpx utmp_entry;
char **pty_argv; char **pty_argv;
static void pty_close(Pty pty); static void pty_close(Pty pty);
static void pty_try_write(Pty pty);
#ifndef OMIT_UTMP #ifndef OMIT_UTMP
static void setup_utmp(char *ttyname, char *location) 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); strncpy(pty->name, ptsname(pty->master_fd), FILENAME_MAX-1);
#endif #endif
{
/*
* Set the pty master into non-blocking mode.
*/
int i = 1;
ioctl(pty->master_fd, FIONBIO, &i);
}
if (!ptys_by_fd) if (!ptys_by_fd)
ptys_by_fd = newtree234(pty_compare_by_fd); ptys_by_fd = newtree234(pty_compare_by_fd);
add234(ptys_by_fd, pty); add234(ptys_by_fd, pty);
@ -375,6 +386,7 @@ void pty_pre_init(void)
#endif #endif
pty = single_pty = snew(struct pty_tag); pty = single_pty = snew(struct pty_tag);
bufchain_init(&pty->output_data);
/* set the child signal handler straight away; it needs to be set /* set the child signal handler straight away; it needs to be set
* before we ever fork. */ * before we ever fork. */
@ -555,6 +567,11 @@ int pty_real_select_result(Pty pty, int event, int status)
} else if (ret > 0) { } else if (ret > 0) {
from_backend(pty->frontend, 0, buf, ret); from_backend(pty->frontend, 0, buf, ret);
} }
} else if (event == 2) {
/*
* Attempt to send data down the pty.
*/
pty_try_write(pty);
} }
} }
@ -629,7 +646,12 @@ int pty_select_result(int fd, int event)
static void pty_uxsel_setup(Pty pty) 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 * In principle this only needs calling once for all pty
@ -871,6 +893,33 @@ static void pty_free(void *handle)
sfree(pty); 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. * Called to send data down the pty.
*/ */
@ -881,16 +930,10 @@ static int pty_send(void *handle, char *buf, int len)
if (pty->master_fd < 0) if (pty->master_fd < 0)
return 0; /* ignore all writes if fd closed */ return 0; /* ignore all writes if fd closed */
while (len > 0) { bufchain_add(&pty->output_data, buf, len);
int ret = write(pty->master_fd, buf, len); pty_try_write(pty);
if (ret < 0) {
perror("write pty master"); return bufchain_size(&pty->output_data);
exit(1);
}
buf += ret;
len -= ret;
}
return 0;
} }
static void pty_close(Pty pty) static void pty_close(Pty pty)