From 26db5626dfa6c2bc73da9f3333d5f5ed4b8ecb16 Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Thu, 11 Feb 2021 17:19:31 -0500 Subject: [PATCH] commit bash-20210209 snapshot --- CWRU/CWRU.chlog | 21 ++++ MANIFEST | 2 + Makefile.in | 3 +- bashline.c | 2 +- builtins/read.def | 25 +++++ include/timer.h | 63 ++++++++++++ lib/sh/Makefile.in | 14 ++- lib/sh/timers.c | 238 +++++++++++++++++++++++++++++++++++++++++++++ shell.h | 6 +- 9 files changed, 368 insertions(+), 6 deletions(-) create mode 100644 include/timer.h create mode 100644 lib/sh/timers.c diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index f78ee80f..9c0bf0c2 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -9555,3 +9555,24 @@ lib/malloc/malloc.c old size and new size are both bigger than the mmap threshold - internal_realloc: call internal_remap if the old size and new size are both above the threshold where we use mmap for allocation + + 2/10 + ---- +include/timer.h + - new file, declaration for a timer struct to be used by a set of + functions to implement timers using SIGALRM or select/pselect + +lib/sh/timers.c + - new file, set of functions to manipulate timer objects and timeouts + using SIGALRM or select/pselect. Inspired by a patch from + Koichi Murase . Not used yet + + 2/11 + ---- +builtins/read.def + - read_builtin: if there is a timeout set, block SIGCHLD around calls + to zread and its siblings, or calls to readline for `read -e', so + SIGCHLD (and the consequent waitpid) doesn't interrupt the read. + Fixes bug reported by Koichi Murase , but + there may be a different fix coming + diff --git a/MANIFEST b/MANIFEST index c808448d..e85057e5 100644 --- a/MANIFEST +++ b/MANIFEST @@ -230,6 +230,7 @@ include/shtty.h f include/stat-time.h f include/stdc.h f include/systimes.h f +include/timer.h f include/typemax.h f include/unionwait.h f lib/glob/Makefile.in f @@ -449,6 +450,7 @@ lib/sh/strtoul.c f lib/sh/strtoull.c f lib/sh/strtoumax.c f lib/sh/strtrans.c f +lib/sh/timers.c f lib/sh/times.c f lib/sh/timeval.c f lib/sh/tmpfile.c f diff --git a/Makefile.in b/Makefile.in index e1f41463..b3173e89 100644 --- a/Makefile.in +++ b/Makefile.in @@ -232,7 +232,8 @@ SHLIB_SOURCE = ${SH_LIBSRC}/clktck.c ${SH_LIBSRC}/getcwd.c \ ${SH_LIBSRC}/fnxform.c ${SH_LIBSRC}/unicode.c \ ${SH_LIBSRC}/wcswidth.c ${SH_LIBSRC}/wcsnwidth.c \ ${SH_LIBSRC}/shmbchar.c ${SH_LIBSRC}/utf8.c \ - ${SH_LIBSRC}/random.c ${SH_LIBSRC}/gettimeofday.c + ${SH_LIBSRC}/random.c ${SH_LIBSRC}/gettimeofday.c \ + ${SH_LIBSRC}/timers.c SHLIB_LIB = -lsh SHLIB_LIBNAME = libsh.a diff --git a/bashline.c b/bashline.c index c69c0c5e..831f35bf 100644 --- a/bashline.c +++ b/bashline.c @@ -4639,7 +4639,7 @@ bash_event_hook () bashline_reset_event_hook (); /* posix mode SIGINT during read -e. We only get here if SIGINT is trapped. */ - if (posixly_correct && this_shell_builtin == read_builtin && sig == 2) + if (posixly_correct && this_shell_builtin == read_builtin && sig == SIGINT) { last_command_exit_value = 128|SIGINT; throw_to_top_level (); diff --git a/builtins/read.def b/builtins/read.def index 39828f3f..82997151 100644 --- a/builtins/read.def +++ b/builtins/read.def @@ -192,6 +192,7 @@ read_builtin (list) struct stat tsb; SHELL_VAR *var; TTYSTRUCT ttattrs, ttset; + sigset_t chldset, prevset; #if defined (ARRAY_VARS) WORD_LIST *alist; int vflags; @@ -388,6 +389,12 @@ read_builtin (list) } } +#if defined (SIGCHLD) + sigemptyset (&chldset); + sigprocmask (SIG_BLOCK, (sigset_t *)0, &chldset); + sigaddset (&chldset, SIGCHLD); +#endif + begin_unwind_frame ("read_builtin"); #if defined (BUFFERED_INPUT) @@ -576,6 +583,10 @@ read_builtin (list) free (rlbuf); rlbuf = (char *)0; } +#if defined (SIGCHLD) + if (tmsec > 0 || tmusec > 0) + sigprocmask (SIG_SETMASK, &chldset, &prevset); +#endif if (rlbuf == 0) { reading = 1; @@ -583,6 +594,10 @@ read_builtin (list) reading = 0; rlind = 0; } +#if defined (SIGCHLD) + if (tmsec > 0 || tmusec > 0) + sigprocmask (SIG_SETMASK, &prevset, (sigset_t *)0); +#endif if (rlbuf == 0) { eof = 1; @@ -606,12 +621,22 @@ read_builtin (list) reading = 1; CHECK_ALRM; errno = 0; + +#if defined (SIGCHLD) + if (tmsec > 0 || tmusec > 0) + sigprocmask (SIG_SETMASK, &chldset, &prevset); +#endif if (unbuffered_read == 2) retval = posixly_correct ? zreadintr (fd, &c, 1) : zreadn (fd, &c, nchars - nr); else if (unbuffered_read) retval = posixly_correct ? zreadintr (fd, &c, 1) : zread (fd, &c, 1); else retval = posixly_correct ? zreadcintr (fd, &c) : zreadc (fd, &c); +#if defined (SIGCHLD) + if (tmsec > 0 || tmusec > 0) + sigprocmask (SIG_SETMASK, &prevset, (sigset_t *)0); +#endif + reading = 0; if (retval <= 0) diff --git a/include/timer.h b/include/timer.h new file mode 100644 index 00000000..833ac1c7 --- /dev/null +++ b/include/timer.h @@ -0,0 +1,63 @@ +/* timer.h -- data structures used by the shell timers in lib/sh/timers.c */ + +/* Copyright (C) 2021 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see . +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "bashjmp.h" +typedef struct _shtimer +{ + struct timeval tmout; + + int fd; + int flags; + + int alrmflag; /* should be set by alrm_handler */ + + SigHandler *alrm_handler; + SigHandler *old_handler; + + procenv_t jmpenv; + + void (*handler) (struct _shtimer *); /* XXX - unused right now */ + PTR_T *data; /* reserved */ +} sh_timer; + +#define SHTIMER_ALARM 0x01 /* mutually exclusive */ +#define SHTIMER_SELECT 0x02 +#define SHTIMER_LONGJMP 0x04 + +#define SHTIMER_SIGSET 0x100 + +extern sh_timer *shtimer_alloc (void); +extern void shtimer_flush (sh_timer *); +extern void shtimer_dispose (sh_timer *); + +extern void shtimer_set (sh_timer *, time_t, long); +extern void shtimer_unset (sh_timer *); + +extern void shtimer_cleanup (sh_timer *); +extern void shtimer_clear (sh_timer *); + +extern int shtimer_chktimeout (sh_timer *); + +extern int shtimer_select (sh_timer *); +extern int shtimer_alrm (sh_timer *); diff --git a/lib/sh/Makefile.in b/lib/sh/Makefile.in index 98064de5..d68f0b87 100644 --- a/lib/sh/Makefile.in +++ b/lib/sh/Makefile.in @@ -93,7 +93,7 @@ CSOURCES = clktck.c clock.c getcwd.c getenv.c oslib.c setlinebuf.c \ wcsdup.c fpurge.c zgetline.c mbscmp.c uconvert.c ufuncs.c \ casemod.c dprintf.c input_avail.c mbscasecmp.c fnxform.c \ strchrnul.c unicode.c wcswidth.c wcsnwidth.c shmbchar.c strdup.c \ - utf8.c random.c gettimeofday.c + utf8.c random.c gettimeofday.c timers.c # The header files for this library. HSOURCES = @@ -108,7 +108,7 @@ OBJECTS = clktck.o clock.o getenv.o oslib.o setlinebuf.o strnlen.o \ fmtullong.o fmtumax.o zcatfd.o zmapfd.o winsize.o wcsdup.o \ fpurge.o zgetline.o mbscmp.o uconvert.o ufuncs.o casemod.o \ input_avail.o mbscasecmp.o fnxform.o unicode.o shmbchar.o \ - utf8.o random.o gettimeofday.o wcsnwidth.o ${LIBOBJS} + utf8.o random.o gettimeofday.o timers.o wcsnwidth.o ${LIBOBJS} SUPPORT = Makefile @@ -197,6 +197,7 @@ strtoul.o: strtoul.c strtoull.o: strtoull.c strtoumax.o: strtoumax.c strtrans.o: strtrans.c +timers.o: timers.c times.o: times.c timeval.o: timeval.c tmpfile.o: tmpfile.c @@ -277,6 +278,7 @@ strtoul.o: ${BUILD_DIR}/config.h strtoull.o: ${BUILD_DIR}/config.h strtoumax.o: ${BUILD_DIR}/config.h strtrans.o: ${BUILD_DIR}/config.h +timers.o: ${BUILD_DIR}/config.h times.o: ${BUILD_DIR}/config.h timeval.o: ${BUILD_DIR}/config.h tmpfile.o: ${BUILD_DIR}/config.h ${topdir}/config-top.h @@ -620,6 +622,14 @@ fnxform.o: ${topdir}/bashintl.h ${LIBINTL_H} ${BASHINCDIR}/gettext.h shmbchar.o: ${BASHINCDIR}/shmbchar.h shmbchar.o: ${BASHINCDIR}/shmbutil.h +timers.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +timers.o: ${BASHINCDIR}/stdc.h +timers.o: ${topdir}/xmalloc.h ${topdir}/sig.h +timers.o: ${BASHINCDIR}/posixtime.h ${BASHINCDIR}/stat-time.h +timers.o: ${BASHINCDIR}/posixselect.h +timers.o: ${topdir}/bashjmp.h ${BASHINCDIR}/posixjmp.h +timers.o: ${BASHINCDIR}/timer.h + unicode.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h unicode.o: ${BASHINCDIR}/stdc.h unicode.o: ${topdir}/xmalloc.h diff --git a/lib/sh/timers.c b/lib/sh/timers.c new file mode 100644 index 00000000..4b8fc009 --- /dev/null +++ b/lib/sh/timers.c @@ -0,0 +1,238 @@ +/* timers - functions to manage shell timers */ + +/* Copyright (C) 2021 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see . +*/ + +#include "config.h" + +#include "bashtypes.h" +#include "posixtime.h" + +#if defined (HAVE_UNISTD_H) +#include +#endif + +#if defined (HAVE_SELECT) +# include "posixselect.h" +# include "stat-time.h" +#endif + +#include "sig.h" +#include "bashjmp.h" +#include "xmalloc.h" + +#include "timer.h" + +#include +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +extern unsigned int falarm (unsigned int, unsigned int); + +sh_timer * +shtimer_alloc (void) +{ + sh_timer *r; + + r = (sh_timer *)xmalloc (sizeof (sh_timer)); + shtimer_flush (r); + return r; +} + +void +shtimer_flush (sh_timer *t) +{ + t->tmout.tv_sec = 0; + t->tmout.tv_usec = 0; + + t->fd = t->flags = t->alrmflag = 0; + + t->alrm_handler = t->old_handler = 0; + + memset (t->jmpenv, '\0', sizeof (t->jmpenv)); + + t->handler = 0; + t->data = 0; +} + +void +shtimer_dispose (sh_timer *t) +{ + free (t); +} + +/* We keep the timer as an offset into the future from the time it's set. */ +void +shtimer_set (sh_timer *t, time_t sec, long usec) +{ + struct timeval now; + + if (t->flags & SHTIMER_ALARM) + { + t->alrmflag = 0; /* just paranoia */ + t->old_handler = set_signal_handler (SIGALRM, t->alrm_handler); + t->flags |= SHTIMER_SIGSET; + falarm (t->tmout.tv_sec = sec, t->tmout.tv_usec = usec); + return; + } + + if (gettimeofday (&now, 0) < 0) + { + now.tv_sec = 0; + now.tv_usec = 0; + } + t->tmout.tv_sec = now.tv_sec + sec; + t->tmout.tv_usec = now.tv_usec + usec; + if (t->tmout.tv_usec > USEC_PER_SEC) + { + t->tmout.tv_sec++; + t->tmout.tv_usec -= USEC_PER_SEC; + } +} + +void +shtimer_unset (sh_timer *t) +{ + t->tmout.tv_sec = 0; + t->tmout.tv_usec = 0; + + if (t->flags & SHTIMER_ALARM) + { + t->alrmflag = 0; + falarm (0, 0); + if (t->old_handler && (t->flags & SHTIMER_SIGSET)) + { + set_signal_handler (SIGALRM, t->old_handler); + t->flags &= ~SHTIMER_SIGSET; + } + } +} + +void +shtimer_cleanup (sh_timer *t) +{ + shtimer_unset (t); +} + +void +shtimer_clear (sh_timer *t) +{ + shtimer_unset (t); + shtimer_dispose (t); +} + +int +shtimer_chktimeout (sh_timer *t) +{ + struct timeval now; + int r; + + /* Use the flag to avoid returning sigalrm_seen here */ + if (t->flags & SHTIMER_ALARM) + return t->alrmflag; + + /* Could check a flag for this */ + if (t->tmout.tv_sec == 0 && t->tmout.tv_usec == 0) + return 0; + + if (gettimeofday (&now, 0) < 0) + return 0; + r = ((now.tv_sec > t->tmout.tv_sec) || + (now.tv_sec == t->tmout.tv_sec && now.tv_usec >= t->tmout.tv_usec)); + + return r; +} + +#if defined (HAVE_SELECT) || defined (HAVE_PSELECT) +int +shtimer_select (sh_timer *t) +{ + int r; + sigset_t blocked_sigs, prevmask; + struct timeval now, tv; + fd_set readfds; +#if defined (HAVE_PSELECT) + struct timespec ts; +#endif + + /* We don't want a SIGCHLD to interrupt this */ + sigemptyset (&blocked_sigs); +# if defined (SIGCHLD) + sigaddset (&blocked_sigs, SIGCHLD); +# endif + + if (gettimeofday (&now, 0) < 0) + { + if (t->flags & SHTIMER_LONGJMP) + sh_longjmp (t->jmpenv, 1); + else + return -1; + } + + /* If the timer has already expired, return immediately */ + if ((now.tv_sec > t->tmout.tv_sec) || + (now.tv_sec == t->tmout.tv_sec && now.tv_usec >= t->tmout.tv_usec)) + { + if (t->flags & SHTIMER_LONGJMP) + sh_longjmp (t->jmpenv, 1); + else + return 0; + } + + /* compute timeout */ + tv.tv_sec = t->tmout.tv_sec - now.tv_sec; + tv.tv_usec = t->tmout.tv_usec - now.tv_usec; + if (tv.tv_usec < 0) + { + tv.tv_sec--; + tv.tv_usec += USEC_PER_SEC; + } + +#if defined (HAVE_PSELECT) + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; +#else + sigemptyset (&prevmask); +#endif /* !HAVE_PSELECT */ + + FD_ZERO (&readfds); + FD_SET (t->fd, &readfds); + +#if defined (HAVE_PSELECT) + r = pselect(t->fd + 1, &readfds, (fd_set *)0, (fd_set *)0, &ts, &blocked_sigs); +#else + sigprocmask (SIG_SETMASK, &blocked_sigs, &prevmask); + r = select(t->fd + 1, &readfds, (fd_set *)0, (fd_set *)0, &tv); + sigprocmask (SIG_SETMASK, &prevmask, NULL); +#endif + + if (r < 0) + return r; /* caller will handle */ + else if (r == 0 && (t->flags & SHTIMER_LONGJMP)) + sh_longjmp (t->jmpenv, 1); + else + return r; +} +#endif /* !HAVE_TIMEVAL || !HAVE_SELECT */ + +int +shtimer_alrm (sh_timer *t) +{ + return 0; +} diff --git a/shell.h b/shell.h index 8b41792f..a9a3d1e7 100644 --- a/shell.h +++ b/shell.h @@ -165,7 +165,8 @@ extern struct user_info current_user; /* Structure in which to save partial parsing state when doing things like PROMPT_COMMAND and bash_execute_unix_command execution. */ -typedef struct _sh_parser_state_t { +typedef struct _sh_parser_state_t +{ /* parsing state */ int parser_state; @@ -208,7 +209,8 @@ typedef struct _sh_parser_state_t { REDIRECT *redir_stack[HEREDOC_MAX]; } sh_parser_state_t; -typedef struct _sh_input_line_state_t { +typedef struct _sh_input_line_state_t +{ char *input_line; size_t input_line_index; size_t input_line_size;