aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2026-01-27 12:13:54 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2026-01-27 13:15:13 +0100
commitdee016821955e7e8de5f8e133e1b2de7b42c0c94 (patch)
tree94975661d06e44bfea45cb3c177736b570025d31 /shell
parent97a78139719deeb2dae02fda6299f174809320fb (diff)
downloadbusybox-w32-dee016821955e7e8de5f8e133e1b2de7b42c0c94.tar.gz
busybox-w32-dee016821955e7e8de5f8e133e1b2de7b42c0c94.tar.bz2
busybox-w32-dee016821955e7e8de5f8e133e1b2de7b42c0c94.zip
ash: reorder functions to reduce forward declarations, no code changes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
-rw-r--r--shell/ash.c903
1 files changed, 453 insertions, 450 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 7cfd1bc42..4122b4dcf 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -76,6 +76,11 @@
76//config: bool "Job control" 76//config: bool "Job control"
77//config: default y 77//config: default y
78//config: depends on SHELL_ASH 78//config: depends on SHELL_ASH
79//config: help
80//config: Enable 'fg', 'bg', 'jobs' and 'kill' builtins.
81//config: Shell will track whether backgrounded pipes are stopped
82//config: by signals, and allow to restart them by 'fg' or 'bg'.
83//config: Otherwise, it will only track whether they have terminated.
79//config: 84//config:
80//config:config ASH_ALIAS 85//config:config ASH_ALIAS
81//config: bool "Alias support" 86//config: bool "Alias support"
@@ -908,6 +913,7 @@ out2str(const char *p)
908#define VSREPLACE 0xc /* ${var/pattern/replacement} */ 913#define VSREPLACE 0xc /* ${var/pattern/replacement} */
909#define VSREPLACEALL 0xd /* ${var//pattern/replacement} */ 914#define VSREPLACEALL 0xd /* ${var//pattern/replacement} */
910#endif 915#endif
916#if DEBUG || JOBS
911static const char vstype_suffix[][3] ALIGN1 = { 917static const char vstype_suffix[][3] ALIGN1 = {
912 [VSNORMAL - VSNORMAL] = "}", // $var or ${var} 918 [VSNORMAL - VSNORMAL] = "}", // $var or ${var}
913 [VSMINUS - VSNORMAL] = "-", // ${var-text} 919 [VSMINUS - VSNORMAL] = "-", // ${var-text}
@@ -919,14 +925,15 @@ static const char vstype_suffix[][3] ALIGN1 = {
919 [VSTRIMLEFT - VSNORMAL] = "#", // ${var#pattern} 925 [VSTRIMLEFT - VSNORMAL] = "#", // ${var#pattern}
920 [VSTRIMLEFTMAX - VSNORMAL] = "##",// ${var##pattern} 926 [VSTRIMLEFTMAX - VSNORMAL] = "##",// ${var##pattern}
921 [VSLENGTH - VSNORMAL] = "", // ${#var} 927 [VSLENGTH - VSNORMAL] = "", // ${#var}
922#if BASH_SUBSTR 928# if BASH_SUBSTR
923 [VSSUBSTR - VSNORMAL] = ":", // ${var:position:length} 929 [VSSUBSTR - VSNORMAL] = ":", // ${var:position:length}
924#endif 930# endif
925#if BASH_PATTERN_SUBST 931# if BASH_PATTERN_SUBST
926 [VSREPLACE - VSNORMAL] = "/", // ${var/pattern/replacement} 932 [VSREPLACE - VSNORMAL] = "/", // ${var/pattern/replacement}
927 [VSREPLACEALL - VSNORMAL] = "//",// ${var//pattern/replacement} 933 [VSREPLACEALL - VSNORMAL] = "//",// ${var//pattern/replacement}
928#endif 934# endif
929}; 935};
936#endif
930 937
931static const char dolatstr[] ALIGN1 = { 938static const char dolatstr[] ALIGN1 = {
932 CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0' 939 CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0'
@@ -3723,63 +3730,6 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3723 3730
3724#endif /* ASH_ALIAS */ 3731#endif /* ASH_ALIAS */
3725 3732
3726/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3727#define FORK_FG 0
3728#define FORK_BG 1
3729#define FORK_NOJOB 2
3730
3731/* mode flags for showjob(s) */
3732#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3733#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3734#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
3735#define SHOW_STDERR 0x08 /* print to stderr (else stdout) */
3736
3737/*
3738 * A job structure contains information about a job. A job is either a
3739 * single process or a set of processes contained in a pipeline. In the
3740 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3741 * array of pids.
3742 */
3743struct procstat {
3744 pid_t ps_pid; /* process id */
3745 int ps_status; /* last process status from wait() */
3746 char *ps_cmd; /* text of command being run */
3747};
3748
3749struct job {
3750 struct procstat ps0; /* status of process */
3751 struct procstat *ps; /* status of processes when more than one */
3752#if JOBS
3753 int stopstatus; /* status of a stopped job */
3754#endif
3755 unsigned nprocs; /* number of processes */
3756
3757#define JOBRUNNING 0 /* at least one proc running */
3758#define JOBSTOPPED 1 /* all procs are stopped */
3759#define JOBDONE 2 /* all procs are completed */
3760 unsigned
3761 state: 8,
3762#if JOBS
3763 sigint: 1, /* job was killed by SIGINT */
3764 jobctl: 1, /* job running under job control */
3765#endif
3766 waited: 1, /* true if this entry has been waited for */
3767 used: 1, /* true if this entry is in used */
3768 changed: 1; /* true if status has changed */
3769 struct job *prev_job; /* previous job */
3770};
3771
3772static int forkshell(struct job *, union node *, int);
3773static int waitforjob(struct job *);
3774
3775#if !JOBS
3776enum { jobctl = 0 };
3777#define setjobctl(on) do {} while (0)
3778#else
3779static smallint jobctl; //references:8
3780static void setjobctl(int);
3781#endif
3782
3783/* 3733/*
3784 * Ignore a signal. 3734 * Ignore a signal.
3785 */ 3735 */
@@ -3948,12 +3898,61 @@ setsignal(int signo)
3948 sigaction_set(signo, &act); 3898 sigaction_set(signo, &act);
3949} 3899}
3950 3900
3951/* mode flags for set_curjob */ 3901/* Mode flags for set_curjob */
3952#define CUR_DELETE 2 3902#define CUR_DELETE 2
3953#define CUR_RUNNING 1 3903#define CUR_RUNNING 1
3954#define CUR_STOPPED 0 3904#define CUR_STOPPED 0
3955 3905
3906/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3907#define FORK_FG 0
3908#define FORK_BG 1
3909#define FORK_NOJOB 2
3910
3911/* Mode flags for showjob(s) */
3912#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3913#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3914#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
3915#define SHOW_STDERR 0x08 /* print to stderr (else stdout) */
3916
3917/*
3918 * A job structure contains information about a job. A job is either a
3919 * single process or a set of processes contained in a pipeline. In the
3920 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3921 * array of pids.
3922 */
3923struct procstat {
3924 pid_t ps_pid; /* process id */
3925 int ps_status; /* last process status from wait() */
3926 char *ps_cmd; /* text of command being run */
3927};
3928
3929struct job {
3930 struct procstat ps0; /* status of process */
3931 struct procstat *ps; /* status of processes when more than one */
3932#if JOBS
3933 int stopstatus; /* status of a stopped job */
3934#endif
3935 unsigned nprocs; /* number of processes */
3936
3937#define JOBRUNNING 0 /* at least one proc running */
3938#define JOBSTOPPED 1 /* all procs are stopped */
3939#define JOBDONE 2 /* all procs are completed */
3940 unsigned
3941 state: 8,
3956#if JOBS 3942#if JOBS
3943 sigint: 1, /* job was killed by SIGINT */
3944 jobctl: 1, /* job running under job control */
3945#endif
3946 waited: 1, /* true if this entry has been waited for */
3947 used: 1, /* true if this entry is in used */
3948 changed: 1; /* true if status has changed */
3949 struct job *prev_job; /* previous job */
3950};
3951
3952#if !JOBS
3953enum { jobctl = 0 };
3954#else
3955static smallint jobctl; //references:8
3957/* pgrp of shell on invocation */ 3956/* pgrp of shell on invocation */
3958static int initialpgrp; //references:2 3957static int initialpgrp; //references:2
3959static int ttyfd = -1; //5 3958static int ttyfd = -1; //5
@@ -4192,7 +4191,9 @@ freejob(struct job *jp)
4192 INTON; 4191 INTON;
4193} 4192}
4194 4193
4195#if JOBS 4194#if !JOBS
4195# define setjobctl(on) ((void)0)
4196#else
4196static void 4197static void
4197xtcsetpgrp(int fd, pid_t pgrp) 4198xtcsetpgrp(int fd, pid_t pgrp)
4198{ 4199{
@@ -4346,75 +4347,6 @@ killcmd(int argc, char **argv)
4346 } 4347 }
4347 return kill_main(argc, argv); 4348 return kill_main(argc, argv);
4348} 4349}
4349
4350static void
4351showpipe(struct job *jp /*, FILE *out*/)
4352{
4353 struct procstat *ps;
4354 struct procstat *psend;
4355
4356 psend = jp->ps + jp->nprocs;
4357 for (ps = jp->ps + 1; ps < psend; ps++)
4358 printf(" | %s", ps->ps_cmd);
4359 newline_and_flush(stdout);
4360 flush_stdout_stderr();
4361}
4362
4363
4364static int
4365restartjob(struct job *jp, int mode)
4366{
4367 struct procstat *ps;
4368 int i;
4369 int status;
4370 pid_t pgid;
4371
4372 INTOFF;
4373 if (jp->state == JOBDONE)
4374 goto out;
4375 jp->state = JOBRUNNING;
4376 pgid = jp->ps[0].ps_pid;
4377 if (mode == FORK_FG) {
4378 get_tty_state();
4379 xtcsetpgrp(ttyfd, pgid);
4380 }
4381 killpg(pgid, SIGCONT);
4382 ps = jp->ps;
4383 i = jp->nprocs;
4384 do {
4385 if (WIFSTOPPED(ps->ps_status)) {
4386 ps->ps_status = -1;
4387 }
4388 ps++;
4389 } while (--i);
4390 out:
4391 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
4392 INTON;
4393 return status;
4394}
4395
4396static int FAST_FUNC
4397fg_bgcmd(int argc UNUSED_PARAM, char **argv)
4398{
4399 struct job *jp;
4400 int mode;
4401 int retval;
4402
4403 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
4404 nextopt(nullstr);
4405 argv = argptr;
4406 do {
4407 jp = getjob(*argv, 1);
4408 if (mode == FORK_BG) {
4409 set_curjob(jp, CUR_RUNNING);
4410 printf("[%d] ", jobno(jp));
4411 }
4412 out1str(jp->ps[0].ps_cmd);
4413 showpipe(jp /*, stdout*/);
4414 retval = restartjob(jp, mode);
4415 } while (*argv && *++argv);
4416 return retval;
4417}
4418#endif 4350#endif
4419 4351
4420static int 4352static int
@@ -4451,6 +4383,44 @@ sprint_status48(char *os, int status, int sigonly)
4451 return s - os; 4383 return s - os;
4452} 4384}
4453 4385
4386/* Called only on finished or stopped jobs (no members are running) */
4387static int
4388getstatus(struct job *job)
4389{
4390 int status;
4391 int retval;
4392 struct procstat *ps;
4393
4394 /* Fetch last member's status */
4395 ps = job->ps + job->nprocs - 1;
4396 status = ps->ps_status;
4397 if (pipefail) {
4398 /* "set -o pipefail" mode: use last _nonzero_ status */
4399 while (status == 0 && --ps >= job->ps)
4400 status = ps->ps_status;
4401 }
4402
4403 retval = WEXITSTATUS(status);
4404 if (!WIFEXITED(status)) {
4405#if JOBS
4406 retval = WSTOPSIG(status);
4407 if (!WIFSTOPPED(status))
4408#endif
4409 {
4410 /* XXX: limits number of signals */
4411 retval = WTERMSIG(status);
4412#if JOBS
4413 if (retval == SIGINT)
4414 job->sigint = 1;
4415#endif
4416 }
4417 retval |= 128;
4418 }
4419 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
4420 jobno(job), job->nprocs, status, retval));
4421 return retval;
4422}
4423
4454/* Inside dowait(): */ 4424/* Inside dowait(): */
4455#define DOWAIT_NONBLOCK 0 /* waitpid() will use WNOHANG and won't wait for signals */ 4425#define DOWAIT_NONBLOCK 0 /* waitpid() will use WNOHANG and won't wait for signals */
4456#define DOWAIT_BLOCK 1 /* waitpid() will NOT use WNOHANG */ 4426#define DOWAIT_BLOCK 1 /* waitpid() will NOT use WNOHANG */
@@ -4637,6 +4607,90 @@ static int dowait(int block, struct job *jp)
4637 return rpid; 4607 return rpid;
4638} 4608}
4639 4609
4610/*
4611 * Wait for job to finish.
4612 *
4613 * Under job control we have the problem that while a child process
4614 * is running interrupts generated by the user are sent to the child
4615 * but not to the shell. This means that an infinite loop started by
4616 * an interactive user may be hard to kill. With job control turned off,
4617 * an interactive user may place an interactive program inside a loop.
4618 * If the interactive program catches interrupts, the user doesn't want
4619 * these interrupts to also abort the loop. The approach we take here
4620 * is to have the shell ignore interrupt signals while waiting for a
4621 * foreground process to terminate, and then send itself an interrupt
4622 * signal if the child process was terminated by an interrupt signal.
4623 * Unfortunately, some programs want to do a bit of cleanup and then
4624 * exit on interrupt; unless these processes terminate themselves by
4625 * sending a signal to themselves (instead of calling exit) they will
4626 * confuse this approach.
4627 *
4628 * Called with interrupts off.
4629 */
4630static int
4631waitforjob(struct job *jp)
4632{
4633 int st;
4634
4635 TRACE(("waitforjob(%%%d) called\n", jp ? jobno(jp) : 0));
4636
4637 /* In non-interactive shells, we _can_ get
4638 * a keyboard signal here and be EINTRed, but we just loop
4639 * inside dowait(), waiting for command to complete.
4640 *
4641 * man bash:
4642 * "If bash is waiting for a command to complete and receives
4643 * a signal for which a trap has been set, the trap
4644 * will not be executed until the command completes."
4645 *
4646 * Reality is that even if trap is not set, bash
4647 * will not act on the signal until command completes.
4648 * Try this. sleep5intoff.c:
4649 * #include <signal.h>
4650 * #include <unistd.h>
4651 * int main() {
4652 * sigset_t set;
4653 * sigemptyset(&set);
4654 * sigaddset(&set, SIGINT);
4655 * sigaddset(&set, SIGQUIT);
4656 * sigprocmask(SIG_BLOCK, &set, NULL);
4657 * sleep(5);
4658 * return 0;
4659 * }
4660 * $ bash -c './sleep5intoff; echo hi'
4661 * ^C^C^C^C <--- pressing ^C once a second
4662 * $ _
4663 * $ bash -c './sleep5intoff; echo hi'
4664 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4665 * $ _
4666 */
4667 dowait(jp ? DOWAIT_BLOCK : DOWAIT_NONBLOCK, jp);
4668 if (!jp)
4669 return exitstatus;
4670
4671 st = getstatus(jp);
4672#if JOBS
4673 if (jp->jobctl) {
4674 xtcsetpgrp(ttyfd, rootpid);
4675 restore_tty_if_stopped_or_signaled(jp);
4676
4677 /*
4678 * This is truly gross.
4679 * If we're doing job control, then we did a TIOCSPGRP which
4680 * caused us (the shell) to no longer be in the controlling
4681 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4682 * intuit from the subprocess exit status whether a SIGINT
4683 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4684 */
4685 if (jp->sigint) /* TODO: do the same with all signals */
4686 raise(SIGINT); /* ... by raise(jp->sig) instead? */
4687 }
4688 if (jp->state == JOBDONE)
4689#endif
4690 freejob(jp);
4691 return st;
4692}
4693
4640#if JOBS 4694#if JOBS
4641static void 4695static void
4642showjob(struct job *jp, int mode) 4696showjob(struct job *jp, int mode)
@@ -4758,43 +4812,95 @@ jobscmd(int argc UNUSED_PARAM, char **argv)
4758 4812
4759 return 0; 4813 return 0;
4760} 4814}
4761#endif /* JOBS */
4762 4815
4763/* Called only on finished or stopped jobs (no members are running) */
4764static int 4816static int
4765getstatus(struct job *job) 4817restartjob(struct job *jp, int mode)
4766{ 4818{
4767 int status;
4768 int retval;
4769 struct procstat *ps; 4819 struct procstat *ps;
4820 int i;
4821 int status;
4822 pid_t pgid;
4770 4823
4771 /* Fetch last member's status */ 4824 INTOFF;
4772 ps = job->ps + job->nprocs - 1; 4825 if (jp->state == JOBDONE)
4773 status = ps->ps_status; 4826 goto out;
4774 if (pipefail) { 4827 jp->state = JOBRUNNING;
4775 /* "set -o pipefail" mode: use last _nonzero_ status */ 4828 pgid = jp->ps[0].ps_pid;
4776 while (status == 0 && --ps >= job->ps) 4829 if (mode == FORK_FG) {
4777 status = ps->ps_status; 4830 get_tty_state();
4831 xtcsetpgrp(ttyfd, pgid);
4778 } 4832 }
4833 killpg(pgid, SIGCONT);
4834 ps = jp->ps;
4835 i = jp->nprocs;
4836 do {
4837 if (WIFSTOPPED(ps->ps_status)) {
4838 ps->ps_status = -1;
4839 }
4840 ps++;
4841 } while (--i);
4842 out:
4843 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
4844 INTON;
4845 return status;
4846}
4779 4847
4780 retval = WEXITSTATUS(status); 4848static void
4781 if (!WIFEXITED(status)) { 4849showpipe(struct job *jp /*, FILE *out*/)
4782#if JOBS 4850{
4783 retval = WSTOPSIG(status); 4851 struct procstat *ps;
4784 if (!WIFSTOPPED(status)) 4852 struct procstat *psend;
4785#endif 4853
4786 { 4854 psend = jp->ps + jp->nprocs;
4787 /* XXX: limits number of signals */ 4855 for (ps = jp->ps + 1; ps < psend; ps++)
4788 retval = WTERMSIG(status); 4856 printf(" | %s", ps->ps_cmd);
4789#if JOBS 4857 newline_and_flush(stdout);
4790 if (retval == SIGINT) 4858 flush_stdout_stderr();
4791 job->sigint = 1; 4859}
4792#endif 4860
4861static int FAST_FUNC
4862fg_bgcmd(int argc UNUSED_PARAM, char **argv)
4863{
4864 struct job *jp;
4865 int mode;
4866 int retval;
4867
4868 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
4869 nextopt(nullstr);
4870 argv = argptr;
4871 do {
4872 jp = getjob(*argv, 1);
4873 if (mode == FORK_BG) {
4874 set_curjob(jp, CUR_RUNNING);
4875 printf("[%d] ", jobno(jp));
4793 } 4876 }
4794 retval |= 128; 4877 out1str(jp->ps[0].ps_cmd);
4878 showpipe(jp /*, stdout*/);
4879 retval = restartjob(jp, mode);
4880 } while (*argv && *++argv);
4881 return retval;
4882}
4883#endif
4884
4885/*
4886 * return 1 if there are stopped jobs, otherwise 0
4887 */
4888static int
4889stoppedjobs(void)
4890{
4891 struct job *jp;
4892 int retval;
4893
4894 retval = 0;
4895 if (!iflag || job_warning)
4896 goto out;
4897 jp = curjob;
4898 if (jp && jp->state == JOBSTOPPED) {
4899 out2str("You have stopped jobs.\n");
4900 job_warning = 2;
4901 retval++;
4795 } 4902 }
4796 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n", 4903 out:
4797 jobno(job), job->nprocs, status, retval));
4798 return retval; 4904 return retval;
4799} 4905}
4800 4906
@@ -5526,140 +5632,6 @@ forkshell(struct job *jp, union node *n, int mode)
5526 return pid; 5632 return pid;
5527} 5633}
5528 5634
5529static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN;
5530
5531static struct job*
5532vforkexec(union node *n, char **argv, const char *path, int idx)
5533{
5534 struct job *jp;
5535 int pid;
5536
5537 jp = makejob(1);
5538
5539 sigblockall(NULL);
5540 vforked = 1;
5541
5542 pid = vfork();
5543
5544 if (!pid) {
5545 forkchild(jp, n, FORK_FG);
5546 sigclearmask();
5547 shellexec(argv[0], argv, path, idx);
5548 /* NOTREACHED */
5549 }
5550
5551 vforked = 0;
5552 sigclearmask();
5553 forkparent(jp, n, FORK_FG, pid);
5554
5555 return jp;
5556}
5557
5558/*
5559 * Wait for job to finish.
5560 *
5561 * Under job control we have the problem that while a child process
5562 * is running interrupts generated by the user are sent to the child
5563 * but not to the shell. This means that an infinite loop started by
5564 * an interactive user may be hard to kill. With job control turned off,
5565 * an interactive user may place an interactive program inside a loop.
5566 * If the interactive program catches interrupts, the user doesn't want
5567 * these interrupts to also abort the loop. The approach we take here
5568 * is to have the shell ignore interrupt signals while waiting for a
5569 * foreground process to terminate, and then send itself an interrupt
5570 * signal if the child process was terminated by an interrupt signal.
5571 * Unfortunately, some programs want to do a bit of cleanup and then
5572 * exit on interrupt; unless these processes terminate themselves by
5573 * sending a signal to themselves (instead of calling exit) they will
5574 * confuse this approach.
5575 *
5576 * Called with interrupts off.
5577 */
5578static int
5579waitforjob(struct job *jp)
5580{
5581 int st;
5582
5583 TRACE(("waitforjob(%%%d) called\n", jp ? jobno(jp) : 0));
5584
5585 /* In non-interactive shells, we _can_ get
5586 * a keyboard signal here and be EINTRed, but we just loop
5587 * inside dowait(), waiting for command to complete.
5588 *
5589 * man bash:
5590 * "If bash is waiting for a command to complete and receives
5591 * a signal for which a trap has been set, the trap
5592 * will not be executed until the command completes."
5593 *
5594 * Reality is that even if trap is not set, bash
5595 * will not act on the signal until command completes.
5596 * Try this. sleep5intoff.c:
5597 * #include <signal.h>
5598 * #include <unistd.h>
5599 * int main() {
5600 * sigset_t set;
5601 * sigemptyset(&set);
5602 * sigaddset(&set, SIGINT);
5603 * sigaddset(&set, SIGQUIT);
5604 * sigprocmask(SIG_BLOCK, &set, NULL);
5605 * sleep(5);
5606 * return 0;
5607 * }
5608 * $ bash -c './sleep5intoff; echo hi'
5609 * ^C^C^C^C <--- pressing ^C once a second
5610 * $ _
5611 * $ bash -c './sleep5intoff; echo hi'
5612 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
5613 * $ _
5614 */
5615 dowait(jp ? DOWAIT_BLOCK : DOWAIT_NONBLOCK, jp);
5616 if (!jp)
5617 return exitstatus;
5618
5619 st = getstatus(jp);
5620#if JOBS
5621 if (jp->jobctl) {
5622 xtcsetpgrp(ttyfd, rootpid);
5623 restore_tty_if_stopped_or_signaled(jp);
5624
5625 /*
5626 * This is truly gross.
5627 * If we're doing job control, then we did a TIOCSPGRP which
5628 * caused us (the shell) to no longer be in the controlling
5629 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
5630 * intuit from the subprocess exit status whether a SIGINT
5631 * occurred, and if so interrupt ourselves. Yuck. - mycroft
5632 */
5633 if (jp->sigint) /* TODO: do the same with all signals */
5634 raise(SIGINT); /* ... by raise(jp->sig) instead? */
5635 }
5636 if (jp->state == JOBDONE)
5637#endif
5638 freejob(jp);
5639 return st;
5640}
5641
5642/*
5643 * return 1 if there are stopped jobs, otherwise 0
5644 */
5645static int
5646stoppedjobs(void)
5647{
5648 struct job *jp;
5649 int retval;
5650
5651 retval = 0;
5652 if (!iflag || job_warning)
5653 goto out;
5654 jp = curjob;
5655 if (jp && jp->state == JOBSTOPPED) {
5656 out2str("You have stopped jobs.\n");
5657 job_warning = 2;
5658 retval++;
5659 }
5660 out:
5661 return retval;
5662}
5663 5635
5664/* 5636/*
5665 * Code for dealing with input/output redirection. 5637 * Code for dealing with input/output redirection.
@@ -6232,6 +6204,193 @@ unwindredir(struct redirtab *stop)
6232} 6204}
6233 6205
6234 6206
6207/*
6208 * execve wrappers
6209 */
6210#if ENABLE_FEATURE_SH_STANDALONE
6211static void
6212tryexec_applet(int applet_no, const char *cmd, char **argv, char **envp)
6213{
6214 if (!vforked && APPLET_IS_NOEXEC(applet_no)) {
6215 dbg_show_dirtymem("dirtymem in NOEXEC tryexec");
6216 clearenv();
6217 while (*envp)
6218 putenv(*envp++);
6219 popredir(/*drop:*/ 1);
6220//FIXME: exec resets all non-DFL, non-IGN signal handlers to DFL,
6221//but we _don't_ exec, such signals will reach ash's handler instead!
6222//Maybe add code there to set the handler to DFL, and signal itself?
6223// This works for "CMD [| CMD]..." pipes:
6224//vforkexec():
6225// vfork();
6226// forkchild(jp, n, FORK_FG); // this resets TSTP,TTOU,INT,TERM,QUIT to DFL
6227// shellexec(argv[0], argv, path, idx)
6228// tryexec_applet()
6229// we are here
6230// And for "exec CMD":
6231//execcmd():
6232// iflag = 0;
6233// mflag = 0;
6234// optschanged(); // this resets TSTP,TTOU,INT,TERM to DFL
6235// shlvl++;
6236// setsignal(SIGQUIT); // this resets QUIT to DFL
6237// shellexec() ->
6238// tryexec_applet() ->
6239// we are here
6240// But ash -c 'LAST_CMD_WONT_FORK' does not work!
6241//evalcommand():
6242// if (!(flags & EV_EXIT) || may_have_traps)
6243// // we don't use this branch
6244// //else:
6245// shellexec() ->
6246// tryexec_applet() ->
6247// we are here
6248//SIGINT works 'by accident' (sets DFL+signals itself)
6249//SIGQUIT is IGNORED!
6250//Fixed by adding in the evalcommand() before that shellexec():
6251// shlvl++;
6252// setsignal(SIGQUIT);
6253// //TODO: setsignal(TSTP,TTOU,INT,TERM) too?
6254//
6255// With traps set, this:
6256// ash -c 'trap "echo HERE!" INT; exec xargs'
6257// didn't work: ^C sets a "run trap later" flag and _returns_,
6258// which is not expected by the NOEXEC'ed xargs applet! (It gets EINTR on read).
6259// clear_traps() helps with this:
6260 clear_traps();
6261 run_noexec_applet_and_exit(applet_no, cmd, argv);
6262 /* does not return */
6263 }
6264 /* re-exec ourselves with the new arguments */
6265 execve(bb_busybox_exec_path, argv, envp);
6266 /* If they called chroot or otherwise made the binary
6267 * no longer executable, return.
6268 */
6269}
6270#endif
6271
6272static void
6273tryexec(const char *cmd, char **argv, char **envp)
6274{
6275 repeat:
6276#ifdef SYSV
6277 do {
6278 execve(cmd, argv, envp);
6279 } while (errno == EINTR);
6280#else
6281 execve(cmd, argv, envp);
6282#endif
6283 if (cmd != bb_busybox_exec_path && errno == ENOEXEC) {
6284 /* Run "cmd" as a shell script:
6285 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
6286 * "If the execve() function fails with ENOEXEC, the shell
6287 * shall execute a command equivalent to having a shell invoked
6288 * with the command name as its first operand,
6289 * with any remaining arguments passed to the new shell"
6290 *
6291 * That is, do not use $SHELL, user's shell, or /bin/sh;
6292 * just call ourselves.
6293 *
6294 * Note that bash reads ~80 chars of the file, and if it sees
6295 * a zero byte before it sees newline, it doesn't try to
6296 * interpret it, but fails with "cannot execute binary file"
6297 * message and exit code 126. For one, this prevents attempts
6298 * to interpret foreign ELF binaries as shell scripts.
6299 */
6300 argv[0] = (char*) cmd;
6301 cmd = bb_busybox_exec_path;
6302 /* NB: this is only possible because all callers of shellexec()
6303 * ensure that the argv[-1] slot exists!
6304 */
6305 argv--;
6306 argv[0] = (char*) "ash";
6307 goto repeat;
6308 }
6309}
6310
6311/*
6312 * Exec a program. Never returns. If you change this routine, you may
6313 * have to change the find_command routine as well.
6314 * argv[-1] must exist and be writable! See tryexec() for why.
6315 */
6316static void shellexec(char *prog, char **argv, const char *path, int idx)
6317{
6318 char *cmdname;
6319 int e;
6320 char **envp;
6321 int exerrno;
6322
6323 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL);
6324 if (strchr(prog, '/') != NULL) {
6325 tryexec(prog, argv, envp);
6326 e = errno;
6327 } else {
6328#if ENABLE_FEATURE_SH_STANDALONE
6329 int applet_no = find_applet_by_name(prog);
6330 if (applet_no >= 0)
6331 tryexec_applet(applet_no, prog, argv, envp);
6332 /* We tried execing ourself, but it didn't work.
6333 * Maybe /proc/self/exe doesn't exist?
6334 */
6335#endif
6336 e = ENOENT;
6337 while (padvance(&path, argv[0]) >= 0) {
6338 cmdname = stackblock();
6339 if (--idx < 0 && pathopt == NULL) {
6340 tryexec(cmdname, argv, envp);
6341 if (errno != ENOENT && errno != ENOTDIR)
6342 e = errno;
6343 }
6344 }
6345 }
6346
6347 /* Map to POSIX errors */
6348 switch (e) {
6349 default:
6350 exerrno = 126;
6351 break;
6352 case ELOOP:
6353 case ENAMETOOLONG:
6354 case ENOENT:
6355 case ENOTDIR:
6356 exerrno = 127;
6357 break;
6358 }
6359 exitstatus = exerrno;
6360 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
6361 prog, e, suppress_int));
6362 ash_msg_and_raise(EXEND, "%s: %s", prog, errmsg(e, E_EXEC));
6363 /* NOTREACHED */
6364}
6365
6366static struct job*
6367vforkexec(union node *n, char **argv, const char *path, int idx)
6368{
6369 struct job *jp;
6370 int pid;
6371
6372 jp = makejob(1);
6373
6374 sigblockall(NULL);
6375 vforked = 1;
6376
6377 pid = vfork();
6378
6379 if (!pid) {
6380 forkchild(jp, n, FORK_FG);
6381 sigclearmask();
6382 shellexec(argv[0], argv, path, idx);
6383 /* NOTREACHED */
6384 }
6385
6386 vforked = 0;
6387 sigclearmask();
6388 forkparent(jp, n, FORK_FG, pid);
6389
6390 return jp;
6391}
6392
6393
6235/* ============ Routines to expand arguments to commands 6394/* ============ Routines to expand arguments to commands
6236 * 6395 *
6237 * We have to deal with backquotes, shell variables, and file metacharacters. 6396 * We have to deal with backquotes, shell variables, and file metacharacters.
@@ -8476,162 +8635,6 @@ static struct tblentry **cmdtable;
8476static int builtinloc = -1; /* index in path of %builtin, or -1 */ 8635static int builtinloc = -1; /* index in path of %builtin, or -1 */
8477 8636
8478 8637
8479#if ENABLE_FEATURE_SH_STANDALONE
8480static void
8481tryexec_applet(int applet_no, const char *cmd, char **argv, char **envp)
8482{
8483 if (!vforked && APPLET_IS_NOEXEC(applet_no)) {
8484 dbg_show_dirtymem("dirtymem in NOEXEC tryexec");
8485 clearenv();
8486 while (*envp)
8487 putenv(*envp++);
8488 popredir(/*drop:*/ 1);
8489//FIXME: exec resets all non-DFL, non-IGN signal handlers to DFL,
8490//but we _don't_ exec, such signals will reach ash's handler instead!
8491//Maybe add code there to set the handler to DFL, and signal itself?
8492// This works for "CMD [| CMD]..." pipes:
8493//vforkexec():
8494// vfork();
8495// forkchild(jp, n, FORK_FG); // this resets TSTP,TTOU,INT,TERM,QUIT to DFL
8496// shellexec(argv[0], argv, path, idx)
8497// tryexec_applet()
8498// we are here
8499// And for "exec CMD":
8500//execcmd():
8501// iflag = 0;
8502// mflag = 0;
8503// optschanged(); // this resets TSTP,TTOU,INT,TERM to DFL
8504// shlvl++;
8505// setsignal(SIGQUIT); // this resets QUIT to DFL
8506// shellexec() ->
8507// tryexec_applet() ->
8508// we are here
8509// But ash -c 'LAST_CMD_WONT_FORK' does not work!
8510//evalcommand():
8511// if (!(flags & EV_EXIT) || may_have_traps)
8512// // we don't use this branch
8513// //else:
8514// shellexec() ->
8515// tryexec_applet() ->
8516// we are here
8517//SIGINT works 'by accident' (sets DFL+signals itself)
8518//SIGQUIT is IGNORED!
8519//Fixed by adding in the evalcommand() before that shellexec():
8520// shlvl++;
8521// setsignal(SIGQUIT);
8522// //TODO: setsignal(TSTP,TTOU,INT,TERM) too?
8523//
8524// With traps set, this:
8525// ash -c 'trap "echo HERE!" INT; exec xargs'
8526// didn't work: ^C sets a "run trap later" flag and _returns_,
8527// which is not expected by the NOEXEC'ed xargs applet! (It gets EINTR on read).
8528// clear_traps() helps with this:
8529 clear_traps();
8530 run_noexec_applet_and_exit(applet_no, cmd, argv);
8531 /* does not return */
8532 }
8533 /* re-exec ourselves with the new arguments */
8534 execve(bb_busybox_exec_path, argv, envp);
8535 /* If they called chroot or otherwise made the binary
8536 * no longer executable, return.
8537 */
8538}
8539#endif
8540
8541static void
8542tryexec(const char *cmd, char **argv, char **envp)
8543{
8544 repeat:
8545#ifdef SYSV
8546 do {
8547 execve(cmd, argv, envp);
8548 } while (errno == EINTR);
8549#else
8550 execve(cmd, argv, envp);
8551#endif
8552 if (cmd != bb_busybox_exec_path && errno == ENOEXEC) {
8553 /* Run "cmd" as a shell script:
8554 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
8555 * "If the execve() function fails with ENOEXEC, the shell
8556 * shall execute a command equivalent to having a shell invoked
8557 * with the command name as its first operand,
8558 * with any remaining arguments passed to the new shell"
8559 *
8560 * That is, do not use $SHELL, user's shell, or /bin/sh;
8561 * just call ourselves.
8562 *
8563 * Note that bash reads ~80 chars of the file, and if it sees
8564 * a zero byte before it sees newline, it doesn't try to
8565 * interpret it, but fails with "cannot execute binary file"
8566 * message and exit code 126. For one, this prevents attempts
8567 * to interpret foreign ELF binaries as shell scripts.
8568 */
8569 argv[0] = (char*) cmd;
8570 cmd = bb_busybox_exec_path;
8571 /* NB: this is only possible because all callers of shellexec()
8572 * ensure that the argv[-1] slot exists!
8573 */
8574 argv--;
8575 argv[0] = (char*) "ash";
8576 goto repeat;
8577 }
8578}
8579
8580/*
8581 * Exec a program. Never returns. If you change this routine, you may
8582 * have to change the find_command routine as well.
8583 * argv[-1] must exist and be writable! See tryexec() for why.
8584 */
8585static void shellexec(char *prog, char **argv, const char *path, int idx)
8586{
8587 char *cmdname;
8588 int e;
8589 char **envp;
8590 int exerrno;
8591
8592 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL);
8593 if (strchr(prog, '/') != NULL) {
8594 tryexec(prog, argv, envp);
8595 e = errno;
8596 } else {
8597#if ENABLE_FEATURE_SH_STANDALONE
8598 int applet_no = find_applet_by_name(prog);
8599 if (applet_no >= 0)
8600 tryexec_applet(applet_no, prog, argv, envp);
8601 /* We tried execing ourself, but it didn't work.
8602 * Maybe /proc/self/exe doesn't exist?
8603 */
8604#endif
8605 e = ENOENT;
8606 while (padvance(&path, argv[0]) >= 0) {
8607 cmdname = stackblock();
8608 if (--idx < 0 && pathopt == NULL) {
8609 tryexec(cmdname, argv, envp);
8610 if (errno != ENOENT && errno != ENOTDIR)
8611 e = errno;
8612 }
8613 }
8614 }
8615
8616 /* Map to POSIX errors */
8617 switch (e) {
8618 default:
8619 exerrno = 126;
8620 break;
8621 case ELOOP:
8622 case ENAMETOOLONG:
8623 case ENOENT:
8624 case ENOTDIR:
8625 exerrno = 127;
8626 break;
8627 }
8628 exitstatus = exerrno;
8629 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
8630 prog, e, suppress_int));
8631 ash_msg_and_raise(EXEND, "%s: %s", prog, errmsg(e, E_EXEC));
8632 /* NOTREACHED */
8633}
8634
8635static void 8638static void
8636printentry(struct tblentry *cmdp) 8639printentry(struct tblentry *cmdp)
8637{ 8640{