From 704d10989e0db83bd6460e847a9f89017e290474 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sun, 11 Apr 2021 22:26:36 +0200 Subject: 48504: use SEEK_ macros in fseek() calls --- Src/input.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Src/input.c') diff --git a/Src/input.c b/Src/input.c index e9989ffe4..f568cc135 100644 --- a/Src/input.c +++ b/Src/input.c @@ -495,9 +495,9 @@ stuff(char *fn) zerr("can't open %s", fn); return 1; } - fseek(in, 0, 2); + fseek(in, 0, SEEK_END); len = ftell(in); - fseek(in, 0, 0); + fseek(in, 0, SEEK_SET); buf = (char *)zalloc(len + 1); if (!(fread(buf, len, 1, in))) { zerr("read error on %s", fn); -- cgit v1.2.3 From e5cd2dd980302f328d232d933f646c3dc02828bf Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Fri, 27 Aug 2021 09:36:06 +0100 Subject: 49290: Replace stdio for buffered shell input. The previous method allowed memory management to interact with signal handlers, causing occasional crashes on some system. Instead, use a simple pre-allocated buffer and raw system calls. --- ChangeLog | 6 +++ Src/init.c | 14 +++---- Src/input.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 123 insertions(+), 19 deletions(-) (limited to 'Src/input.c') diff --git a/ChangeLog b/ChangeLog index 458446401..dd7b630d0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2021-08-27 Peter Stephenson + + * 49290: Src/init.c, Src/input.c: Replace stdio for buffered + shell input to avoid memory management interacting with signal + handlers. + 2021-08-27 Oliver Kiddle * Marlon: 49272: Completion/Base/Utility/_call_program: diff --git a/Src/init.c b/Src/init.c index 3d6c94d04..878a53a37 100644 --- a/Src/init.c +++ b/Src/init.c @@ -1229,7 +1229,7 @@ setupshin(char *runscript) /* * Finish setting up SHIN and its relatives. */ - bshin = SHIN ? fdopen(SHIN, "r") : stdin; + shinbufalloc(); if (isset(SHINSTDIN) && !SHIN && unset(INTERACTIVE)) { #ifdef _IONBF setvbuf(stdin, NULL, _IONBF, 0); @@ -1384,9 +1384,9 @@ init_misc(char *cmd, char *zsh_name) dosetopt(RESTRICTED, 1, 0, opts); if (cmd) { if (SHIN >= 10) - fclose(bshin); + close(SHIN); SHIN = movefd(open("/dev/null", O_RDONLY | O_NOCTTY)); - bshin = fdopen(SHIN, "r"); + shinbufreset(); execstring(cmd, 0, 1, "cmdarg"); stopmsg = 1; zexit((exit_pending || shell_exiting) ? exit_val : lastval, ZEXIT_NORMAL); @@ -1409,7 +1409,6 @@ source(char *s) int tempfd = -1, fd, cj; zlong oldlineno; int oldshst, osubsh, oloops; - FILE *obshin; char *old_scriptname = scriptname, *us; char *old_scriptfilename = scriptfilename; unsigned char *ocs; @@ -1426,7 +1425,6 @@ source(char *s) /* save the current shell state */ fd = SHIN; /* store the shell input fd */ - obshin = bshin; /* store file handle for buffered shell input */ osubsh = subsh; /* store whether we are in a subshell */ cj = thisjob; /* store our current job number */ oldlineno = lineno; /* store our current lineno */ @@ -1439,7 +1437,7 @@ source(char *s) if (!prog) { SHIN = tempfd; - bshin = fdopen(SHIN, "r"); + shinbufsave(); } subsh = 0; lineno = 1; @@ -1507,10 +1505,10 @@ source(char *s) if (prog) freeeprog(prog); else { - fclose(bshin); + close(SHIN); fdtable[SHIN] = FDT_UNUSED; SHIN = fd; /* the shell input fd */ - bshin = obshin; /* file handle for buffered shell input */ + shinbufrestore(); } subsh = osubsh; /* whether we are in a subshell */ thisjob = cj; /* current job number */ diff --git a/Src/input.c b/Src/input.c index f568cc135..3c6ad7002 100644 --- a/Src/input.c +++ b/Src/input.c @@ -80,11 +80,6 @@ /**/ int SHIN; -/* buffered shell input for non-interactive shells */ - -/**/ -FILE *bshin; - /* != 0 means we are reading input from a string */ /**/ @@ -129,7 +124,116 @@ static struct instacks *instack, *instacktop; static int instacksz = INSTACK_INITIAL; -/* Read a line from bshin. Convert tokens and * +/* Size of buffer for non-interactive command input */ + +#define SHINBUFSIZE 8192 + +/* Input buffer for non-interactive command input */ +static char *shinbuffer; + +/* Pointer into shinbuffer */ +static char *shinbufptr; + +/* End of contents read into shinbuffer */ +static char *shinbufendptr; + +/* Entry on SHIN buffer save stack */ +struct shinsaveentry { + /* Next entry on stack */ + struct shinsaveentry *next; + /* Saved shinbuffer */ + char *buffer; + /* Saved shinbufptr */ + char *ptr; + /* Saved shinbufendptr */ + char *endptr; +}; + +/* SHIN buffer save stack */ +struct shinsaveentry *shinsavestack; + +/* Reset the input buffer for SHIN, discarding any pending input */ + +/**/ +void +shinbufreset(void) +{ + shinbufendptr = shinbufptr = shinbuffer; +} + +/* Allocate a new shinbuffer + * + * Only called at shell initialisation and when saving on the stack. + */ + +/**/ +void +shinbufalloc(void) +{ + shinbuffer = zalloc(SHINBUFSIZE); + shinbufreset(); +} + +/* Save entry on SHIN buffer save stack */ + +/**/ +void +shinbufsave(void) +{ + struct shinsaveentry *entry = + (struct shinsaveentry *)zalloc(sizeof(struct shinsaveentry)); + + entry->next = shinsavestack; + entry->buffer = shinbuffer; + entry->ptr = shinbufptr; + entry->endptr = shinbufendptr; + + shinsavestack = entry; + + shinbufalloc(); +} + +/* Restore entry from SHIN buffer save stack */ + +/**/ +void +shinbufrestore(void) +{ + struct shinsaveentry *entry = shinsavestack; + + zfree(shinbuffer, SHINBUFSIZE); + + shinbuffer = entry->buffer; + shinbufptr = entry->ptr; + shinbufendptr = entry->endptr; + + shinsavestack = entry->next; + zfree(entry, sizeof(struct shinsaveentry)); +} + +/* Get a character from SHIN, -1 if none available */ + +/**/ +static int +shingetchar(void) +{ + int nread; + + if (shinbufptr < shinbufendptr) + return STOUC(*shinbufptr++); + + shinbufreset(); + do { + errno = 0; + nread = read(SHIN, shinbuffer, SHINBUFSIZE); + } while (nread < 0 && errno == EINTR); + if (nread <= 0) + return -1; + shinbufendptr = shinbuffer + nread; + return STOUC(*shinbufptr++); +} + +/* Read a line from SHIN. Convert tokens and * * null characters to Meta c^32 character pairs. */ /**/ @@ -147,11 +251,7 @@ shingetline(void) winch_unblock(); dont_queue_signals(); for (;;) { - /* Can't fgets() here because we need to accept '\0' bytes */ - do { - errno = 0; - c = fgetc(bshin); - } while (c < 0 && errno == EINTR); + c = shingetchar(); if (c < 0 || c == '\n') { winch_block(); restore_queue_signals(q); -- cgit v1.2.3 From 309d899507adc62de5a6c37c32386898b44895fd Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Wed, 8 Sep 2021 10:18:51 +0900 Subject: unposted: add 'static' to shinsavestack --- ChangeLog | 2 ++ Src/input.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'Src/input.c') diff --git a/ChangeLog b/ChangeLog index da91b0c6c..fe9a3b4ee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2021-09-08 Jun-ichi Takimoto + * unposted: Src/input.c: add 'static' to shinsavestack + * 49377: Src/Zle/zle_keymap.c, Test/X03zlebindkey.ztst: fix segfalut by 'bindkey -d' with reordered keymapnamtab diff --git a/Src/input.c b/Src/input.c index 3c6ad7002..caeaff0e3 100644 --- a/Src/input.c +++ b/Src/input.c @@ -150,7 +150,7 @@ struct shinsaveentry { }; /* SHIN buffer save stack */ -struct shinsaveentry *shinsavestack; +static struct shinsaveentry *shinsavestack; /* Reset the input buffer for SHIN, discarding any pending input */ -- cgit v1.2.3 From 1640457f4755a4a5248f5e9c0106fa01f8f1e9ff Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 3 Mar 2022 19:19:35 +0000 Subject: 49792: Non-interative shell input is line buffered. --- ChangeLog | 3 +++ Src/input.c | 21 ++++++++++++++------- Test/A01grammar.ztst | 9 +++++++++ 3 files changed, 26 insertions(+), 7 deletions(-) (limited to 'Src/input.c') diff --git a/ChangeLog b/ChangeLog index a5ba0fa6a..ec015533a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2022-03-03 Peter Stephenson + * 49792: Src/input.c, Test/A01grammar.ztst: Use line buffering + for non-interactive input. + * 49787: Test/W02jobs.ztst, Test/W03jobparameters.ztst: test for jobs fix in 49783. diff --git a/Src/input.c b/Src/input.c index caeaff0e3..c59232681 100644 --- a/Src/input.c +++ b/Src/input.c @@ -223,13 +223,20 @@ shingetchar(void) return STOUC(*shinbufptr++); shinbufreset(); - do { - errno = 0; - nread = read(SHIN, shinbuffer, SHINBUFSIZE); - } while (nread < 0 && errno == EINTR); - if (nread <= 0) - return -1; - shinbufendptr = shinbuffer + nread; + for (;;) { + errno = 0; + nread = read(SHIN, shinbufendptr, 1); + if (nread > 0) { + /* Use line buffering (POSIX requirement) */ + if (*shinbufendptr++ == '\n') + break; + if (shinbufendptr == shinbuffer + SHINBUFSIZE) + break; + } else if (nread == 0 || errno != EINTR) + break; + } + if (shinbufendptr == shinbuffer) + return -1; return STOUC(*shinbufptr++); } diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst index 4e39a8f3c..0312fe94e 100644 --- a/Test/A01grammar.ztst +++ b/Test/A01grammar.ztst @@ -961,3 +961,12 @@ F:Note that the behaviour of 'exit' inside try-list inside a function is unspeci F:This test was written to ensure the behaviour doesn't change silently. F:If this test fails during development, it *might* be appropriate to change F:its expectations. + + ( + export VALUE=first + print -l 'echo Value is $VALUE' 'VALUE=second sh' 'echo Value is $VALUE' | + $ZTST_testdir/../Src/zsh -f + ) +0:Non-interactive shell command input is line buffered +>Value is first +>Value is second -- cgit v1.2.3