diff options
author | Ron Yorston <rmy@pobox.com> | 2021-09-26 08:33:36 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2021-09-26 08:33:36 +0100 |
commit | 010abea6f548a367d0d89998aa6c38a45d756c0d (patch) | |
tree | 6d86f10e7a7753478c0600cb3659e23d5fe89b43 | |
parent | bcb1d0c76d6e665133d1f878590af2006b31de6e (diff) | |
download | busybox-w32-010abea6f548a367d0d89998aa6c38a45d756c0d.tar.gz busybox-w32-010abea6f548a367d0d89998aa6c38a45d756c0d.tar.bz2 busybox-w32-010abea6f548a367d0d89998aa6c38a45d756c0d.zip |
ash: additional support for background jobs
The rationale for the jobs builtin in POSIX notes:
The jobs utility is not dependent on the job control option,
as are the seemingly related bg and fg utilities because jobs
is useful for examining background jobs, regardless of the
condition of job control. When the user has invoked a set +m
command and job control has been turned off, jobs can still be
used to examine the background jobs associated with that current
session. Similarly, kill can then be used to kill background
jobs with kill %<background job number>.
Although ash in busybox-w32 doesn't support job control it can
handle background jobs.
Allow the ASH_JOB_CONTROL setting to enable certain features:
- the jobs builtin;
- killing of jobs by job id in the kill builtin;
- monitoring of changes to jobs.
Since process groups aren't supported it's necessary to kill the
processes constituting a background job individually. When doing
this we ask kill(2) to kill all children too.
-rw-r--r-- | configs/mingw32_defconfig | 2 | ||||
-rw-r--r-- | configs/mingw64_defconfig | 2 | ||||
-rw-r--r-- | shell/ash.c | 51 |
3 files changed, 44 insertions, 11 deletions
diff --git a/configs/mingw32_defconfig b/configs/mingw32_defconfig index 195020151..d41899fdc 100644 --- a/configs/mingw32_defconfig +++ b/configs/mingw32_defconfig | |||
@@ -1131,7 +1131,7 @@ CONFIG_ASH_INTERNAL_GLOB=y | |||
1131 | CONFIG_ASH_BASH_COMPAT=y | 1131 | CONFIG_ASH_BASH_COMPAT=y |
1132 | # CONFIG_ASH_BASH_SOURCE_CURDIR is not set | 1132 | # CONFIG_ASH_BASH_SOURCE_CURDIR is not set |
1133 | CONFIG_ASH_BASH_NOT_FOUND_HOOK=y | 1133 | CONFIG_ASH_BASH_NOT_FOUND_HOOK=y |
1134 | # CONFIG_ASH_JOB_CONTROL is not set | 1134 | CONFIG_ASH_JOB_CONTROL=y |
1135 | CONFIG_ASH_ALIAS=y | 1135 | CONFIG_ASH_ALIAS=y |
1136 | CONFIG_ASH_RANDOM_SUPPORT=y | 1136 | CONFIG_ASH_RANDOM_SUPPORT=y |
1137 | CONFIG_ASH_EXPAND_PRMT=y | 1137 | CONFIG_ASH_EXPAND_PRMT=y |
diff --git a/configs/mingw64_defconfig b/configs/mingw64_defconfig index aa704fe75..449f16ae6 100644 --- a/configs/mingw64_defconfig +++ b/configs/mingw64_defconfig | |||
@@ -1131,7 +1131,7 @@ CONFIG_ASH_INTERNAL_GLOB=y | |||
1131 | CONFIG_ASH_BASH_COMPAT=y | 1131 | CONFIG_ASH_BASH_COMPAT=y |
1132 | # CONFIG_ASH_BASH_SOURCE_CURDIR is not set | 1132 | # CONFIG_ASH_BASH_SOURCE_CURDIR is not set |
1133 | CONFIG_ASH_BASH_NOT_FOUND_HOOK=y | 1133 | CONFIG_ASH_BASH_NOT_FOUND_HOOK=y |
1134 | # CONFIG_ASH_JOB_CONTROL is not set | 1134 | CONFIG_ASH_JOB_CONTROL=y |
1135 | CONFIG_ASH_ALIAS=y | 1135 | CONFIG_ASH_ALIAS=y |
1136 | CONFIG_ASH_RANDOM_SUPPORT=y | 1136 | CONFIG_ASH_RANDOM_SUPPORT=y |
1137 | CONFIG_ASH_EXPAND_PRMT=y | 1137 | CONFIG_ASH_EXPAND_PRMT=y |
diff --git a/shell/ash.c b/shell/ash.c index fafbeb86e..6be99864f 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -24,7 +24,7 @@ | |||
24 | * - command without ".exe" extension is still understood as executable | 24 | * - command without ".exe" extension is still understood as executable |
25 | * - shell scripts on the path are detected by the presence of '#!' | 25 | * - shell scripts on the path are detected by the presence of '#!' |
26 | * - both / and \ are supported in PATH. Usually you must use / | 26 | * - both / and \ are supported in PATH. Usually you must use / |
27 | * - job control doesn't work | 27 | * - job control doesn't work, though the jobs builtin is available |
28 | * - trap doesn't work for signals, only EXIT | 28 | * - trap doesn't work for signals, only EXIT |
29 | * - /dev/null is supported for redirection | 29 | * - /dev/null is supported for redirection |
30 | * - fake $PPID | 30 | * - fake $PPID |
@@ -212,7 +212,17 @@ | |||
212 | 212 | ||
213 | #define PROFILE 0 | 213 | #define PROFILE 0 |
214 | 214 | ||
215 | /* | ||
216 | * Only one of JOBS or JOBS_WIN32 is enabled at a time (or neither). | ||
217 | * JOBS_WIN32 doesn't enable job control, just some job-related features. | ||
218 | */ | ||
219 | #if ENABLE_PLATFORM_MINGW32 | ||
220 | #define JOBS_WIN32 ENABLE_ASH_JOB_CONTROL | ||
221 | #define JOBS 0 | ||
222 | #else | ||
223 | #define JOBS_WIN32 0 | ||
215 | #define JOBS ENABLE_ASH_JOB_CONTROL | 224 | #define JOBS ENABLE_ASH_JOB_CONTROL |
225 | #endif | ||
216 | 226 | ||
217 | #include <fnmatch.h> | 227 | #include <fnmatch.h> |
218 | #include <sys/times.h> | 228 | #include <sys/times.h> |
@@ -4112,10 +4122,13 @@ static int forkshell(struct job *, union node *, int); | |||
4112 | #endif | 4122 | #endif |
4113 | static int waitforjob(struct job *); | 4123 | static int waitforjob(struct job *); |
4114 | 4124 | ||
4115 | #if !JOBS | 4125 | #if !JOBS && !JOBS_WIN32 |
4116 | enum { doing_jobctl = 0 }; | 4126 | enum { doing_jobctl = 0 }; |
4117 | #define setjobctl(on) do {} while (0) | 4127 | #define setjobctl(on) do {} while (0) |
4118 | #else | 4128 | #elif JOBS_WIN32 |
4129 | static smallint doing_jobctl; //references:8 | ||
4130 | #define setjobctl(on) do { if (rootshell) doing_jobctl = on; } while (0) | ||
4131 | #else /* JOBS */ | ||
4119 | static smallint doing_jobctl; //references:8 | 4132 | static smallint doing_jobctl; //references:8 |
4120 | static void setjobctl(int); | 4133 | static void setjobctl(int); |
4121 | #endif | 4134 | #endif |
@@ -4404,7 +4417,7 @@ set_curjob(struct job *jp, unsigned mode) | |||
4404 | } | 4417 | } |
4405 | } | 4418 | } |
4406 | 4419 | ||
4407 | #if JOBS || DEBUG | 4420 | #if JOBS || JOBS_WIN32 || DEBUG |
4408 | static int | 4421 | static int |
4409 | jobno(const struct job *jp) | 4422 | jobno(const struct job *jp) |
4410 | { | 4423 | { |
@@ -4617,7 +4630,9 @@ setjobctl(int on) | |||
4617 | ttyfd = fd; | 4630 | ttyfd = fd; |
4618 | doing_jobctl = on; | 4631 | doing_jobctl = on; |
4619 | } | 4632 | } |
4633 | #endif | ||
4620 | 4634 | ||
4635 | #if JOBS || JOBS_WIN32 | ||
4621 | static int FAST_FUNC | 4636 | static int FAST_FUNC |
4622 | killcmd(int argc, char **argv) | 4637 | killcmd(int argc, char **argv) |
4623 | { | 4638 | { |
@@ -4647,8 +4662,10 @@ killcmd(int argc, char **argv) | |||
4647 | * sh -c 'true|sleep 1 & sleep 2; kill %1' | 4662 | * sh -c 'true|sleep 1 & sleep 2; kill %1' |
4648 | */ | 4663 | */ |
4649 | n = jp->nprocs; /* can't be 0 (I hope) */ | 4664 | n = jp->nprocs; /* can't be 0 (I hope) */ |
4665 | #if !ENABLE_PLATFORM_MINGW32 | ||
4650 | if (jp->jobctl) | 4666 | if (jp->jobctl) |
4651 | n = 1; | 4667 | n = 1; |
4668 | #endif | ||
4652 | dst = alloca(n * sizeof(int)*4); | 4669 | dst = alloca(n * sizeof(int)*4); |
4653 | argv[i] = dst; | 4670 | argv[i] = dst; |
4654 | for (j = 0; j < n; j++) { | 4671 | for (j = 0; j < n; j++) { |
@@ -4663,7 +4680,11 @@ killcmd(int argc, char **argv) | |||
4663 | * leading space. Needed to not confuse | 4680 | * leading space. Needed to not confuse |
4664 | * negative pids with "kill -SIGNAL_NO" syntax | 4681 | * negative pids with "kill -SIGNAL_NO" syntax |
4665 | */ | 4682 | */ |
4683 | #if !ENABLE_PLATFORM_MINGW32 | ||
4666 | dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid); | 4684 | dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid); |
4685 | #else | ||
4686 | dst += sprintf(dst, " -%u", (int)ps->ps_pid); | ||
4687 | #endif | ||
4667 | } | 4688 | } |
4668 | *dst = '\0'; | 4689 | *dst = '\0'; |
4669 | } | 4690 | } |
@@ -4671,7 +4692,9 @@ killcmd(int argc, char **argv) | |||
4671 | } | 4692 | } |
4672 | return kill_main(argc, argv); | 4693 | return kill_main(argc, argv); |
4673 | } | 4694 | } |
4695 | #endif | ||
4674 | 4696 | ||
4697 | #if JOBS | ||
4675 | static void | 4698 | static void |
4676 | showpipe(struct job *jp /*, FILE *out*/) | 4699 | showpipe(struct job *jp /*, FILE *out*/) |
4677 | { | 4700 | { |
@@ -5048,7 +5071,7 @@ dowait(int block, struct job *jp) | |||
5048 | #endif | 5071 | #endif |
5049 | } | 5072 | } |
5050 | 5073 | ||
5051 | #if JOBS | 5074 | #if JOBS || JOBS_WIN32 |
5052 | static void | 5075 | static void |
5053 | showjob(struct job *jp, int mode) | 5076 | showjob(struct job *jp, int mode) |
5054 | { | 5077 | { |
@@ -5085,8 +5108,10 @@ showjob(struct job *jp, int mode) | |||
5085 | col += sizeof("Running") - 1; | 5108 | col += sizeof("Running") - 1; |
5086 | } else { | 5109 | } else { |
5087 | int status = psend[-1].ps_status; | 5110 | int status = psend[-1].ps_status; |
5111 | #if !ENABLE_PLATFORM_MINGW32 | ||
5088 | if (jp->state == JOBSTOPPED) | 5112 | if (jp->state == JOBSTOPPED) |
5089 | status = jp->stopstatus; | 5113 | status = jp->stopstatus; |
5114 | #endif | ||
5090 | col += sprint_status48(s + col, status, 0); | 5115 | col += sprint_status48(s + col, status, 0); |
5091 | } | 5116 | } |
5092 | /* By now, "[JOBID]* [maybe PID] STATUS" is printed */ | 5117 | /* By now, "[JOBID]* [maybe PID] STATUS" is printed */ |
@@ -5107,12 +5132,16 @@ showjob(struct job *jp, int mode) | |||
5107 | if (mode & SHOW_PIDS) | 5132 | if (mode & SHOW_PIDS) |
5108 | col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1; | 5133 | col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1; |
5109 | start: | 5134 | start: |
5135 | #if !ENABLE_PLATFORM_MINGW32 | ||
5110 | fprintf(out, "%s%*c%s%s", | 5136 | fprintf(out, "%s%*c%s%s", |
5111 | s, | 5137 | s, |
5112 | 33 - col >= 0 ? 33 - col : 0, ' ', | 5138 | 33 - col >= 0 ? 33 - col : 0, ' ', |
5113 | ps == jp->ps ? "" : "| ", | 5139 | ps == jp->ps ? "" : "| ", |
5114 | ps->ps_cmd | 5140 | ps->ps_cmd |
5115 | ); | 5141 | ); |
5142 | #else | ||
5143 | fprintf(out, "%s", s); | ||
5144 | #endif | ||
5116 | } while (++ps != psend); | 5145 | } while (++ps != psend); |
5117 | newline_and_flush(out); | 5146 | newline_and_flush(out); |
5118 | 5147 | ||
@@ -5369,7 +5398,7 @@ makejob(/*union node *node,*/ int nprocs) | |||
5369 | break; | 5398 | break; |
5370 | if (jp->state != JOBDONE || !jp->waited) | 5399 | if (jp->state != JOBDONE || !jp->waited) |
5371 | continue; | 5400 | continue; |
5372 | #if JOBS | 5401 | #if JOBS || JOBS_WIN32 |
5373 | if (doing_jobctl) | 5402 | if (doing_jobctl) |
5374 | continue; | 5403 | continue; |
5375 | #endif | 5404 | #endif |
@@ -10997,7 +11026,7 @@ static const struct builtincmd builtintab[] = { | |||
10997 | #if MAX_HISTORY | 11026 | #if MAX_HISTORY |
10998 | { BUILTIN_NOSPEC "history" , historycmd }, | 11027 | { BUILTIN_NOSPEC "history" , historycmd }, |
10999 | #endif | 11028 | #endif |
11000 | #if JOBS | 11029 | #if JOBS || JOBS_WIN32 |
11001 | { BUILTIN_REGULAR "jobs" , jobscmd }, | 11030 | { BUILTIN_REGULAR "jobs" , jobscmd }, |
11002 | { BUILTIN_REGULAR "kill" , killcmd }, | 11031 | { BUILTIN_REGULAR "kill" , killcmd }, |
11003 | #endif | 11032 | #endif |
@@ -11039,7 +11068,7 @@ static const struct builtincmd builtintab[] = { | |||
11039 | /* [ */ 1 * ENABLE_ASH_TEST + \ | 11068 | /* [ */ 1 * ENABLE_ASH_TEST + \ |
11040 | /* [[ */ 1 * BASH_TEST2 + \ | 11069 | /* [[ */ 1 * BASH_TEST2 + \ |
11041 | /* alias */ 1 * ENABLE_ASH_ALIAS + \ | 11070 | /* alias */ 1 * ENABLE_ASH_ALIAS + \ |
11042 | /* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \ | 11071 | /* bg */ 1 * JOBS + \ |
11043 | /* break cd cddir */ 3) | 11072 | /* break cd cddir */ 3) |
11044 | #define EVALCMD (COMMANDCMD + \ | 11073 | #define EVALCMD (COMMANDCMD + \ |
11045 | /* command */ 1 * ENABLE_ASH_CMDCMD + \ | 11074 | /* command */ 1 * ENABLE_ASH_CMDCMD + \ |
@@ -14365,7 +14394,7 @@ cmdloop(int top) | |||
14365 | int skip; | 14394 | int skip; |
14366 | 14395 | ||
14367 | setstackmark(&smark); | 14396 | setstackmark(&smark); |
14368 | #if JOBS | 14397 | #if JOBS || JOBS_WIN32 |
14369 | if (doing_jobctl) | 14398 | if (doing_jobctl) |
14370 | showjobs(SHOW_CHANGED|SHOW_STDERR); | 14399 | showjobs(SHOW_CHANGED|SHOW_STDERR); |
14371 | #endif | 14400 | #endif |
@@ -16735,6 +16764,10 @@ forkshell_init(const char *idstr) | |||
16735 | else { | 16764 | else { |
16736 | SetConsoleCtrlHandler(ctrl_handler, TRUE); | 16765 | SetConsoleCtrlHandler(ctrl_handler, TRUE); |
16737 | } | 16766 | } |
16767 | #if JOBS_WIN32 | ||
16768 | /* do job control only in root shell */ | ||
16769 | doing_jobctl = 0; | ||
16770 | #endif | ||
16738 | end: | 16771 | end: |
16739 | forkshell_child(fs); | 16772 | forkshell_child(fs); |
16740 | } | 16773 | } |