- 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
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
/* 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
.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\a\\*(]X\au-3p \{\\*(]X
-.\" .br\}
-.\" .el \\*(]X\h\a|\\n()Iu+\\n()Ru\a\c
-.\" .}f
-.\" ..
-.\"
.ie \n(.g \{\
.ds ' \(aq
.ds " \(dq
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)
#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,
/* 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];
/* 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
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)
{
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
{
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)
{
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,
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
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 */
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 ();
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 ();
}
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)
/* 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;
}
#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)
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);
}
int
-freeze_jobs_list (void)
+freeze_jobs_list (int n)
{
return 0;
}
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. */
/* 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. */
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;
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);
temp = quote_var_value (temp, quoted, pflags);
+#if defined (ARRAY_VARS)
FREE (tt);
+#endif
}
else
temp = (char *)NULL;
--- /dev/null
+#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);
+}
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
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
./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 &