diff --git a/man/remind.1.in b/man/remind.1.in index 0680691f..0ccdaa42 100644 --- a/man/remind.1.in +++ b/man/remind.1.in @@ -758,11 +758,12 @@ are treated as the beginnings of new paragraphs, and the \fB$FirstIndent\fR indentation is used for the next line. You can use two consecutive newlines to have spaced paragraphs emitted from a single reminder body. .PP -A \fBRUN\fR-type -reminder also passes the \fIbody\fR through the substitution filter, but -then executes the result as a system command. A \fBCAL\fR-type reminder -is used only to place entries in the calendar produced when \fBRemind\fR -is run with the \fB\-c\fR, \fB\-s\fR or \fB\-p\fR options. +A \fBRUN\fR-type reminder also passes the \fIbody\fR through the +substitution filter, but then executes the result as a system command. +A \fBCAL\fR-type reminder is used only to place entries in the +calendar produced when \fBRemind\fR is run with the \fB\-c\fR, +\fB\-s\fR or \fB\-p\fR options. When \fBRemind\fR runs a command, it +sets the command's standard input to come from /dev/null. .PP A \fBPS\fR or \fBPSFILE\fR-type reminder is used to pass PostScript code directly to the printer when producing PostScript calendars. This can @@ -4621,6 +4622,9 @@ been used, \fBshell()\fR will result in an error, and \fIcmd\fR will not be executed. .RS .PP +When \fBshell\fR runs \fIcmd\fR, it arranges for \fIcmd\fR's standard +input file descriptor to be connected to /dev/null. +.PP If \fImaxlen\fR is specified, then \fBshell()\fR returns the first \fImaxlen\fR characters of output (rather than the first 511). If \fImaxlen\fR is specified as a negative number, then it defaults to diff --git a/src/funcs.c b/src/funcs.c index b456c497..f1184f82 100644 --- a/src/funcs.c +++ b/src/funcs.c @@ -2412,6 +2412,10 @@ static int FShell(func_info *info) used a static buffer for reading results from shell() command */ int maxlen = 511; + /* Redirect stdin to /dev/null */ + int stdin_dup; + int devnull = -1; + DBufInit(&buf); if (RunDisabled) return E_RUN_DISABLED; ASSERT_TYPE(0, STR_TYPE); @@ -2428,8 +2432,25 @@ static int FShell(func_info *info) } } + stdin_dup = dup(STDIN_FILENO); + if (stdin_dup >= 0) { + devnull = open("/dev/null", O_RDONLY); + if (devnull >= 0) { + if (dup2(devnull, STDIN_FILENO) >= 0) { + set_cloexec(stdin_dup); + } + (void) close(devnull); + } + } + fp = popen(ARGSTR(0), "r"); - if (!fp) return E_IO_ERR; + if (!fp) { + if (stdin_dup >= 0) { + (void) dup2(stdin_dup, STDIN_FILENO); + (void) close(stdin_dup); + } + return E_IO_ERR; + } while (1) { ch = getc(fp); if (ch == EOF) { @@ -2438,6 +2459,10 @@ static int FShell(func_info *info) if (isspace(ch)) ch = ' '; if (DBufPutc(&buf, (char) ch) != OK) { pclose(fp); + if (stdin_dup >= 0) { + (void) dup2(stdin_dup, STDIN_FILENO); + (void) close(stdin_dup); + } DBufFree(&buf); return E_NO_MEM; } @@ -2454,6 +2479,10 @@ static int FShell(func_info *info) /* XXX Should we consume remaining output from cmd? */ pclose(fp); + if (stdin_dup >= 0) { + (void) dup2(stdin_dup, STDIN_FILENO); + (void) close(stdin_dup); + } r = RetStrVal(DBufValue(&buf), info); DBufFree(&buf); return r; diff --git a/tests/test-rem b/tests/test-rem index 34864599..19875461 100644 --- a/tests/test-rem +++ b/tests/test-rem @@ -848,6 +848,10 @@ SET $WarningLevel today() SET $WarningLevel "3.1.4" EOF +# Make sure shell() and RUN redirect stdin to /dev/null +(echo 'set a shell("cat")'; sleep 1; echo 'rem msg devnull test [a]') | $REMIND - 2025-10-01 >> ../tests/test.out 2>&1 +(echo 'REM RUN cat'; sleep 1; echo 'rem msg devnull test b') | $REMIND - 2025-10-01 >> ../tests/test.out 2>&1 + cmp -s ../tests/test.out ../tests/test.cmp if [ "$?" = "0" ]; then echo "Remind: Acceptance test PASSED" diff --git a/tests/test.cmp b/tests/test.cmp index a3a75911..f9393c14 100644 --- a/tests/test.cmp +++ b/tests/test.cmp @@ -39907,3 +39907,11 @@ SET $WarningLevel today() SET $WarningLevel "3.1.4" -stdin-(8): Ill-formed number +Reminders for Wednesday, 1st October, 2025: + +devnull test + +Reminders for Wednesday, 1st October, 2025: + +devnull test b +