fix for crash without arrays built in; allow some job status changes while the jobs list is frozen; change format for interactive shell terminated job notifications; allow some job notifications while running traps

This commit is contained in:
Chet Ramey
2024-05-06 11:15:17 -04:00
parent 1f42d15864
commit d17d185fff
13 changed files with 205 additions and 79 deletions
+50
View File
@@ -9277,3 +9277,53 @@ subst.c
- function_substitute: make sure we unbind the local REPLY we created
at the current (fake) context
From a report by Koichi Murase <myoga.murase@gmail.com>
4/30
----
subst.c
- expand_arrayref: fix crash from freeing memory that's only allocated
and initialized if ARRAY_VARS is defined
Report and patch from Henrik Lindström <henrik@lxm.se>
jobs.h,jobs.c
- JLIST_POSIX: new format argument for pretty_print_job; implements
POSIX requirement that jobs display the status of all background
jobs and all jobs the user hasn't been notified about (unused yet)
- JLIST_BGONLY: new format argument for pretty_print_job; restricts
output to background jobs only (unused yet)
jobs.c,jobs.h,execute_cmd.c,subst.c
- freeze_jobs_list: now takes an int argument with the new value of
jobs_list_frozen; prep for having different values with different
meanings; changed callers
jobs.c
- wait_for_any_job: return right away if jobs_list_frozen > 0; allow
job status changes (e.g., J_NOTIFIED) if jobs_list_frozen < 0; use
this instead of testing executing_funsub directly
- notify_and_cleanup: allow notification and status change if
jobs_list_frozen < 0; don't delete any dead jobs
- should_notify: takes a job index and returns 1 if the shell would
notify the user about it, given the current job state
- pretty_print_job: if the jobs list is frozen, only print status
about jobs for which the shell would notify users (by calling
should_notify())
subst.c
- function_substitute: freeze the jobs list with value -1 so jobs
can change status and possibly inhibit printing by `jobs'
5/2
---
jobs.c
- notify_and_cleanup: allow job notifications if an interactive shell
is running a trap (interactive == 0 && interactive_shell && running_trap)
Fixes report by Koichi Murase <myoga.murase@gmail.com> on 11/14/2022
- print_pipeline: don't print an extra space before the pipeline; push
that into pretty_print_job; print the space after the pid if we
print one
jobs.h
- LONGEST_SIGNAL_DESC: update to 27 (macos SIGPROF). This changes the
test output
+1
View File
@@ -728,6 +728,7 @@ support/mkinstalldirs f 755
support/mkversion.sh f 755
support/mksignames.c f
support/signames.c f
support/siglen.c f
support/bashbug.sh f
support/bashbug.sh.in f
support/man2html.c f
+1 -1
View File
@@ -47,7 +47,7 @@
/* Define DONT_REPORT_SIGTERM if you don't want to see `Terminated' message
when a job exits due to SIGTERM, since that's the default signal sent
by the kill builtin. */
by the kill builtin. Only effective for non-interactive shells. */
#define DONT_REPORT_SIGTERM
/* Define DONT_REPORT_BROKEN_PIPE_WRITE_ERRORS if you don't want builtins
-25
View File
@@ -14,31 +14,6 @@
.if \n(zY=1 .ig zY
.TH BASH 1 "2024 April 23" "GNU Bash 5.3"
.\"
.\" There's some problem with having a `@'
.\" in a tagged paragraph with the BSD man macros.
.\" It has to do with `@' appearing in the }1 macro.
.\" This is a problem on 4.3 BSD and Ultrix, but Sun
.\" appears to have fixed it.
.\" If you're seeing the characters
.\" `@u-3p' appearing before the lines reading
.\" `possible-hostname-completions
.\" and `complete-hostname' down in READLINE,
.\" then uncomment this redefinition.
.\"
.\" .de }1
.\" .ds ]X \&\\*(]B\\
.\" .nr )E 0
.\" .if !"\\$1"" .nr )I \\$1n
.\" .}f
.\" .ll \\n(LLu
.\" .in \\n()Ru+\\n(INu+\\n()Iu
.\" .ti \\n(INu
.\" .ie !\\n()Iu+\\n()Ru-\w\\*(]Xu-3p \{\\*(]X
.\" .br\}
.\" .el \\*(]X\h|\\n()Iu+\\n()Ru\c
.\" .}f
.\" ..
.\"
.ie \n(.g \{\
.ds ' \(aq
.ds " \(dq
+5 -1
View File
@@ -2695,7 +2695,7 @@ execute_pipeline (COMMAND *command, int asynchronous, int pipe_in, int pipe_out,
prev = NO_PIPE;
add_unwind_protect (uw_restore_stdin, (void *) (intptr_t) lstdin);
lastpipe_flag = 1;
old_frozen = freeze_jobs_list ();
old_frozen = freeze_jobs_list (1);
lastpipe_jid = stop_pipeline (0, (COMMAND *)NULL); /* XXX */
add_unwind_protect (uw_lastpipe_cleanup, (void *) (intptr_t) old_frozen);
#if defined (JOB_CONTROL)
@@ -2831,6 +2831,10 @@ execute_connection (COMMAND *command, int asynchronous, int pipe_in, int pipe_ou
#endif
QUIT;
#if defined (JOB_CONTROL)
if (command->value.Connection->connector == ';' && job_control && interactive)
notify_and_cleanup ();
#endif
optimize_connection_fork (command); /* XXX */
exec_result = execute_command_internal (command->value.Connection->second,
asynchronous, pipe_in, pipe_out,
+77 -20
View File
@@ -337,10 +337,11 @@ static SigHandler *old_cont = (SigHandler *)SIG_DFL;
/* A place to temporarily save the current pipeline. */
static struct pipeline_saver *saved_pipeline;
/* Set this to non-zero whenever you don't want the jobs list to change at
/* Set this to 1 whenever you don't want the jobs list to change at
all: no jobs deleted and no status change notifications. This is used,
for example, when executing SIGCHLD traps, which may run arbitrary
commands. */
commands. Set to -1 if you allow status change notifications but no
jobs deleted. 0 means everything is allowed. */
static int jobs_list_frozen;
static char retcode_name_buffer[64];
@@ -1905,19 +1906,26 @@ printable_job_status (int j, PROCESS *p, int format)
/* This is the way to print out information on a job if you
know the index. FORMAT is:
JLIST_NORMAL) [1]+ Running emacs
JLIST_STANDARD) [1]+ Running emacs
JLIST_LONG ) [1]+ 2378 Running emacs
-1 ) [1]+ 2378 emacs
JLIST_NORMAL) [1]+ Stopped ls | more
JLIST_STANDARD) [1]+ Stopped ls | more
JLIST_LONG ) [1]+ 2369 Stopped ls
2367 | more
JLIST_PID_ONLY)
Just list the pid of the process group leader (really
the process group).
JLIST_CHANGED_ONLY)
Use format JLIST_NORMAL, but list only jobs about which
the user has not been notified. */
Use format JLIST_STANDARD, but list only jobs about which
the user has not been notified.
JLIST_POSIX)
Use format JLIST_STANDARD, list all background jobs, running
and stopped, plus jobs about which the user has not been
notified (that would be notified)
JLIST_BGONLY)
Use format JLIST_STANDARD, but restrict output to background
jobs only. */
/* Print status for pipeline P. If JOB_INDEX is >= 0, it is the index into
the JOBS array corresponding to this pipeline. FORMAT is as described
@@ -1945,9 +1953,14 @@ print_pipeline (PROCESS *p, int job_index, int format, FILE *stream)
fprintf (stream, format ? " " : " |");
if (format != JLIST_STANDARD)
fprintf (stream, "%5ld", (long)p->pid);
{
fprintf (stream, "%5ld", (long)p->pid);
if (p == first)
fprintf (stream, " ");
}
fprintf (stream, " ");
if (p != first)
fprintf (stream, " ");
if (format > -1 && job_index >= 0)
{
@@ -2021,6 +2034,29 @@ print_pipeline (PROCESS *p, int job_index, int format, FILE *stream)
fflush (stream);
}
/* We want to print information about a job if it's running or terminated in
the background, if it's stopped, or if it was a foreground job terminated
due to a signal that we don't ignore (SIGINT and possibly SIGTERM and
SIGPIPE). If we change this, change the conditions in notify_of_job_status()
so they stay consistent.
This is for use by the jobs builtin. */
static int
should_notify (int job)
{
/* Background jobs, stopped jobs whether they were in the foreground or
background. */
if ((IS_FOREGROUND (job) == 0) || STOPPED (job))
return 1;
/* Foreground job killed by a signal we report on. */
if (DEADJOB (job) && IS_FOREGROUND (job) && job_killed_by_signal (job))
return 1;
return 0; /* catch-all */
}
/* Print information to STREAM about jobs[JOB_INDEX] according to FORMAT.
Must be called with SIGCHLD blocked or queued with queue_sigchld */
static void
@@ -2028,6 +2064,13 @@ pretty_print_job (int job_index, int format, FILE *stream)
{
register PROCESS *p;
/* If the jobs list is frozen, skip jobs we would remove and not report on */
if (jobs_list_frozen && should_notify (job_index) == 0)
return;
if (format == JLIST_BGONLY && IS_FOREGROUND (job_index))
return;
/* Format only pid information about the process group leader? */
if (format == JLIST_PID_ONLY)
{
@@ -2041,6 +2084,12 @@ pretty_print_job (int job_index, int format, FILE *stream)
return;
format = JLIST_STANDARD;
}
else if (format == JLIST_POSIX)
{
if (IS_FOREGROUND (job_index) && IS_NOTIFIED (job_index))
return;
format = JLIST_STANDARD;
}
if (format != JLIST_NONINTERACTIVE)
fprintf (stream, "[%d]%c ", job_index + 1,
@@ -2050,8 +2099,10 @@ pretty_print_job (int job_index, int format, FILE *stream)
if (format == JLIST_NONINTERACTIVE)
format = JLIST_LONG;
p = jobs[job_index]->pipe;
if (format == JLIST_STANDARD)
fprintf (stream, "%c", ' '); /* used to be in print_pipeline */
p = jobs[job_index]->pipe;
print_pipeline (p, job_index, format, stream);
/* We have printed information about this job. When the job's
@@ -3275,7 +3326,7 @@ wait_for_any_job (int flags, struct procstat *ps)
sigset_t set, oset;
/* Allow funsubs to run this, but don't remove jobs from the jobs table. */
if (jobs_list_frozen && executing_funsub == 0)
if (jobs_list_frozen > 0)
return -1;
/* First see if there are any unnotified dead jobs that we can report on */
@@ -3297,13 +3348,11 @@ return_job:
if (jobs_list_frozen == 0) /* must be running a funsub to get here */
{
notify_of_job_status (); /* XXX */
#if 1 /* kre@munnari.oz.au 01/30/2024 */
/* kre@munnari.oz.au 01/30/2024 */
delete_job (i, posixly_correct ? DEL_NOBGPID : 0);
#else
delete_job (i, 0);
#endif
}
else
else /* if (jobs_list_frozen < 0) */ /* status changes only */
jobs[i]->flags |= J_NOTIFIED; /* clean up later */
#if defined (COPROCESS_SUPPORT)
coproc_reap ();
@@ -3371,12 +3420,15 @@ return_job:
void
notify_and_cleanup (void)
{
if (jobs_list_frozen)
if (jobs_list_frozen > 0)
return;
if (interactive || interactive_shell == 0 || sourcelevel)
if (interactive || interactive_shell == 0 || sourcelevel || (interactive_shell && running_trap))
notify_of_job_status ();
if (jobs_list_frozen < 0)
return; /* status changes only */
cleanup_dead_jobs ();
}
@@ -4357,14 +4409,19 @@ notify_of_job_status (void)
if (termsig && WIFSIGNALED (s) && termsig != SIGINT && termsig != SIGPIPE)
#endif
{
#if 0
fprintf (stderr, "%s", j_strsignal (termsig));
if (WIFCORED (s))
fprintf (stderr, _(" (core dumped)"));
fprintf (stderr, "\n");
#else
print_pipeline (jobs[job]->pipe, job, JLIST_STANDARD, stderr);
#endif
}
/* foreground jobs that exit cleanly */
/* foreground jobs that exit cleanly or due to a signal we
don't report on */
jobs[job]->flags |= J_NOTIFIED;
}
else if (job_control)
@@ -4970,12 +5027,12 @@ itrace("mark_dead_jobs_as_notified: child_max = %d ndead = %d ndeadproc = %d", j
/* Here to allow other parts of the shell (like the trap stuff) to
freeze and unfreeze the jobs list. */
int
freeze_jobs_list (void)
freeze_jobs_list (int n)
{
int o;
o = jobs_list_frozen;
jobs_list_frozen = 1;
jobs_list_frozen = n;
return o;
}
+8 -3
View File
@@ -34,9 +34,14 @@
#define JLIST_PID_ONLY 2
#define JLIST_CHANGED_ONLY 3
#define JLIST_NONINTERACTIVE 4
#define JLIST_POSIX 5
#define JLIST_BGONLY 6
/* I looked it up. For pretty_print_job (). The real answer is 24. */
#define LONGEST_SIGNAL_DESC 24
/* I looked it up. For pretty_print_job (). The real answer is 27
(macOS SIGPROF) but the makefile or configure can override it. */
#ifndef LONGEST_SIGNAL_DESC
# define LONGEST_SIGNAL_DESC 27
#endif
/* Defines for the wait_for_* functions and for the wait builtin to use */
#define JWAIT_PERROR (1 << 0)
@@ -301,7 +306,7 @@ extern int give_terminal_to (pid_t, int);
extern void run_sigchld_trap (int);
extern int freeze_jobs_list (void);
extern int freeze_jobs_list (int);
extern int unfreeze_jobs_list (void);
extern void set_jobs_list_frozen (int);
extern int jobs_list_frozen_status (void);
+1 -1
View File
@@ -1010,7 +1010,7 @@ describe_pid (pid_t pid)
}
int
freeze_jobs_list (void)
freeze_jobs_list (int n)
{
return 0;
}
+2
View File
@@ -7242,9 +7242,11 @@ flush_parser_state (sh_parser_state_t *ps)
FREE (ps->token_state);
ps->token_state = 0;
#if defined (ARRAY_VARS)
if (ps->pipestatus)
array_dispose (ps->pipestatus);
ps->pipestatus = 0;
#endif
/* We want to free the saved token and leave the current token for error
reporting and cleanup. */
+2 -2
View File
@@ -448,7 +448,7 @@ main (int argc, char **argv, char **env)
/* Fix for the `infinite process creation' bug when running shell scripts
from startup files on System V. */
login_shell = make_login_shell = 0;
login_shell = make_login_shell = su_shell = 0;
/* If this shell has already been run, then reinitialize it to a
vanilla state. */
@@ -1996,7 +1996,7 @@ shell_reinitialize (void)
no_rc = no_profile = 1;
/* Things that get 0. */
login_shell = make_login_shell = executing = 0;
login_shell = make_login_shell = su_shell = executing = 0;
debugging = debugging_mode = 0;
do_version = line_number = last_command_exit_value = 0;
forced_interactive = interactive_shell = interactive = 0;
+5 -1
View File
@@ -7088,8 +7088,10 @@ function_substitute (char *string, int quoted, int flags)
add_unwind_protect (uw_unbind_localvar, "REPLY");
}
old_frozen = freeze_jobs_list ();
#if 1 /* TAG:bash-5.3 myoga.murase@gmail.com 04/30/2024 */
old_frozen = freeze_jobs_list (-1);
add_unwind_protect (uw_lastpipe_cleanup, (void *) (intptr_t) old_frozen);
#endif
#if defined (JOB_CONTROL)
unwind_protect_var (pipeline_pgrp);
@@ -7806,7 +7808,9 @@ expand_arrayref:
temp = quote_var_value (temp, quoted, pflags);
#if defined (ARRAY_VARS)
FREE (tt);
#endif
}
else
temp = (char *)NULL;
+28
View File
@@ -0,0 +1,28 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
int
main(int argc, char **argv)
{
int i, l, longest, verb;
size_t len;
char *msg;
verb = (argc > 1 && argv[1] && strcmp (argv[1], "-l") == 0);
longest = 0;
for (i = 0; i < NSIG; i++) {
msg = strsignal (i);
len = msg ? strlen (msg) : 0;
l = len;
if (msg && verb)
printf ("%d\t%s\n", l, msg);
if (l > longest)
longest = l;
}
printf ("%d\n", longest);
exit (0);
}
+25 -25
View File
@@ -16,17 +16,17 @@ Waiting for job 6
job 6 returns 0
Waiting for job 7
job 7 returns 0
[1] Running sleep 2 &
[2] Running sleep 2 &
[3] Running sleep 2 &
[4]- Running sleep 2 &
[5]+ Running ( sleep 2; exit 4 ) &
[1] Running sleep 2 &
[2] Running sleep 2 &
[3] Running sleep 2 &
[4]- Running sleep 2 &
[5]+ Running ( sleep 2; exit 4 ) &
4
0
i killed it
12
[1]- Running sleep 20 &
[3]+ Running sleep 20 &
[1]- Running sleep 20 &
[3]+ Running sleep 20 &
5: ok 1
./jobs5.sub: line 40: wait: %8: no such job
2: ok 2
@@ -53,7 +53,7 @@ async list wait for child
forked
wait-when-no-children
posix jobs output
[1]+ Done sleep 1
[1]+ Done sleep 1
wait-for-job
./jobs.tests: line 96: wait: %2: no such job
127
@@ -84,36 +84,36 @@ wait-for-non-child
./jobs.tests: line 150: wait: pid 1 is not a child of this shell
127
3 -- 1 2 3 -- 1 - 2 - 3
[1] Running sleep 300 &
[2]- Running sleep 350 &
[3]+ Running sleep 400 &
[1] Running sleep 300 &
[2]- Running sleep 350 &
[3]+ Running sleep 400 &
running jobs:
[1] Running sleep 300 &
[2]- Running sleep 350 &
[3]+ Running sleep 400 &
[1] Running sleep 300 &
[2]- Running sleep 350 &
[3]+ Running sleep 400 &
./jobs.tests: line 167: kill: %4: no such job
./jobs.tests: line 169: jobs: %4: no such job
current job:
[3]+ Running sleep 400 &
[3]+ Running sleep 400 &
previous job:
[2]- Running sleep 350 &
[2]- Running sleep 350 &
after kill -STOP
running jobs:
[1] Running sleep 300 &
[3]- Running sleep 400 &
[1] Running sleep 300 &
[3]- Running sleep 400 &
stopped jobs:
[2]+ Stopped sleep 350
[2]+ Stopped sleep 350
after disown
[2]+ Stopped sleep 350
[3]- Running sleep 400 &
[2]+ Stopped sleep 350
[3]- Running sleep 400 &
running jobs:
[3]- Running sleep 400 &
[3]- Running sleep 400 &
stopped jobs:
[2]+ Stopped sleep 350
[2]+ Stopped sleep 350
after kill -s CONT
running jobs:
[2]+ Running sleep 350 &
[3]- Running sleep 400 &
[2]+ Running sleep 350 &
[3]- Running sleep 400 &
stopped jobs:
after kill -STOP, backgrounding %3:
[3]+ sleep 400 &