diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2026-01-27 12:13:54 +0100 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2026-01-27 13:15:13 +0100 |
| commit | dee016821955e7e8de5f8e133e1b2de7b42c0c94 (patch) | |
| tree | 94975661d06e44bfea45cb3c177736b570025d31 /shell | |
| parent | 97a78139719deeb2dae02fda6299f174809320fb (diff) | |
| download | busybox-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.c | 903 |
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 | ||
| 911 | static const char vstype_suffix[][3] ALIGN1 = { | 917 | static 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 | ||
| 931 | static const char dolatstr[] ALIGN1 = { | 938 | static 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 | */ | ||
| 3743 | struct 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 | |||
| 3749 | struct 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 | |||
| 3772 | static int forkshell(struct job *, union node *, int); | ||
| 3773 | static int waitforjob(struct job *); | ||
| 3774 | |||
| 3775 | #if !JOBS | ||
| 3776 | enum { jobctl = 0 }; | ||
| 3777 | #define setjobctl(on) do {} while (0) | ||
| 3778 | #else | ||
| 3779 | static smallint jobctl; //references:8 | ||
| 3780 | static 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 | */ | ||
| 3923 | struct 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 | |||
| 3929 | struct 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 | ||
| 3953 | enum { jobctl = 0 }; | ||
| 3954 | #else | ||
| 3955 | static smallint jobctl; //references:8 | ||
| 3957 | /* pgrp of shell on invocation */ | 3956 | /* pgrp of shell on invocation */ |
| 3958 | static int initialpgrp; //references:2 | 3957 | static int initialpgrp; //references:2 |
| 3959 | static int ttyfd = -1; //5 | 3958 | static 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 | ||
| 4196 | static void | 4197 | static void |
| 4197 | xtcsetpgrp(int fd, pid_t pgrp) | 4198 | xtcsetpgrp(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 | |||
| 4350 | static void | ||
| 4351 | showpipe(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 | |||
| 4364 | static int | ||
| 4365 | restartjob(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 | |||
| 4396 | static int FAST_FUNC | ||
| 4397 | fg_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 | ||
| 4420 | static int | 4352 | static 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) */ | ||
| 4387 | static int | ||
| 4388 | getstatus(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 | */ | ||
| 4630 | static int | ||
| 4631 | waitforjob(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 |
| 4641 | static void | 4695 | static void |
| 4642 | showjob(struct job *jp, int mode) | 4696 | showjob(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) */ | ||
| 4764 | static int | 4816 | static int |
| 4765 | getstatus(struct job *job) | 4817 | restartjob(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); | 4848 | static void |
| 4781 | if (!WIFEXITED(status)) { | 4849 | showpipe(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 | |
| 4861 | static int FAST_FUNC | ||
| 4862 | fg_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 | */ | ||
| 4888 | static int | ||
| 4889 | stoppedjobs(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 | ||
| 5529 | static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN; | ||
| 5530 | |||
| 5531 | static struct job* | ||
| 5532 | vforkexec(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 | */ | ||
| 5578 | static int | ||
| 5579 | waitforjob(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 | */ | ||
| 5645 | static int | ||
| 5646 | stoppedjobs(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 | ||
| 6211 | static void | ||
| 6212 | tryexec_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 | |||
| 6272 | static void | ||
| 6273 | tryexec(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 | */ | ||
| 6316 | static 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 | |||
| 6366 | static struct job* | ||
| 6367 | vforkexec(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; | |||
| 8476 | static int builtinloc = -1; /* index in path of %builtin, or -1 */ | 8635 | static int builtinloc = -1; /* index in path of %builtin, or -1 */ |
| 8477 | 8636 | ||
| 8478 | 8637 | ||
| 8479 | #if ENABLE_FEATURE_SH_STANDALONE | ||
| 8480 | static void | ||
| 8481 | tryexec_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 | |||
| 8541 | static void | ||
| 8542 | tryexec(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 | */ | ||
| 8585 | static 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 | |||
| 8635 | static void | 8638 | static void |
| 8636 | printentry(struct tblentry *cmdp) | 8639 | printentry(struct tblentry *cmdp) |
| 8637 | { | 8640 | { |
