diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-31 11:22:57 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-31 11:22:57 +0000 |
| commit | d5762932fbcbc0a385047945276f10e2f3fea12d (patch) | |
| tree | 7f48d6b4f8da360f68a4efd662e5d8de773fb001 /shell | |
| parent | d690f68554f1c1301975bc0ab0e479e6870b3589 (diff) | |
| download | busybox-w32-d5762932fbcbc0a385047945276f10e2f3fea12d.tar.gz busybox-w32-d5762932fbcbc0a385047945276f10e2f3fea12d.tar.bz2 busybox-w32-d5762932fbcbc0a385047945276f10e2f3fea12d.zip | |
hush: rework signal and trap handling. Some smaller bits are TODO,
expect minor breakage
function old new delta
set_fatal_sighandler 12 186 +174
check_and_run_traps - 122 +122
maybe_set_sighandler - 76 +76
hush_main 831 887 +56
sigtimedwait - 50 +50
__GI_sigtimedwait - 50 +50
hush_exit 49 93 +44
set_mode 749 777 +28
pseudo_exec_argv 131 151 +20
static.zero_ts - 8 +8
expand_variables 1962 1970 +8
builtin_wait 172 174 +2
set_misc_sighandler 12 - -12
set_jobctrl_sighandler 12 - -12
handler_ctrl_c 16 - -16
builtin_set_mode 28 - -28
handle_trap 97 - -97
handler_ctrl_z 107 - -107
builtin_trap 545 438 -107
run_list 2149 2006 -143
------------------------------------------------------------------------------
(add/remove: 5/6 grow/shrink: 7/2 up/down: 638/-522) Total: 116 bytes
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/hush.c | 584 |
1 files changed, 376 insertions, 208 deletions
diff --git a/shell/hush.c b/shell/hush.c index cd6e12b11..bcccfb0bb 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -460,13 +460,13 @@ struct globals { | |||
| 460 | int last_jobid; | 460 | int last_jobid; |
| 461 | struct pipe *job_list; | 461 | struct pipe *job_list; |
| 462 | struct pipe *toplevel_list; | 462 | struct pipe *toplevel_list; |
| 463 | smallint ctrl_z_flag; | 463 | //// smallint ctrl_z_flag; |
| 464 | #endif | 464 | #endif |
| 465 | #if ENABLE_HUSH_LOOPS | 465 | #if ENABLE_HUSH_LOOPS |
| 466 | smallint flag_break_continue; | 466 | smallint flag_break_continue; |
| 467 | #endif | 467 | #endif |
| 468 | smallint fake_mode; | 468 | smallint fake_mode; |
| 469 | /* these three support $?, $#, and $1 */ | 469 | /* These four support $?, $#, and $1 */ |
| 470 | smalluint last_return_code; | 470 | smalluint last_return_code; |
| 471 | /* is global_argv and global_argv[1..n] malloced? (note: not [0]) */ | 471 | /* is global_argv and global_argv[1..n] malloced? (note: not [0]) */ |
| 472 | smalluint global_args_malloced; | 472 | smalluint global_args_malloced; |
| @@ -489,10 +489,14 @@ struct globals { | |||
| 489 | #endif | 489 | #endif |
| 490 | unsigned char charmap[256]; | 490 | unsigned char charmap[256]; |
| 491 | char user_input_buf[ENABLE_FEATURE_EDITING ? BUFSIZ : 2]; | 491 | char user_input_buf[ENABLE_FEATURE_EDITING ? BUFSIZ : 2]; |
| 492 | struct { | 492 | /* Signal and trap handling */ |
| 493 | char *cmd; | 493 | char **traps; /* char *traps[NSIG] */ |
| 494 | struct sigaction oact; | 494 | /* which signals have non-DFL handler (even with no traps set)? */ |
| 495 | } *traps; | 495 | unsigned non_DFL_mask; |
| 496 | /* which signals are known to be IGNed on entry to shell? */ | ||
| 497 | unsigned IGN_mask; | ||
| 498 | sigset_t blocked_set; | ||
| 499 | sigset_t inherited_set; | ||
| 496 | }; | 500 | }; |
| 497 | 501 | ||
| 498 | #define G (*ptr_to_globals) | 502 | #define G (*ptr_to_globals) |
| @@ -521,11 +525,9 @@ static int builtin_help(char **argv); | |||
| 521 | static int builtin_pwd(char **argv); | 525 | static int builtin_pwd(char **argv); |
| 522 | static int builtin_read(char **argv); | 526 | static int builtin_read(char **argv); |
| 523 | static int builtin_test(char **argv); | 527 | static int builtin_test(char **argv); |
| 524 | static void handle_trap(int sig); | ||
| 525 | static int builtin_trap(char **argv); | 528 | static int builtin_trap(char **argv); |
| 526 | static int builtin_true(char **argv); | 529 | static int builtin_true(char **argv); |
| 527 | static int builtin_set(char **argv); | 530 | static int builtin_set(char **argv); |
| 528 | static int builtin_set_mode(const char, const char); | ||
| 529 | static int builtin_shift(char **argv); | 531 | static int builtin_shift(char **argv); |
| 530 | static int builtin_source(char **argv); | 532 | static int builtin_source(char **argv); |
| 531 | static int builtin_umask(char **argv); | 533 | static int builtin_umask(char **argv); |
| @@ -750,95 +752,270 @@ static void free_strings(char **strings) | |||
| 750 | } | 752 | } |
| 751 | 753 | ||
| 752 | 754 | ||
| 753 | /* Signals are grouped, we handle them in batches */ | 755 | /* Basic theory of signal handling in shell |
| 754 | static void set_misc_sighandler(void (*handler)(int)) | 756 | * ======================================== |
| 757 | * This does not describe what hush does, rahter, it is current understanding | ||
| 758 | * what it _should_ do. | ||
| 759 | * http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#trap | ||
| 760 | * | ||
| 761 | * Signals are handled only after each pipe ("cmd | cmd | cmd" thing) | ||
| 762 | * is finished or backgrounded. It is the same in interactive and | ||
| 763 | * non-interactive shells, and is the same regardless of whether | ||
| 764 | * a user trap handler is installed or a default one is in effect. | ||
| 765 | * ^C or ^Z from keyboard seem to execute "at once" because it usually | ||
| 766 | * backgrounds (i.e. stops) or kills all members of currently running | ||
| 767 | * pipe. | ||
| 768 | * | ||
| 769 | * Wait builtin in interruptible by signals for which user trap is set | ||
| 770 | * or by SIGINT in interactive shell. | ||
| 771 | * | ||
| 772 | * Trap handlers will execute even within trap handlers. (right?) | ||
| 773 | * | ||
| 774 | * User trap handlers are forgotten when subshell is entered. | ||
| 775 | * | ||
| 776 | * If job control is off, backgrounded commands ("cmd &") | ||
| 777 | * have SIGINT, SIGQUIT set to SIG_IGN. | ||
| 778 | * | ||
| 779 | * Commands run in command substitution ("`cmd`") | ||
| 780 | * have SIGTTIN, SIGTTOU, SIGTSTP set to SIG_IGN. | ||
| 781 | * | ||
| 782 | * Ordinary commands have IGN/DFL set as inherited by the shell | ||
| 783 | * from its parent. | ||
| 784 | * | ||
| 785 | * Default handlers which differ from DFL action | ||
| 786 | * (note: subshell is not an interactive shell): | ||
| 787 | * | ||
| 788 | * SIGQUIT: ignore | ||
| 789 | * SIGTERM (interactive): ignore | ||
| 790 | * SUGHUP (interactive): send SIGCONT to stopped jobs, | ||
| 791 | * send SIGHUP to all jobs and exit | ||
| 792 | * SIGTTIN, SIGTTOU, SIGTSTP (if job control is on): ignore | ||
| 793 | * (note that ^Z is handled not by trapping SIGTSTP, but by seeing | ||
| 794 | * that all pipe members are stopped) (right?) | ||
| 795 | * SIGINT (interactive): wait for last pipe, ignore the rest | ||
| 796 | * of the command line, show prompt. (check/expand this) | ||
| 797 | * Example 1: this waits 5 sec, but does not execute ls: | ||
| 798 | * "echo $$; sleep 5; ls -l" + "kill -INT <pid>" | ||
| 799 | * Example 2: this does not wait and does not execute ls: | ||
| 800 | * "echo $$; sleep 5 & wait; ls -l" + "kill -INT <pid>" | ||
| 801 | * | ||
| 802 | * (What happens to signals which are IGN on shell start?) | ||
| 803 | * (What happens with signal mask on shell start?) | ||
| 804 | * | ||
| 805 | * Implementation in hush | ||
| 806 | * ====================== | ||
| 807 | * We use in-kernel pending signal mask to determine which signals were sent. | ||
| 808 | * We block all signals which we don't want to take action immediately, | ||
| 809 | * i.e. we block all signals which need to have special handling as described | ||
| 810 | * above, and all signals which have traps set. | ||
| 811 | * After each pipe execution, we extract any pending signals via sigtimedwait() | ||
| 812 | * and act on them. | ||
| 813 | * | ||
| 814 | * unsigned non_DFL_mask: a mask of such "special" signals | ||
| 815 | * sigset_t blocked_set: current blocked signal set | ||
| 816 | * | ||
| 817 | * "trap - SIGxxx": clear bit in blocked_set unless it is also in non_DFL | ||
| 818 | * "trap 'cmd' SIGxxx": set bit in blocked_set (even if 'cmd' is '') | ||
| 819 | * after [v]fork, if we plan to be a shell: | ||
| 820 | * nothing for {} subshell (say, "true | { true; true; } | true") | ||
| 821 | * unset all traps if () shell. [TODO] | ||
| 822 | * after [v]fork, if we plan to exec: | ||
| 823 | * POSIX says pending signal mask is cleared in child - no need to clear it. | ||
| 824 | * restore blocked signal set to one inherited by shell just prior to exec. | ||
| 825 | * | ||
| 826 | * Note: as a result, we do not use signal handlers much. The only use | ||
| 827 | * is to restore terminal pgrp on exit. | ||
| 828 | * | ||
| 829 | * TODO: check/fix wait builtin to be interruptible. | ||
| 830 | */ | ||
| 831 | |||
| 832 | /* called once at shell init */ | ||
| 833 | static void init_signal_mask(void) | ||
| 755 | { | 834 | { |
| 756 | bb_signals(0 | 835 | unsigned sig; |
| 757 | + (1 << SIGINT) | 836 | unsigned mask = (1 << SIGQUIT); |
| 758 | + (1 << SIGQUIT) | 837 | #if ENABLE_HUSH_INTERACTIVE |
| 759 | + (1 << SIGTERM) | 838 | if (G.interactive_fd) { |
| 760 | , handler); | 839 | mask = 0 |
| 840 | | (1 << SIGQUIT) | ||
| 841 | | (1 << SIGTERM) | ||
| 842 | | (1 << SIGHUP) | ||
| 843 | #if ENABLE_HUSH_JOB | ||
| 844 | | (1 << SIGTTIN) | (1 << SIGTTOU) | (1 << SIGTSTP) | ||
| 845 | #endif | ||
| 846 | | (1 << SIGINT) | ||
| 847 | ; | ||
| 848 | } | ||
| 849 | #endif | ||
| 850 | G.non_DFL_mask = mask; | ||
| 851 | |||
| 852 | /*sigemptyset(&G.blocked_set); - already is */ | ||
| 853 | sigprocmask(SIG_SETMASK, NULL, &G.blocked_set); | ||
| 854 | sig = 0; | ||
| 855 | while (mask) { | ||
| 856 | if (mask & 1) | ||
| 857 | sigaddset(&G.blocked_set, sig); | ||
| 858 | mask >>= 1; | ||
| 859 | sig++; | ||
| 860 | } | ||
| 861 | sigprocmask(SIG_SETMASK, &G.blocked_set, &G.inherited_set); | ||
| 862 | } | ||
| 863 | static void check_and_run_traps(void) | ||
| 864 | { | ||
| 865 | static const struct timespec zero_ts = { 0, 0 }; | ||
| 866 | smalluint save_rcode; | ||
| 867 | int sig; | ||
| 868 | |||
| 869 | while (1) { | ||
| 870 | sig = sigtimedwait(&G.blocked_set, NULL, &zero_ts); | ||
| 871 | if (sig <= 0) | ||
| 872 | break; | ||
| 873 | |||
| 874 | if (G.traps && G.traps[sig]) { | ||
| 875 | if (G.traps[sig][0]) { | ||
| 876 | /* We have user-defined handler */ | ||
| 877 | char *argv[] = { NULL, xstrdup(G.traps[sig]), NULL }; | ||
| 878 | save_rcode = G.last_return_code; | ||
| 879 | builtin_eval(argv); | ||
| 880 | free(argv[1]); | ||
| 881 | G.last_return_code = save_rcode; | ||
| 882 | } /* else: "" trap, ignoring signal */ | ||
| 883 | continue; | ||
| 884 | } | ||
| 885 | /* not a trap: special action */ | ||
| 886 | #if 0 //TODO | ||
| 887 | switch (sig) { | ||
| 888 | case SIGHUP: ... | ||
| 889 | break; | ||
| 890 | case SIGINT: ... | ||
| 891 | break; | ||
| 892 | default: /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */ | ||
| 893 | } | ||
| 894 | #endif | ||
| 895 | } | ||
| 761 | } | 896 | } |
| 762 | 897 | ||
| 898 | /* The stuff below needs to be migrated to "Special action" above */ | ||
| 899 | |||
| 900 | /////* Signals are grouped, we handle them in batches */ | ||
| 901 | ////static void set_misc_sighandler(void (*handler)(int)) | ||
| 902 | ////{ | ||
| 903 | //// bb_signals(0 | ||
| 904 | //// + (1 << SIGINT) | ||
| 905 | //// + (1 << SIGQUIT) | ||
| 906 | //// + (1 << SIGTERM) | ||
| 907 | //// , handler); | ||
| 908 | ////} | ||
| 909 | |||
| 763 | #if ENABLE_HUSH_JOB | 910 | #if ENABLE_HUSH_JOB |
| 764 | 911 | ||
| 912 | /* helper */ | ||
| 913 | static unsigned maybe_set_sighandler(int sig, void (*handler)(int)) | ||
| 914 | { | ||
| 915 | /* non_DFL_mask'ed signals are, well, masked, | ||
| 916 | * no need to set handler for them. | ||
| 917 | * IGN_mask'ed signals were found to be IGN on entry, | ||
| 918 | * do not change that -> no need to set handler for them either | ||
| 919 | */ | ||
| 920 | unsigned ign = ((G.IGN_mask|G.non_DFL_mask) >> sig) & 1; | ||
| 921 | if (!ign) { | ||
| 922 | handler = signal(sig, handler); | ||
| 923 | ign = (handler == SIG_IGN); | ||
| 924 | if (ign) /* restore back to IGN! */ | ||
| 925 | signal(sig, handler); | ||
| 926 | } | ||
| 927 | return ign << sig; /* pass back knowledge about SIG_IGN */ | ||
| 928 | } | ||
| 929 | /* Used only to set handler to restore pgrp on exit, and to reset it to DFL */ | ||
| 765 | static void set_fatal_sighandler(void (*handler)(int)) | 930 | static void set_fatal_sighandler(void (*handler)(int)) |
| 766 | { | 931 | { |
| 767 | bb_signals(0 | 932 | unsigned mask = 0; |
| 768 | + (1 << SIGILL) | 933 | |
| 769 | + (1 << SIGTRAP) | 934 | if (HUSH_DEBUG) { |
| 770 | + (1 << SIGABRT) | 935 | mask |= maybe_set_sighandler(SIGILL , handler); |
| 771 | + (1 << SIGFPE) | 936 | mask |= maybe_set_sighandler(SIGFPE , handler); |
| 772 | + (1 << SIGBUS) | 937 | mask |= maybe_set_sighandler(SIGBUS , handler); |
| 773 | + (1 << SIGSEGV) | 938 | mask |= maybe_set_sighandler(SIGSEGV, handler); |
| 939 | mask |= maybe_set_sighandler(SIGTRAP, handler); | ||
| 940 | } /* else: hush is perfect. what SEGV? */ | ||
| 941 | |||
| 942 | mask |= maybe_set_sighandler(SIGABRT, handler); | ||
| 943 | |||
| 774 | /* bash 3.2 seems to handle these just like 'fatal' ones */ | 944 | /* bash 3.2 seems to handle these just like 'fatal' ones */ |
| 775 | + (1 << SIGHUP) | 945 | mask |= maybe_set_sighandler(SIGPIPE, handler); |
| 776 | + (1 << SIGPIPE) | 946 | mask |= maybe_set_sighandler(SIGALRM, handler); |
| 777 | + (1 << SIGALRM) | 947 | mask |= maybe_set_sighandler(SIGHUP , handler); |
| 778 | , handler); | 948 | |
| 949 | /* if we aren't interactive... but in this case | ||
| 950 | * we never want to restore pgrp on exit, and this fn is not called */ | ||
| 951 | /*mask |= maybe_set_sighandler(SIGTERM, handler); */ | ||
| 952 | /*mask |= maybe_set_sighandler(SIGINT , handler); */ | ||
| 953 | |||
| 954 | G.IGN_mask = mask; | ||
| 779 | } | 955 | } |
| 780 | static void set_jobctrl_sighandler(void (*handler)(int)) | 956 | /* Used only to suppress ^Z in `cmd` */ |
| 957 | static void IGN_jobctrl_signals(void) | ||
| 781 | { | 958 | { |
| 782 | bb_signals(0 | 959 | bb_signals(0 |
| 783 | + (1 << SIGTSTP) | 960 | + (1 << SIGTSTP) |
| 784 | + (1 << SIGTTIN) | 961 | + (1 << SIGTTIN) |
| 785 | + (1 << SIGTTOU) | 962 | + (1 << SIGTTOU) |
| 786 | , handler); | 963 | , SIG_IGN); |
| 787 | } | 964 | } |
| 788 | /* SIGCHLD is special and handled separately */ | 965 | /* SIGCHLD is special and handled separately */ |
| 789 | 966 | ||
| 790 | static void set_every_sighandler(void (*handler)(int)) | 967 | ////static void set_every_sighandler(void (*handler)(int)) |
| 791 | { | 968 | ////{ |
| 792 | set_fatal_sighandler(handler); | 969 | //// set_fatal_sighandler(handler); |
| 793 | set_jobctrl_sighandler(handler); | 970 | //// set_jobctrl_sighandler(handler); |
| 794 | set_misc_sighandler(handler); | 971 | //// set_misc_sighandler(handler); |
| 795 | signal(SIGCHLD, handler); | 972 | //// signal(SIGCHLD, handler); |
| 796 | } | 973 | ////} |
| 797 | 974 | ||
| 798 | static void handler_ctrl_c(int sig UNUSED_PARAM) | 975 | ////static void handler_ctrl_c(int sig UNUSED_PARAM) |
| 799 | { | 976 | ////{ |
| 800 | debug_printf_jobs("got sig %d\n", sig); | 977 | //// debug_printf_jobs("got sig %d\n", sig); |
| 801 | // as usual we can have all kinds of nasty problems with leaked malloc data here | 978 | ////// as usual we can have all kinds of nasty problems with leaked malloc data here |
| 802 | siglongjmp(G.toplevel_jb, 1); | 979 | //// siglongjmp(G.toplevel_jb, 1); |
| 803 | } | 980 | ////} |
| 804 | 981 | ||
| 805 | static void handler_ctrl_z(int sig UNUSED_PARAM) | 982 | ////static void handler_ctrl_z(int sig UNUSED_PARAM) |
| 806 | { | 983 | ////{ |
| 807 | pid_t pid; | 984 | //// pid_t pid; |
| 808 | 985 | //// | |
| 809 | debug_printf_jobs("got tty sig %d in pid %d\n", sig, getpid()); | 986 | //// debug_printf_jobs("got tty sig %d in pid %d\n", sig, getpid()); |
| 810 | 987 | //// | |
| 811 | if (!BB_MMU) { | 988 | //// if (!BB_MMU) { |
| 812 | fputs("Sorry, backgrounding (CTRL+Z) of foreground scripts not supported on nommu\n", stderr); | 989 | //// fputs("Sorry, backgrounding (CTRL+Z) of foreground scripts not supported on nommu\n", stderr); |
| 813 | return; | 990 | //// return; |
| 814 | } | 991 | //// } |
| 815 | 992 | //// | |
| 816 | pid = fork(); | 993 | //// pid = fork(); |
| 817 | if (pid < 0) /* can't fork. Pretend there was no ctrl-Z */ | 994 | //// if (pid < 0) /* can't fork. Pretend there was no ctrl-Z */ |
| 818 | return; | 995 | //// return; |
| 819 | G.ctrl_z_flag = 1; | 996 | //// G.ctrl_z_flag = 1; |
| 820 | if (!pid) { /* child */ | 997 | //// if (!pid) { /* child */ |
| 821 | if (ENABLE_HUSH_JOB) | 998 | //// if (ENABLE_HUSH_JOB) |
| 822 | die_sleep = 0; /* let nofork's xfuncs die */ | 999 | //// die_sleep = 0; /* let nofork's xfuncs die */ |
| 823 | bb_setpgrp(); | 1000 | //// bb_setpgrp(); |
| 824 | debug_printf_jobs("set pgrp for child %d ok\n", getpid()); | 1001 | //// debug_printf_jobs("set pgrp for child %d ok\n", getpid()); |
| 825 | set_every_sighandler(SIG_DFL); | 1002 | //////// set_every_sighandler(SIG_DFL); |
| 826 | raise(SIGTSTP); /* resend TSTP so that child will be stopped */ | 1003 | //// raise(SIGTSTP); /* resend TSTP so that child will be stopped */ |
| 827 | debug_printf_jobs("returning in child\n"); | 1004 | //// debug_printf_jobs("returning in child\n"); |
| 828 | /* return to nofork, it will eventually exit now, | 1005 | //// /* return to nofork, it will eventually exit now, |
| 829 | * not return back to shell */ | 1006 | //// * not return back to shell */ |
| 830 | return; | 1007 | //// return; |
| 831 | } | 1008 | //// } |
| 832 | /* parent */ | 1009 | //// /* parent */ |
| 833 | /* finish filling up pipe info */ | 1010 | //// /* finish filling up pipe info */ |
| 834 | G.toplevel_list->pgrp = pid; /* child is in its own pgrp */ | 1011 | //// G.toplevel_list->pgrp = pid; /* child is in its own pgrp */ |
| 835 | G.toplevel_list->cmds[0].pid = pid; | 1012 | //// G.toplevel_list->cmds[0].pid = pid; |
| 836 | /* parent needs to longjmp out of running nofork. | 1013 | //// /* parent needs to longjmp out of running nofork. |
| 837 | * we will "return" exitcode 0, with child put in background */ | 1014 | //// * we will "return" exitcode 0, with child put in background */ |
| 838 | // as usual we can have all kinds of nasty problems with leaked malloc data here | 1015 | ////// as usual we can have all kinds of nasty problems with leaked malloc data here |
| 839 | debug_printf_jobs("siglongjmp in parent\n"); | 1016 | //// debug_printf_jobs("siglongjmp in parent\n"); |
| 840 | siglongjmp(G.toplevel_jb, 1); | 1017 | //// siglongjmp(G.toplevel_jb, 1); |
| 841 | } | 1018 | ////} |
| 842 | 1019 | ||
| 843 | /* Restores tty foreground process group, and exits. | 1020 | /* Restores tty foreground process group, and exits. |
| 844 | * May be called as signal handler for fatal signal | 1021 | * May be called as signal handler for fatal signal |
| @@ -865,8 +1042,8 @@ static void sigexit(int sig) | |||
| 865 | 1042 | ||
| 866 | #else /* !JOB */ | 1043 | #else /* !JOB */ |
| 867 | 1044 | ||
| 868 | #define set_fatal_sighandler(handler) ((void)0) | 1045 | #define set_fatal_sighandler(handler) ((void)0) |
| 869 | #define set_jobctrl_sighandler(handler) ((void)0) | 1046 | #define IGN_jobctrl_signals(handler) ((void)0) |
| 870 | 1047 | ||
| 871 | #endif /* JOB */ | 1048 | #endif /* JOB */ |
| 872 | 1049 | ||
| @@ -874,8 +1051,11 @@ static void sigexit(int sig) | |||
| 874 | static void hush_exit(int exitcode) NORETURN; | 1051 | static void hush_exit(int exitcode) NORETURN; |
| 875 | static void hush_exit(int exitcode) | 1052 | static void hush_exit(int exitcode) |
| 876 | { | 1053 | { |
| 877 | if (G.traps && G.traps[0].cmd) | 1054 | if (G.traps && G.traps[0] && G.traps[0][0]) { |
| 878 | handle_trap(0); | 1055 | char *argv[] = { NULL, xstrdup(G.traps[0]), NULL }; |
| 1056 | builtin_eval(argv); | ||
| 1057 | free(argv[1]); | ||
| 1058 | } | ||
| 879 | 1059 | ||
| 880 | if (ENABLE_HUSH_JOB) { | 1060 | if (ENABLE_HUSH_JOB) { |
| 881 | fflush(NULL); /* flush all streams */ | 1061 | fflush(NULL); /* flush all streams */ |
| @@ -2052,6 +2232,8 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save, char **argv, int assignme | |||
| 2052 | } | 2232 | } |
| 2053 | #endif | 2233 | #endif |
| 2054 | 2234 | ||
| 2235 | sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); | ||
| 2236 | |||
| 2055 | debug_printf_exec("execing '%s'\n", argv[0]); | 2237 | debug_printf_exec("execing '%s'\n", argv[0]); |
| 2056 | execvp(argv[0], argv); | 2238 | execvp(argv[0], argv); |
| 2057 | bb_perror_msg("can't exec '%s'", argv[0]); | 2239 | bb_perror_msg("can't exec '%s'", argv[0]); |
| @@ -2200,6 +2382,8 @@ static int checkjobs(struct pipe* fg_pipe) | |||
| 2200 | pid_t childpid; | 2382 | pid_t childpid; |
| 2201 | int rcode = 0; | 2383 | int rcode = 0; |
| 2202 | 2384 | ||
| 2385 | debug_printf_jobs("checkjobs %p\n", fg_pipe); | ||
| 2386 | |||
| 2203 | attributes = WUNTRACED; | 2387 | attributes = WUNTRACED; |
| 2204 | if (fg_pipe == NULL) | 2388 | if (fg_pipe == NULL) |
| 2205 | attributes |= WNOHANG; | 2389 | attributes |= WNOHANG; |
| @@ -2459,7 +2643,7 @@ static int run_pipe(struct pipe *pi) | |||
| 2459 | 2643 | ||
| 2460 | /* Disable job control signals for shell (parent) and | 2644 | /* Disable job control signals for shell (parent) and |
| 2461 | * for initial child code after fork */ | 2645 | * for initial child code after fork */ |
| 2462 | set_jobctrl_sighandler(SIG_IGN); | 2646 | //// set_jobctrl_sighandler(SIG_IGN); |
| 2463 | 2647 | ||
| 2464 | /* Going to fork a child per each pipe member */ | 2648 | /* Going to fork a child per each pipe member */ |
| 2465 | pi->alive_cmds = 0; | 2649 | pi->alive_cmds = 0; |
| @@ -2486,9 +2670,9 @@ static int run_pipe(struct pipe *pi) | |||
| 2486 | 2670 | ||
| 2487 | command->pid = BB_MMU ? fork() : vfork(); | 2671 | command->pid = BB_MMU ? fork() : vfork(); |
| 2488 | if (!command->pid) { /* child */ | 2672 | if (!command->pid) { /* child */ |
| 2489 | if (ENABLE_HUSH_JOB) | ||
| 2490 | die_sleep = 0; /* let nofork's xfuncs die */ | ||
| 2491 | #if ENABLE_HUSH_JOB | 2673 | #if ENABLE_HUSH_JOB |
| 2674 | die_sleep = 0; /* let nofork's xfuncs die */ | ||
| 2675 | |||
| 2492 | /* Every child adds itself to new process group | 2676 | /* Every child adds itself to new process group |
| 2493 | * with pgid == pid_of_first_child_in_pipe */ | 2677 | * with pgid == pid_of_first_child_in_pipe */ |
| 2494 | if (G.run_list_level == 1 && G.interactive_fd) { | 2678 | if (G.run_list_level == 1 && G.interactive_fd) { |
| @@ -2514,9 +2698,9 @@ static int run_pipe(struct pipe *pi) | |||
| 2514 | setup_redirects(command, NULL); | 2698 | setup_redirects(command, NULL); |
| 2515 | 2699 | ||
| 2516 | /* Restore default handlers just prior to exec */ | 2700 | /* Restore default handlers just prior to exec */ |
| 2517 | set_jobctrl_sighandler(SIG_DFL); | 2701 | //// set_jobctrl_sighandler(SIG_DFL); |
| 2518 | set_misc_sighandler(SIG_DFL); | 2702 | //// set_misc_sighandler(SIG_DFL); |
| 2519 | signal(SIGCHLD, SIG_DFL); | 2703 | //// signal(SIGCHLD, SIG_DFL); |
| 2520 | /* Stores to nommu_save list of env vars putenv'ed | 2704 | /* Stores to nommu_save list of env vars putenv'ed |
| 2521 | * (NOMMU, on MMU we don't need that) */ | 2705 | * (NOMMU, on MMU we don't need that) */ |
| 2522 | /* cast away volatility... */ | 2706 | /* cast away volatility... */ |
| @@ -2693,6 +2877,8 @@ static int run_list(struct pipe *pi) | |||
| 2693 | * in order to return, no direct "return" statements please. | 2877 | * in order to return, no direct "return" statements please. |
| 2694 | * This helps to ensure that no memory is leaked. */ | 2878 | * This helps to ensure that no memory is leaked. */ |
| 2695 | 2879 | ||
| 2880 | //TODO: needs re-thinking | ||
| 2881 | |||
| 2696 | #if ENABLE_HUSH_JOB | 2882 | #if ENABLE_HUSH_JOB |
| 2697 | /* Example of nested list: "while true; do { sleep 1 | exit 2; } done". | 2883 | /* Example of nested list: "while true; do { sleep 1 | exit 2; } done". |
| 2698 | * We are saving state before entering outermost list ("while...done") | 2884 | * We are saving state before entering outermost list ("while...done") |
| @@ -2713,27 +2899,27 @@ static int run_list(struct pipe *pi) | |||
| 2713 | restore_nofork_data(&G.nofork_save); | 2899 | restore_nofork_data(&G.nofork_save); |
| 2714 | } | 2900 | } |
| 2715 | #endif | 2901 | #endif |
| 2716 | if (G.ctrl_z_flag) { | 2902 | //// if (G.ctrl_z_flag) { |
| 2717 | /* ctrl-Z has forked and stored pid of the child in pi->pid. | 2903 | //// /* ctrl-Z has forked and stored pid of the child in pi->pid. |
| 2718 | * Remember this child as background job */ | 2904 | //// * Remember this child as background job */ |
| 2719 | insert_bg_job(pi); | 2905 | //// insert_bg_job(pi); |
| 2720 | } else { | 2906 | //// } else { |
| 2721 | /* ctrl-C. We just stop doing whatever we were doing */ | 2907 | /* ctrl-C. We just stop doing whatever we were doing */ |
| 2722 | bb_putchar('\n'); | 2908 | bb_putchar('\n'); |
| 2723 | } | 2909 | //// } |
| 2724 | USE_HUSH_LOOPS(loop_top = NULL;) | 2910 | USE_HUSH_LOOPS(loop_top = NULL;) |
| 2725 | USE_HUSH_LOOPS(G.depth_of_loop = 0;) | 2911 | USE_HUSH_LOOPS(G.depth_of_loop = 0;) |
| 2726 | rcode = 0; | 2912 | rcode = 0; |
| 2727 | goto ret; | 2913 | goto ret; |
| 2728 | } | 2914 | } |
| 2729 | /* ctrl-Z handler will store pid etc in pi */ | 2915 | //// /* ctrl-Z handler will store pid etc in pi */ |
| 2730 | G.toplevel_list = pi; | 2916 | //// G.toplevel_list = pi; |
| 2731 | G.ctrl_z_flag = 0; | 2917 | //// G.ctrl_z_flag = 0; |
| 2732 | #if ENABLE_FEATURE_SH_STANDALONE | 2918 | ////#if ENABLE_FEATURE_SH_STANDALONE |
| 2733 | G.nofork_save.saved = 0; /* in case we will run a nofork later */ | 2919 | //// G.nofork_save.saved = 0; /* in case we will run a nofork later */ |
| 2734 | #endif | 2920 | ////#endif |
| 2735 | signal_SA_RESTART_empty_mask(SIGTSTP, handler_ctrl_z); | 2921 | //// signal_SA_RESTART_empty_mask(SIGTSTP, handler_ctrl_z); |
| 2736 | signal(SIGINT, handler_ctrl_c); | 2922 | //// signal(SIGINT, handler_ctrl_c); |
| 2737 | } | 2923 | } |
| 2738 | #endif /* JOB */ | 2924 | #endif /* JOB */ |
| 2739 | 2925 | ||
| @@ -2854,11 +3040,17 @@ static int run_list(struct pipe *pi) | |||
| 2854 | continue; /* not matched yet, skip this pipe */ | 3040 | continue; /* not matched yet, skip this pipe */ |
| 2855 | } | 3041 | } |
| 2856 | #endif | 3042 | #endif |
| 2857 | if (pi->num_cmds == 0) | 3043 | /* Just pressing <enter> in shell should check for jobs. |
| 2858 | goto check_jobs_and_continue; | 3044 | * OTOH, in non-interactive shell this is useless |
| 3045 | * and only leads to extra job checks */ | ||
| 3046 | if (pi->num_cmds == 0) { | ||
| 3047 | if (G.interactive_fd) | ||
| 3048 | goto check_jobs_and_continue; | ||
| 3049 | continue; | ||
| 3050 | } | ||
| 2859 | 3051 | ||
| 2860 | /* After analyzing all keywords and conditions, we decided | 3052 | /* After analyzing all keywords and conditions, we decided |
| 2861 | * to execute this pipe. NB: has to do checkjobs(NULL) | 3053 | * to execute this pipe. NB: have to do checkjobs(NULL) |
| 2862 | * after run_pipe() to collect any background children, | 3054 | * after run_pipe() to collect any background children, |
| 2863 | * even if list execution is to be stopped. */ | 3055 | * even if list execution is to be stopped. */ |
| 2864 | debug_printf_exec(": run_pipe with %d members\n", pi->num_cmds); | 3056 | debug_printf_exec(": run_pipe with %d members\n", pi->num_cmds); |
| @@ -2871,6 +3063,7 @@ static int run_list(struct pipe *pi) | |||
| 2871 | if (r != -1) { | 3063 | if (r != -1) { |
| 2872 | /* we only ran a builtin: rcode is already known | 3064 | /* we only ran a builtin: rcode is already known |
| 2873 | * and we don't need to wait for anything. */ | 3065 | * and we don't need to wait for anything. */ |
| 3066 | check_and_run_traps(); | ||
| 2874 | #if ENABLE_HUSH_LOOPS | 3067 | #if ENABLE_HUSH_LOOPS |
| 2875 | /* was it "break" or "continue"? */ | 3068 | /* was it "break" or "continue"? */ |
| 2876 | if (G.flag_break_continue) { | 3069 | if (G.flag_break_continue) { |
| @@ -2895,6 +3088,7 @@ static int run_list(struct pipe *pi) | |||
| 2895 | /* even bash 3.2 doesn't do that well with nested bg: | 3088 | /* even bash 3.2 doesn't do that well with nested bg: |
| 2896 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". | 3089 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". |
| 2897 | * I'm NOT treating inner &'s as jobs */ | 3090 | * I'm NOT treating inner &'s as jobs */ |
| 3091 | check_and_run_traps(); | ||
| 2898 | #if ENABLE_HUSH_JOB | 3092 | #if ENABLE_HUSH_JOB |
| 2899 | if (G.run_list_level == 1) | 3093 | if (G.run_list_level == 1) |
| 2900 | insert_bg_job(pi); | 3094 | insert_bg_job(pi); |
| @@ -2905,11 +3099,13 @@ static int run_list(struct pipe *pi) | |||
| 2905 | if (G.run_list_level == 1 && G.interactive_fd) { | 3099 | if (G.run_list_level == 1 && G.interactive_fd) { |
| 2906 | /* waits for completion, then fg's main shell */ | 3100 | /* waits for completion, then fg's main shell */ |
| 2907 | rcode = checkjobs_and_fg_shell(pi); | 3101 | rcode = checkjobs_and_fg_shell(pi); |
| 3102 | check_and_run_traps(); | ||
| 2908 | debug_printf_exec(": checkjobs_and_fg_shell returned %d\n", rcode); | 3103 | debug_printf_exec(": checkjobs_and_fg_shell returned %d\n", rcode); |
| 2909 | } else | 3104 | } else |
| 2910 | #endif | 3105 | #endif |
| 2911 | { /* this one just waits for completion */ | 3106 | { /* this one just waits for completion */ |
| 2912 | rcode = checkjobs(pi); | 3107 | rcode = checkjobs(pi); |
| 3108 | check_and_run_traps(); | ||
| 2913 | debug_printf_exec(": checkjobs returned %d\n", rcode); | 3109 | debug_printf_exec(": checkjobs returned %d\n", rcode); |
| 2914 | } | 3110 | } |
| 2915 | } | 3111 | } |
| @@ -2948,17 +3144,18 @@ static int run_list(struct pipe *pi) | |||
| 2948 | } /* for (pi) */ | 3144 | } /* for (pi) */ |
| 2949 | 3145 | ||
| 2950 | #if ENABLE_HUSH_JOB | 3146 | #if ENABLE_HUSH_JOB |
| 2951 | if (G.ctrl_z_flag) { | 3147 | //// if (G.ctrl_z_flag) { |
| 2952 | /* ctrl-Z forked somewhere in the past, we are the child, | 3148 | //// /* ctrl-Z forked somewhere in the past, we are the child, |
| 2953 | * and now we completed running the list. Exit. */ | 3149 | //// * and now we completed running the list. Exit. */ |
| 2954 | //TODO: _exit? | 3150 | //////TODO: _exit? |
| 2955 | exit(rcode); | 3151 | //// exit(rcode); |
| 2956 | } | 3152 | //// } |
| 2957 | ret: | 3153 | ret: |
| 2958 | if (!--G.run_list_level && G.interactive_fd) { | 3154 | G.run_list_level--; |
| 2959 | signal(SIGTSTP, SIG_IGN); | 3155 | //// if (!G.run_list_level && G.interactive_fd) { |
| 2960 | signal(SIGINT, SIG_IGN); | 3156 | //// signal(SIGTSTP, SIG_IGN); |
| 2961 | } | 3157 | //// signal(SIGINT, SIG_IGN); |
| 3158 | //// } | ||
| 2962 | #endif | 3159 | #endif |
| 2963 | debug_printf_exec("run_list lvl %d return %d\n", G.run_list_level + 1, rcode); | 3160 | debug_printf_exec("run_list lvl %d return %d\n", G.run_list_level + 1, rcode); |
| 2964 | #if ENABLE_HUSH_LOOPS | 3161 | #if ENABLE_HUSH_LOOPS |
| @@ -3478,10 +3675,10 @@ static FILE *generate_stream_from_list(struct pipe *head) | |||
| 3478 | /* Process substitution is not considered to be usual | 3675 | /* Process substitution is not considered to be usual |
| 3479 | * 'command execution'. | 3676 | * 'command execution'. |
| 3480 | * SUSv3 says ctrl-Z should be ignored, ctrl-C should not. */ | 3677 | * SUSv3 says ctrl-Z should be ignored, ctrl-C should not. */ |
| 3481 | /* Not needed, we are relying on it being disabled | 3678 | //// /* Not needed, we are relying on it being disabled |
| 3482 | * everywhere outside actual command execution. */ | 3679 | //// * everywhere outside actual command execution. */ |
| 3483 | /*set_jobctrl_sighandler(SIG_IGN);*/ | 3680 | IGN_jobctrl_signals(); |
| 3484 | set_misc_sighandler(SIG_DFL); | 3681 | //// set_misc_sighandler(SIG_DFL); |
| 3485 | /* Freeing 'head' here would break NOMMU. */ | 3682 | /* Freeing 'head' here would break NOMMU. */ |
| 3486 | _exit(run_list(head)); | 3683 | _exit(run_list(head)); |
| 3487 | } | 3684 | } |
| @@ -4287,7 +4484,6 @@ static void setup_job_control(void) | |||
| 4287 | pid_t shell_pgrp; | 4484 | pid_t shell_pgrp; |
| 4288 | 4485 | ||
| 4289 | shell_pgrp = getpgrp(); | 4486 | shell_pgrp = getpgrp(); |
| 4290 | close_on_exec_on(G.interactive_fd); | ||
| 4291 | 4487 | ||
| 4292 | /* If we were ran as 'hush &', | 4488 | /* If we were ran as 'hush &', |
| 4293 | * sleep until we are in the foreground. */ | 4489 | * sleep until we are in the foreground. */ |
| @@ -4298,8 +4494,8 @@ static void setup_job_control(void) | |||
| 4298 | } | 4494 | } |
| 4299 | 4495 | ||
| 4300 | /* Ignore job-control and misc signals. */ | 4496 | /* Ignore job-control and misc signals. */ |
| 4301 | set_jobctrl_sighandler(SIG_IGN); | 4497 | //// set_jobctrl_sighandler(SIG_IGN); |
| 4302 | set_misc_sighandler(SIG_IGN); | 4498 | //// set_misc_sighandler(SIG_IGN); |
| 4303 | //huh? signal(SIGCHLD, SIG_IGN); | 4499 | //huh? signal(SIGCHLD, SIG_IGN); |
| 4304 | 4500 | ||
| 4305 | /* We _must_ restore tty pgrp on fatal signals */ | 4501 | /* We _must_ restore tty pgrp on fatal signals */ |
| @@ -4312,6 +4508,16 @@ static void setup_job_control(void) | |||
| 4312 | } | 4508 | } |
| 4313 | #endif | 4509 | #endif |
| 4314 | 4510 | ||
| 4511 | static int set_mode(const char cstate, const char mode) | ||
| 4512 | { | ||
| 4513 | int state = (cstate == '-' ? 1 : 0); | ||
| 4514 | switch (mode) { | ||
| 4515 | case 'n': G.fake_mode = state; break; | ||
| 4516 | case 'x': /*G.debug_mode = state;*/ break; | ||
| 4517 | default: return EXIT_FAILURE; | ||
| 4518 | } | ||
| 4519 | return EXIT_SUCCESS; | ||
| 4520 | } | ||
| 4315 | 4521 | ||
| 4316 | int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 4522 | int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| 4317 | int hush_main(int argc, char **argv) | 4523 | int hush_main(int argc, char **argv) |
| @@ -4408,7 +4614,7 @@ int hush_main(int argc, char **argv) | |||
| 4408 | break; | 4614 | break; |
| 4409 | case 'n': | 4615 | case 'n': |
| 4410 | case 'x': | 4616 | case 'x': |
| 4411 | if (!builtin_set_mode('-', opt)) | 4617 | if (!set_mode('-', opt)) |
| 4412 | break; | 4618 | break; |
| 4413 | default: | 4619 | default: |
| 4414 | #ifndef BB_VER | 4620 | #ifndef BB_VER |
| @@ -4447,6 +4653,7 @@ int hush_main(int argc, char **argv) | |||
| 4447 | // to (inadvertently) close/redirect it | 4653 | // to (inadvertently) close/redirect it |
| 4448 | } | 4654 | } |
| 4449 | } | 4655 | } |
| 4656 | init_signal_mask(); | ||
| 4450 | debug_printf("G.interactive_fd=%d\n", G.interactive_fd); | 4657 | debug_printf("G.interactive_fd=%d\n", G.interactive_fd); |
| 4451 | if (G.interactive_fd) { | 4658 | if (G.interactive_fd) { |
| 4452 | fcntl(G.interactive_fd, F_SETFD, FD_CLOEXEC); | 4659 | fcntl(G.interactive_fd, F_SETFD, FD_CLOEXEC); |
| @@ -4475,9 +4682,10 @@ int hush_main(int argc, char **argv) | |||
| 4475 | } | 4682 | } |
| 4476 | if (G.interactive_fd) { | 4683 | if (G.interactive_fd) { |
| 4477 | fcntl(G.interactive_fd, F_SETFD, FD_CLOEXEC); | 4684 | fcntl(G.interactive_fd, F_SETFD, FD_CLOEXEC); |
| 4478 | set_misc_sighandler(SIG_IGN); | 4685 | //// set_misc_sighandler(SIG_IGN); |
| 4479 | } | 4686 | } |
| 4480 | } | 4687 | } |
| 4688 | init_signal_mask(); | ||
| 4481 | #endif | 4689 | #endif |
| 4482 | 4690 | ||
| 4483 | #if ENABLE_HUSH_INTERACTIVE && !ENABLE_FEATURE_SH_EXTRA_QUIET | 4691 | #if ENABLE_HUSH_INTERACTIVE && !ENABLE_FEATURE_SH_EXTRA_QUIET |
| @@ -4530,93 +4738,63 @@ int lash_main(int argc, char **argv) | |||
| 4530 | /* | 4738 | /* |
| 4531 | * Built-ins | 4739 | * Built-ins |
| 4532 | */ | 4740 | */ |
| 4533 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#trap | ||
| 4534 | * | ||
| 4535 | * Traps are also evaluated immediately instead of being delayed properly: | ||
| 4536 | * http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_11 | ||
| 4537 | * Example: hush -c 'trap "echo hi" 31; sleep 10; echo moo' & sleep 1; kill -31 $! | ||
| 4538 | * "hi" should not be displayed until the sleep finishes | ||
| 4539 | * This will have to get fixed ... | ||
| 4540 | */ | ||
| 4541 | static void handle_trap(int sig) | ||
| 4542 | { | ||
| 4543 | int save_errno, save_rcode; | ||
| 4544 | char *argv[] = { NULL, G.traps[sig].cmd, NULL }; | ||
| 4545 | /* Race! We transitioned from handled to ignore/default, but | ||
| 4546 | * the signal came in after updating .cmd but before we could | ||
| 4547 | * register the new signal handler. | ||
| 4548 | */ | ||
| 4549 | if (!argv[1] || argv[1][0] == '\0') | ||
| 4550 | return; | ||
| 4551 | /* need to save/restore errno/$? across traps */ | ||
| 4552 | save_errno = errno; | ||
| 4553 | save_rcode = G.last_return_code; | ||
| 4554 | builtin_eval(argv); | ||
| 4555 | errno = save_errno; | ||
| 4556 | G.last_return_code = save_rcode; | ||
| 4557 | } | ||
| 4558 | static int builtin_trap(char **argv) | 4741 | static int builtin_trap(char **argv) |
| 4559 | { | 4742 | { |
| 4560 | size_t i; | 4743 | int i; |
| 4561 | int sig; | 4744 | int sig; |
| 4562 | bool ign = false; | 4745 | char *new_cmd; |
| 4563 | char *new_cmd = NULL; | ||
| 4564 | 4746 | ||
| 4565 | if (!G.traps) | 4747 | if (!G.traps) |
| 4566 | G.traps = xzalloc(sizeof(*G.traps) * NSIG); | 4748 | G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); |
| 4567 | 4749 | ||
| 4568 | if (!argv[1]) { | 4750 | if (!argv[1]) { |
| 4569 | /* No args: print all trapped. This isn't 100% correct as we should | 4751 | /* No args: print all trapped. This isn't 100% correct as we should |
| 4570 | * be escaping the cmd so that it can be pasted back in ... | 4752 | * be escaping the cmd so that it can be pasted back in ... |
| 4571 | */ | 4753 | */ |
| 4572 | for (i = 0; i < NSIG; ++i) | 4754 | for (i = 0; i < NSIG; ++i) |
| 4573 | if (G.traps[i].cmd) | 4755 | if (G.traps[i]) |
| 4574 | printf("trap -- '%s' %s\n", G.traps[i].cmd, get_signame(i)); | 4756 | printf("trap -- '%s' %s\n", G.traps[i], get_signame(i)); |
| 4575 | return EXIT_SUCCESS; | 4757 | return EXIT_SUCCESS; |
| 4576 | } | 4758 | } |
| 4577 | 4759 | ||
| 4578 | /* first arg is decimal: reset all specified */ | 4760 | new_cmd = NULL; |
| 4579 | sig = bb_strtou(argv[1], NULL, 10); | 4761 | i = 0; |
| 4762 | /* if first arg is decimal: reset all specified */ | ||
| 4763 | sig = bb_strtou(*++argv, NULL, 10); | ||
| 4580 | if (errno == 0) { | 4764 | if (errno == 0) { |
| 4581 | int ret; | 4765 | int ret; |
| 4582 | i = 0; | ||
| 4583 | set_all: | 4766 | set_all: |
| 4584 | ret = EXIT_SUCCESS; | 4767 | ret = EXIT_SUCCESS; |
| 4585 | while (argv[++i]) { | 4768 | while (*argv) { |
| 4586 | char *old_cmd; | 4769 | sig = get_signum(*argv++); |
| 4587 | |||
| 4588 | sig = get_signum(argv[i]); | ||
| 4589 | if (sig < 0 || sig >= NSIG) { | 4770 | if (sig < 0 || sig >= NSIG) { |
| 4590 | ret = EXIT_FAILURE; | 4771 | ret = EXIT_FAILURE; |
| 4772 | /* mimic bash message exactly */ | ||
| 4591 | bb_perror_msg("trap: %s: invalid signal specification", argv[i]); | 4773 | bb_perror_msg("trap: %s: invalid signal specification", argv[i]); |
| 4592 | continue; | 4774 | continue; |
| 4593 | } | 4775 | } |
| 4594 | 4776 | ||
| 4595 | /* Make sure .cmd is always a valid command list since | 4777 | free(G.traps[sig]); |
| 4596 | * signals can occur at any time ... | 4778 | G.traps[sig] = xstrdup(new_cmd); |
| 4597 | */ | ||
| 4598 | old_cmd = G.traps[sig].cmd; | ||
| 4599 | G.traps[sig].cmd = xstrdup(new_cmd); | ||
| 4600 | free(old_cmd); | ||
| 4601 | 4779 | ||
| 4602 | debug_printf("trap: setting SIG%s (%i) to: %s", | 4780 | debug_printf("trap: setting SIG%s (%i) to '%s'", |
| 4603 | get_signame(sig), sig, G.traps[sig].cmd); | 4781 | get_signame(sig), sig, G.traps[sig]); |
| 4604 | 4782 | ||
| 4605 | /* There is no signal for 0 (EXIT) */ | 4783 | /* There is no signal for 0 (EXIT) */ |
| 4606 | if (sig == 0) | 4784 | if (sig == 0) |
| 4607 | continue; | 4785 | continue; |
| 4608 | 4786 | ||
| 4609 | if (new_cmd) { | 4787 | if (new_cmd) { |
| 4610 | /* add/update a handler */ | 4788 | sigaddset(&G.blocked_set, sig); |
| 4611 | struct sigaction act = { | 4789 | } else { |
| 4612 | .sa_handler = ign ? SIG_IGN : handle_trap, | 4790 | /* there was a trap handler, we are removing it |
| 4613 | .sa_flags = SA_RESTART, | 4791 | * (if sig has non-DFL handling, |
| 4614 | }; | 4792 | * we don't need to do anything) */ |
| 4615 | sigemptyset(&act.sa_mask); | 4793 | if (sig < 32 && (G.non_DFL_mask & (1 << sig))) |
| 4616 | sigaction(sig, &act, old_cmd ? NULL : &G.traps[sig].oact); | 4794 | continue; |
| 4617 | } else if (old_cmd && !new_cmd) | 4795 | sigdelset(&G.blocked_set, sig); |
| 4618 | /* there was a handler, and we are removing it */ | 4796 | } |
| 4619 | sigaction_set(sig, &G.traps[sig].oact); | 4797 | sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); |
| 4620 | } | 4798 | } |
| 4621 | return ret; | 4799 | return ret; |
| 4622 | } | 4800 | } |
| @@ -4624,17 +4802,15 @@ static int builtin_trap(char **argv) | |||
| 4624 | /* first arg is "-": reset all specified to default */ | 4802 | /* first arg is "-": reset all specified to default */ |
| 4625 | /* first arg is "": ignore all specified */ | 4803 | /* first arg is "": ignore all specified */ |
| 4626 | /* everything else: execute first arg upon signal */ | 4804 | /* everything else: execute first arg upon signal */ |
| 4627 | if (!argv[2]) { | 4805 | if (!argv[1]) { |
| 4628 | bb_error_msg("trap: invalid arguments"); | 4806 | bb_error_msg("trap: invalid arguments"); |
| 4629 | return EXIT_FAILURE; | 4807 | return EXIT_FAILURE; |
| 4630 | } | 4808 | } |
| 4631 | if (LONE_DASH(argv[1])) | 4809 | if (LONE_DASH(*argv)) |
| 4632 | /* nothing! */; | 4810 | /* nothing! */; |
| 4633 | else | 4811 | else |
| 4634 | new_cmd = argv[1]; | 4812 | new_cmd = *argv; |
| 4635 | if (argv[1][0] == '\0') | 4813 | argv++; |
| 4636 | ign = true; | ||
| 4637 | i = 1; | ||
| 4638 | goto set_all; | 4814 | goto set_all; |
| 4639 | } | 4815 | } |
| 4640 | 4816 | ||
| @@ -4891,16 +5067,6 @@ static int builtin_read(char **argv) | |||
| 4891 | * | 5067 | * |
| 4892 | * So far, we only support "set -- [argument...]" and some of the short names. | 5068 | * So far, we only support "set -- [argument...]" and some of the short names. |
| 4893 | */ | 5069 | */ |
| 4894 | static int builtin_set_mode(const char cstate, const char mode) | ||
| 4895 | { | ||
| 4896 | int state = (cstate == '-' ? 1 : 0); | ||
| 4897 | switch (mode) { | ||
| 4898 | case 'n': G.fake_mode = state; break; | ||
| 4899 | case 'x': /*G.debug_mode = state;*/ break; | ||
| 4900 | default: return EXIT_FAILURE; | ||
| 4901 | } | ||
| 4902 | return EXIT_SUCCESS; | ||
| 4903 | } | ||
| 4904 | static int builtin_set(char **argv) | 5070 | static int builtin_set(char **argv) |
| 4905 | { | 5071 | { |
| 4906 | int n; | 5072 | int n; |
| @@ -4922,7 +5088,7 @@ static int builtin_set(char **argv) | |||
| 4922 | 5088 | ||
| 4923 | if (arg[0] == '+' || arg[0] == '-') { | 5089 | if (arg[0] == '+' || arg[0] == '-') { |
| 4924 | for (n = 1; arg[n]; ++n) | 5090 | for (n = 1; arg[n]; ++n) |
| 4925 | if (builtin_set_mode(arg[0], arg[n])) | 5091 | if (set_mode(arg[0], arg[n])) |
| 4926 | goto error; | 5092 | goto error; |
| 4927 | continue; | 5093 | continue; |
| 4928 | } | 5094 | } |
| @@ -5068,27 +5234,29 @@ static int builtin_wait(char **argv) | |||
| 5068 | int ret = EXIT_SUCCESS; | 5234 | int ret = EXIT_SUCCESS; |
| 5069 | int status; | 5235 | int status; |
| 5070 | 5236 | ||
| 5071 | if (argv[1] == NULL) | 5237 | if (*++argv == NULL) |
| 5072 | /* don't care about exit status */ | 5238 | /* don't care about exit status */ |
| 5073 | wait(&status); | 5239 | wait(NULL); |
| 5074 | 5240 | ||
| 5075 | while (argv[1]) { | 5241 | while (*argv) { |
| 5076 | pid_t pid = bb_strtou(argv[1], NULL, 10); | 5242 | pid_t pid = bb_strtou(*argv, NULL, 10); |
| 5077 | if (errno) { | 5243 | if (errno) { |
| 5078 | bb_perror_msg("wait %s", argv[1]); | 5244 | /* mimic bash message */ |
| 5245 | bb_error_msg("wait: '%s': not a pid or valid job spec", *argv); | ||
| 5079 | return EXIT_FAILURE; | 5246 | return EXIT_FAILURE; |
| 5080 | } else if (waitpid(pid, &status, 0) == pid) { | 5247 | } |
| 5248 | if (waitpid(pid, &status, 0) == pid) { | ||
| 5081 | if (WIFSIGNALED(status)) | 5249 | if (WIFSIGNALED(status)) |
| 5082 | ret = 128 + WTERMSIG(status); | 5250 | ret = 128 + WTERMSIG(status); |
| 5083 | else if (WIFEXITED(status)) | 5251 | else if (WIFEXITED(status)) |
| 5084 | ret = WEXITSTATUS(status); | 5252 | ret = WEXITSTATUS(status); |
| 5085 | else | 5253 | else /* wtf? */ |
| 5086 | ret = EXIT_FAILURE; | 5254 | ret = EXIT_FAILURE; |
| 5087 | } else { | 5255 | } else { |
| 5088 | bb_perror_msg("wait %s", argv[1]); | 5256 | bb_perror_msg("wait %s", *argv); |
| 5089 | ret = 127; | 5257 | ret = 127; |
| 5090 | } | 5258 | } |
| 5091 | ++argv; | 5259 | argv++; |
| 5092 | } | 5260 | } |
| 5093 | 5261 | ||
| 5094 | return ret; | 5262 | return ret; |
