diff --git a/src/gnutls.c b/src/gnutls.c index 903393fed1..e7d0d3d845 100644 --- a/src/gnutls.c +++ b/src/gnutls.c @@ -708,16 +708,18 @@ emacs_gnutls_read (struct Lisp_Process *proc, char *buf, ptrdiff_t nbyte) rtnval = gnutls_record_recv (state, buf, nbyte); if (rtnval >= 0) return rtnval; - else if (rtnval == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) - /* The peer closed the connection. */ - return 0; else if (emacs_gnutls_handle_error (state, rtnval)) - /* non-fatal error */ - return -1; - else { - /* a fatal error occurred */ - return 0; - } + { + /* non-fatal error */ + errno = EAGAIN; + return -1; + } + else + { + /* a fatal error occurred */ + errno = EPROTO; + return 0; + } } static char const * @@ -756,8 +758,10 @@ emacs_gnutls_handle_error (gnutls_session_t session, int err) connection. */ # ifdef HAVE_GNUTLS3 if (err == GNUTLS_E_PREMATURE_TERMINATION) - level = 3; +# else + if (err == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) # endif + level = 3; GNUTLS_LOG2 (level, max_log_level, "fatal error:", str); ret = false; diff --git a/src/process.c b/src/process.c index b201e9b6ac..c1116c1deb 100644 --- a/src/process.c +++ b/src/process.c @@ -5005,6 +5005,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, struct timespec got_output_end_time = invalid_timespec (); enum { MINIMUM = -1, TIMEOUT, INFINITY } wait; int got_some_output = -1; + uintmax_t prev_wait_proc_nbytes_read = wait_proc ? wait_proc->nbytes_read : 0; #if defined HAVE_GETADDRINFO_A || defined HAVE_GNUTLS bool retry_for_async; #endif @@ -5390,60 +5391,31 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, #endif /* !HAVE_GLIB */ #ifdef HAVE_GNUTLS - /* GnuTLS buffers data internally. In lowat mode it leaves - some data in the TCP buffers so that select works, but - with custom pull/push functions we need to check if some - data is available in the buffers manually. */ - if (nfds == 0) + /* GnuTLS buffers data internally. select() will only report + available data for the underlying kernel sockets API, not + what has been buffered internally. As such, we need to loop + through all channels and check for available data manually. */ + if (nfds >= 0) { - fd_set tls_available; - int set = 0; - - FD_ZERO (&tls_available); - if (! wait_proc) - { - /* We're not waiting on a specific process, so loop - through all the channels and check for data. - This is a workaround needed for some versions of - the gnutls library -- 2.12.14 has been confirmed - to need it. See - http://comments.gmane.org/gmane.emacs.devel/145074 */ - for (channel = 0; channel < FD_SETSIZE; ++channel) - if (! NILP (chan_process[channel])) - { - struct Lisp_Process *p = - XPROCESS (chan_process[channel]); - if (p && p->gnutls_p && p->gnutls_state - && ((emacs_gnutls_record_check_pending - (p->gnutls_state)) - > 0)) - { - nfds++; - eassert (p->infd == channel); - FD_SET (p->infd, &tls_available); - set++; - } - } - } - else - { - /* Check this specific channel. */ - if (wait_proc->gnutls_p /* Check for valid process. */ - && wait_proc->gnutls_state - /* Do we have pending data? */ - && ((emacs_gnutls_record_check_pending - (wait_proc->gnutls_state)) - > 0)) - { - nfds = 1; - eassert (0 <= wait_proc->infd); - /* Set to Available. */ - FD_SET (wait_proc->infd, &tls_available); - set++; - } - } - if (set) - Available = tls_available; + for (channel = 0; channel < FD_SETSIZE; ++channel) + if (! NILP (chan_process[channel])) + { + struct Lisp_Process *p = + XPROCESS (chan_process[channel]); + + if (just_wait_proc && p != wait_proc) + continue; + + if (p && p->gnutls_p && p->gnutls_state + && ((emacs_gnutls_record_check_pending + (p->gnutls_state)) + > 0)) + { + nfds++; + eassert (p->infd == channel); + FD_SET (p->infd, &Available); + } + } } #endif } @@ -5459,6 +5431,8 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, if (nfds == 0) { /* Exit the main loop if we've passed the requested timeout, + or have read some bytes from our wait_proc (either directly + in this call or indirectly through timers / process filters), or aren't skipping processes and got some output and haven't lowered our timeout due to timers or SIGIO and have waited a long amount of time due to repeated @@ -5466,7 +5440,9 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, struct timespec huge_timespec = make_timespec (TYPE_MAXIMUM (time_t), 2 * TIMESPEC_RESOLUTION); struct timespec cmp_time = huge_timespec; - if (wait < TIMEOUT) + if (wait < TIMEOUT + || (wait_proc + && wait_proc->nbytes_read != prev_wait_proc_nbytes_read)) break; if (wait == TIMEOUT) cmp_time = end_time; @@ -5781,6 +5757,15 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, maybe_quit (); } + /* Timers and/or process filters that we have run could have themselves called + `accept-process-output' (and by that indirectly this function), thus + possibly reading some (or all) output of wait_proc without us noticing it. + This could potentially lead to an endless wait (dealt with earlier in the + function) and/or a wrong return value (dealt with here). */ + if (wait_proc && wait_proc->nbytes_read != prev_wait_proc_nbytes_read) + got_some_output = min (INT_MAX, (wait_proc->nbytes_read + - prev_wait_proc_nbytes_read)); + return got_some_output; } @@ -5899,6 +5884,9 @@ read_process_output (Lisp_Object proc, int channel) coding->mode |= CODING_MODE_LAST_BLOCK; } + /* Ignore carryover, it's been added by a previous iteration already. */ + p->nbytes_read += nbytes; + /* Now set NBYTES how many bytes we must decode. */ nbytes += carryover; diff --git a/src/process.h b/src/process.h index ab468b18c5..6464a8cc61 100644 --- a/src/process.h +++ b/src/process.h @@ -129,6 +129,8 @@ struct Lisp_Process pid_t pid; /* Descriptor by which we read from this process. */ int infd; + /* Byte-count modulo (UINTMAX_MAX + 1) for process output read from `infd'. */ + uintmax_t nbytes_read; /* Descriptor by which we write to this process. */ int outfd; /* Descriptors that were created for this process and that need diff --git a/src/xgselect.c b/src/xgselect.c index fedd3127ef..f68982143e 100644 --- a/src/xgselect.c +++ b/src/xgselect.c @@ -143,6 +143,14 @@ xg_select (int fds_lim, fd_set *rfds, fd_set *wfds, fd_set *efds, ++retval; } } + else if (nfds == 0) + { + // pselect() clears the file descriptor sets if no fd is ready (but + // not if an error occurred), so should we to be compatible. (Bug#21337) + if (rfds) FD_ZERO (rfds); + if (wfds) FD_ZERO (wfds); + if (efds) FD_ZERO (efds); + } /* If Gtk+ is in use eventually gtk_main_iteration will be called, unless retval is zero. */