diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-18 01:23:21 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-18 01:23:21 +0000 |
commit | 6b9e05392b4457f0ea808810761f8fb9efdffc44 (patch) | |
tree | 22f987ef8ae40bcee3cfb2b54dd19afbd1dc9e07 /shell | |
parent | 7b9e5c57ae388be642234ee89c75b733711f905d (diff) | |
download | busybox-w32-6b9e05392b4457f0ea808810761f8fb9efdffc44.tar.gz busybox-w32-6b9e05392b4457f0ea808810761f8fb9efdffc44.tar.bz2 busybox-w32-6b9e05392b4457f0ea808810761f8fb9efdffc44.zip |
hush: implement proper SIGHUP handling
function old new delta
check_and_run_traps 164 229 +65
insert_bg_job 376 366 -10
hush_main 937 927 -10
Diffstat (limited to 'shell')
-rw-r--r-- | shell/hush.c | 203 |
1 files changed, 108 insertions, 95 deletions
diff --git a/shell/hush.c b/shell/hush.c index f9757a723..72589fd7f 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -52,11 +52,10 @@ | |||
52 | * grep for "TODO" and fix (some of them are easy) | 52 | * grep for "TODO" and fix (some of them are easy) |
53 | * change { and } from special chars to reserved words | 53 | * change { and } from special chars to reserved words |
54 | * $var refs in function do not pick up values set by "var=val func" | 54 | * $var refs in function do not pick up values set by "var=val func" |
55 | * builtins: return, ulimit | 55 | * builtins: ulimit |
56 | * follow IFS rules more precisely, including update semantics | 56 | * follow IFS rules more precisely, including update semantics |
57 | * figure out what to do with backslash-newline | 57 | * figure out what to do with backslash-newline |
58 | * continuation lines, both explicit and implicit - done? | 58 | * continuation lines, both explicit and implicit - done? |
59 | * SIGHUP handling | ||
60 | * separate job control from interactiveness | 59 | * separate job control from interactiveness |
61 | * (testcase: booting with init=/bin/hush does not show prompt (2009-04)) | 60 | * (testcase: booting with init=/bin/hush does not show prompt (2009-04)) |
62 | * | 61 | * |
@@ -1070,8 +1069,9 @@ static void restore_G_args(save_arg_t *sv, char **argv) | |||
1070 | * "trap 'cmd' SIGxxx": | 1069 | * "trap 'cmd' SIGxxx": |
1071 | * set bit in blocked_set (even if 'cmd' is '') | 1070 | * set bit in blocked_set (even if 'cmd' is '') |
1072 | * after [v]fork, if we plan to be a shell: | 1071 | * after [v]fork, if we plan to be a shell: |
1073 | * nothing for {} child shell (say, "true | { true; true; } | true") | 1072 | * unblock signals with special interactive handling |
1074 | * unset all traps if () shell. | 1073 | * (child shell is not interactive), |
1074 | * unset all traps (note: regardless of child shell's type - {}, (), etc) | ||
1075 | * after [v]fork, if we plan to exec: | 1075 | * after [v]fork, if we plan to exec: |
1076 | * POSIX says pending signal mask is cleared in child - no need to clear it. | 1076 | * POSIX says pending signal mask is cleared in child - no need to clear it. |
1077 | * Restore blocked signal set to one inherited by shell just prior to exec. | 1077 | * Restore blocked signal set to one inherited by shell just prior to exec. |
@@ -1084,9 +1084,9 @@ enum { | |||
1084 | SPECIAL_INTERACTIVE_SIGS = 0 | 1084 | SPECIAL_INTERACTIVE_SIGS = 0 |
1085 | #if ENABLE_HUSH_JOB | 1085 | #if ENABLE_HUSH_JOB |
1086 | | (1 << SIGTTIN) | (1 << SIGTTOU) | (1 << SIGTSTP) | 1086 | | (1 << SIGTTIN) | (1 << SIGTTOU) | (1 << SIGTSTP) |
1087 | | (1 << SIGHUP) | ||
1087 | #endif | 1088 | #endif |
1088 | | (1 << SIGTERM) | 1089 | | (1 << SIGTERM) |
1089 | //TODO | (1 << SIGHUP) | ||
1090 | | (1 << SIGINT) | 1090 | | (1 << SIGINT) |
1091 | }; | 1091 | }; |
1092 | 1092 | ||
@@ -1095,53 +1095,6 @@ enum { | |||
1095 | // G.count_SIGCHLD++; | 1095 | // G.count_SIGCHLD++; |
1096 | //} | 1096 | //} |
1097 | 1097 | ||
1098 | static int check_and_run_traps(int sig) | ||
1099 | { | ||
1100 | static const struct timespec zero_timespec = { 0, 0 }; | ||
1101 | smalluint save_rcode; | ||
1102 | int last_sig = 0; | ||
1103 | |||
1104 | if (sig) | ||
1105 | goto jump_in; | ||
1106 | while (1) { | ||
1107 | sig = sigtimedwait(&G.blocked_set, NULL, &zero_timespec); | ||
1108 | if (sig <= 0) | ||
1109 | break; | ||
1110 | jump_in: | ||
1111 | last_sig = sig; | ||
1112 | if (G.traps && G.traps[sig]) { | ||
1113 | if (G.traps[sig][0]) { | ||
1114 | /* We have user-defined handler */ | ||
1115 | char *argv[] = { NULL, xstrdup(G.traps[sig]), NULL }; | ||
1116 | save_rcode = G.last_exitcode; | ||
1117 | builtin_eval(argv); | ||
1118 | free(argv[1]); | ||
1119 | G.last_exitcode = save_rcode; | ||
1120 | } /* else: "" trap, ignoring signal */ | ||
1121 | continue; | ||
1122 | } | ||
1123 | /* not a trap: special action */ | ||
1124 | switch (sig) { | ||
1125 | // case SIGCHLD: | ||
1126 | // G.count_SIGCHLD++; | ||
1127 | // break; | ||
1128 | case SIGINT: | ||
1129 | //TODO: add putchar('\n') also when we detect that child was killed (sleep 5 + ^C) | ||
1130 | /* Builtin was ^C'ed, make it look prettier: */ | ||
1131 | bb_putchar('\n'); | ||
1132 | G.flag_SIGINT = 1; | ||
1133 | break; | ||
1134 | //TODO | ||
1135 | // case SIGHUP: ... | ||
1136 | // break; | ||
1137 | default: /* ignored: */ | ||
1138 | /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */ | ||
1139 | break; | ||
1140 | } | ||
1141 | } | ||
1142 | return last_sig; | ||
1143 | } | ||
1144 | |||
1145 | #if ENABLE_HUSH_JOB | 1098 | #if ENABLE_HUSH_JOB |
1146 | 1099 | ||
1147 | /* After [v]fork, in child: do not restore tty pgrp on xfunc death */ | 1100 | /* After [v]fork, in child: do not restore tty pgrp on xfunc death */ |
@@ -1151,7 +1104,7 @@ static int check_and_run_traps(int sig) | |||
1151 | 1104 | ||
1152 | /* Restores tty foreground process group, and exits. | 1105 | /* Restores tty foreground process group, and exits. |
1153 | * May be called as signal handler for fatal signal | 1106 | * May be called as signal handler for fatal signal |
1154 | * (will faithfully resend signal to itself, producing correct exit state) | 1107 | * (will resend signal to itself, producing correct exit state) |
1155 | * or called directly with -EXITCODE. | 1108 | * or called directly with -EXITCODE. |
1156 | * We also call it if xfunc is exiting. */ | 1109 | * We also call it if xfunc is exiting. */ |
1157 | static void sigexit(int sig) NORETURN; | 1110 | static void sigexit(int sig) NORETURN; |
@@ -1201,6 +1154,65 @@ static void hush_exit(int exitcode) | |||
1201 | #endif | 1154 | #endif |
1202 | } | 1155 | } |
1203 | 1156 | ||
1157 | static int check_and_run_traps(int sig) | ||
1158 | { | ||
1159 | static const struct timespec zero_timespec = { 0, 0 }; | ||
1160 | smalluint save_rcode; | ||
1161 | int last_sig = 0; | ||
1162 | |||
1163 | if (sig) | ||
1164 | goto jump_in; | ||
1165 | while (1) { | ||
1166 | sig = sigtimedwait(&G.blocked_set, NULL, &zero_timespec); | ||
1167 | if (sig <= 0) | ||
1168 | break; | ||
1169 | jump_in: | ||
1170 | last_sig = sig; | ||
1171 | if (G.traps && G.traps[sig]) { | ||
1172 | if (G.traps[sig][0]) { | ||
1173 | /* We have user-defined handler */ | ||
1174 | char *argv[] = { NULL, xstrdup(G.traps[sig]), NULL }; | ||
1175 | save_rcode = G.last_exitcode; | ||
1176 | builtin_eval(argv); | ||
1177 | free(argv[1]); | ||
1178 | G.last_exitcode = save_rcode; | ||
1179 | } /* else: "" trap, ignoring signal */ | ||
1180 | continue; | ||
1181 | } | ||
1182 | /* not a trap: special action */ | ||
1183 | switch (sig) { | ||
1184 | // case SIGCHLD: | ||
1185 | // G.count_SIGCHLD++; | ||
1186 | // break; | ||
1187 | case SIGINT: | ||
1188 | //TODO: add putchar('\n') also when we detect that child was killed (sleep 5 + ^C) | ||
1189 | /* Builtin was ^C'ed, make it look prettier: */ | ||
1190 | bb_putchar('\n'); | ||
1191 | G.flag_SIGINT = 1; | ||
1192 | break; | ||
1193 | #if ENABLE_HUSH_JOB | ||
1194 | case SIGHUP: { | ||
1195 | struct pipe *job; | ||
1196 | /* bash is observed to signal whole process groups, | ||
1197 | * not individual processes */ | ||
1198 | for (job = G.job_list; job; job = job->next) { | ||
1199 | if (job->pgrp <= 0) | ||
1200 | continue; | ||
1201 | debug_printf_exec("HUPing pgrp %d\n", job->pgrp); | ||
1202 | if (kill(- job->pgrp, SIGHUP) == 0) | ||
1203 | kill(- job->pgrp, SIGCONT); | ||
1204 | } | ||
1205 | sigexit(SIGHUP); | ||
1206 | } | ||
1207 | #endif | ||
1208 | default: /* ignored: */ | ||
1209 | /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */ | ||
1210 | break; | ||
1211 | } | ||
1212 | } | ||
1213 | return last_sig; | ||
1214 | } | ||
1215 | |||
1204 | 1216 | ||
1205 | static const char *set_cwd(void) | 1217 | static const char *set_cwd(void) |
1206 | { | 1218 | { |
@@ -2061,7 +2073,9 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
2061 | case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ | 2073 | case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ |
2062 | *p = '\0'; | 2074 | *p = '\0'; |
2063 | arg++; | 2075 | arg++; |
2064 | //TODO: can we just stuff it into "output" directly? | 2076 | /* Can't just stuff it into output o_string, |
2077 | * expanded result may need to be globbed | ||
2078 | * and $IFS-splitted */ | ||
2065 | debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch); | 2079 | debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch); |
2066 | process_command_subs(&subst_result, arg); | 2080 | process_command_subs(&subst_result, arg); |
2067 | debug_printf_subst("SUBST RES '%s'\n", subst_result.data); | 2081 | debug_printf_subst("SUBST RES '%s'\n", subst_result.data); |
@@ -2777,7 +2791,8 @@ static const struct function *find_function(const char *name) | |||
2777 | } | 2791 | } |
2778 | funcp = funcp->next; | 2792 | funcp = funcp->next; |
2779 | } | 2793 | } |
2780 | debug_printf_exec("found function '%s'\n", name); | 2794 | if (funcp) |
2795 | debug_printf_exec("found function '%s'\n", name); | ||
2781 | return funcp; | 2796 | return funcp; |
2782 | } | 2797 | } |
2783 | 2798 | ||
@@ -2819,7 +2834,7 @@ static struct function *new_function(char *name) | |||
2819 | } | 2834 | } |
2820 | goto skip; | 2835 | goto skip; |
2821 | } | 2836 | } |
2822 | debug_printf_exec("remembering new function '%s'\n", command->argv[0]); | 2837 | debug_printf_exec("remembering new function '%s'\n", name); |
2823 | funcp = *funcpp = xzalloc(sizeof(*funcp)); | 2838 | funcp = *funcpp = xzalloc(sizeof(*funcp)); |
2824 | /*funcp->next = NULL;*/ | 2839 | /*funcp->next = NULL;*/ |
2825 | skip: | 2840 | skip: |
@@ -3059,8 +3074,11 @@ static const char *get_cmdtext(struct pipe *pi) | |||
3059 | } | 3074 | } |
3060 | 3075 | ||
3061 | len = 0; | 3076 | len = 0; |
3062 | do len += strlen(*argv) + 1; while (*++argv); | 3077 | do { |
3063 | pi->cmdtext = p = xmalloc(len); | 3078 | len += strlen(*argv) + 1; |
3079 | } while (*++argv); | ||
3080 | p = xmalloc(len); | ||
3081 | pi->cmdtext = p;// = xmalloc(len); | ||
3064 | argv = pi->cmds[0].argv; | 3082 | argv = pi->cmds[0].argv; |
3065 | do { | 3083 | do { |
3066 | len = strlen(*argv); | 3084 | len = strlen(*argv); |
@@ -3074,44 +3092,36 @@ static const char *get_cmdtext(struct pipe *pi) | |||
3074 | 3092 | ||
3075 | static void insert_bg_job(struct pipe *pi) | 3093 | static void insert_bg_job(struct pipe *pi) |
3076 | { | 3094 | { |
3077 | struct pipe *thejob; | 3095 | struct pipe *job, **jobp; |
3078 | int i; | 3096 | int i; |
3079 | 3097 | ||
3080 | /* Linear search for the ID of the job to use */ | 3098 | /* Linear search for the ID of the job to use */ |
3081 | pi->jobid = 1; | 3099 | pi->jobid = 1; |
3082 | for (thejob = G.job_list; thejob; thejob = thejob->next) | 3100 | for (job = G.job_list; job; job = job->next) |
3083 | if (thejob->jobid >= pi->jobid) | 3101 | if (job->jobid >= pi->jobid) |
3084 | pi->jobid = thejob->jobid + 1; | 3102 | pi->jobid = job->jobid + 1; |
3085 | 3103 | ||
3086 | /* Add thejob to the list of running jobs */ | 3104 | /* Add job to the list of running jobs */ |
3087 | if (!G.job_list) { | 3105 | jobp = &G.job_list; |
3088 | thejob = G.job_list = xmalloc(sizeof(*thejob)); | 3106 | while ((job = *jobp) != NULL) |
3089 | } else { | 3107 | jobp = &job->next; |
3090 | for (thejob = G.job_list; thejob->next; thejob = thejob->next) | 3108 | job = *jobp = xmalloc(sizeof(*job)); |
3091 | continue; | 3109 | |
3092 | thejob->next = xmalloc(sizeof(*thejob)); | 3110 | *job = *pi; /* physical copy */ |
3093 | thejob = thejob->next; | 3111 | job->next = NULL; |
3094 | } | 3112 | job->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds); |
3095 | 3113 | /* Cannot copy entire pi->cmds[] vector! This causes double frees */ | |
3096 | /* Physically copy the struct job */ | ||
3097 | memcpy(thejob, pi, sizeof(struct pipe)); | ||
3098 | thejob->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds); | ||
3099 | /* We cannot copy entire pi->cmds[] vector! Double free()s will happen */ | ||
3100 | for (i = 0; i < pi->num_cmds; i++) { | 3114 | for (i = 0; i < pi->num_cmds; i++) { |
3101 | // TODO: do we really need to have so many fields which are just dead weight | 3115 | job->cmds[i].pid = pi->cmds[i].pid; |
3102 | // at execution stage? | ||
3103 | thejob->cmds[i].pid = pi->cmds[i].pid; | ||
3104 | /* all other fields are not used and stay zero */ | 3116 | /* all other fields are not used and stay zero */ |
3105 | } | 3117 | } |
3106 | thejob->next = NULL; | 3118 | job->cmdtext = xstrdup(get_cmdtext(pi)); |
3107 | thejob->cmdtext = xstrdup(get_cmdtext(pi)); | ||
3108 | 3119 | ||
3109 | /* We don't wait for background thejobs to return -- append it | ||
3110 | to the list of backgrounded thejobs and leave it alone */ | ||
3111 | if (G_interactive_fd) | 3120 | if (G_interactive_fd) |
3112 | printf("[%d] %d %s\n", thejob->jobid, thejob->cmds[0].pid, thejob->cmdtext); | 3121 | printf("[%d] %d %s\n", job->jobid, job->cmds[0].pid, job->cmdtext); |
3113 | G.last_bg_pid = thejob->cmds[0].pid; | 3122 | /* Last command's pid goes to $! */ |
3114 | G.last_jobid = thejob->jobid; | 3123 | G.last_bg_pid = job->cmds[job->num_cmds - 1].pid; |
3124 | G.last_jobid = job->jobid; | ||
3115 | } | 3125 | } |
3116 | 3126 | ||
3117 | static void remove_bg_job(struct pipe *pi) | 3127 | static void remove_bg_job(struct pipe *pi) |
@@ -3306,7 +3316,7 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe) | |||
3306 | * cmd || ... { list } || ... | 3316 | * cmd || ... { list } || ... |
3307 | * If it is, then we can run cmd as a builtin, NOFORK [do we do this?], | 3317 | * If it is, then we can run cmd as a builtin, NOFORK [do we do this?], |
3308 | * or (if SH_STANDALONE) an applet, and we can run the { list } | 3318 | * or (if SH_STANDALONE) an applet, and we can run the { list } |
3309 | * with run_list(). If it isn't one of these, we fork and exec cmd. | 3319 | * with run_list. If it isn't one of these, we fork and exec cmd. |
3310 | * | 3320 | * |
3311 | * Cases when we must fork: | 3321 | * Cases when we must fork: |
3312 | * non-single: cmd | cmd | 3322 | * non-single: cmd | cmd |
@@ -3942,7 +3952,11 @@ static int run_list(struct pipe *pi) | |||
3942 | check_and_run_traps(0); | 3952 | check_and_run_traps(0); |
3943 | #if ENABLE_HUSH_JOB | 3953 | #if ENABLE_HUSH_JOB |
3944 | if (G.run_list_level == 1) | 3954 | if (G.run_list_level == 1) |
3955 | { | ||
3956 | debug_printf_exec("insert_bg_job1\n"); | ||
3945 | insert_bg_job(pi); | 3957 | insert_bg_job(pi); |
3958 | debug_printf_exec("insert_bg_job2\n"); | ||
3959 | } | ||
3946 | #endif | 3960 | #endif |
3947 | G.last_exitcode = rcode = EXIT_SUCCESS; | 3961 | G.last_exitcode = rcode = EXIT_SUCCESS; |
3948 | debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); | 3962 | debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); |
@@ -5853,11 +5867,10 @@ static void set_fatal_handlers(void) | |||
5853 | /* bash 3.2 seems to handle these just like 'fatal' ones */ | 5867 | /* bash 3.2 seems to handle these just like 'fatal' ones */ |
5854 | maybe_set_to_sigexit(SIGPIPE); | 5868 | maybe_set_to_sigexit(SIGPIPE); |
5855 | maybe_set_to_sigexit(SIGALRM); | 5869 | maybe_set_to_sigexit(SIGALRM); |
5856 | //TODO: disable and move down when proper SIGHUP handling is added | 5870 | /* if we are interactive, SIGHUP, SIGTERM and SIGINT are masked. |
5857 | maybe_set_to_sigexit(SIGHUP ); | ||
5858 | /* if we are interactive, [SIGHUP,] SIGTERM and SIGINT are masked. | ||
5859 | * if we aren't interactive... but in this case | 5871 | * if we aren't interactive... but in this case |
5860 | * we never want to restore pgrp on exit, and this fn is not called */ | 5872 | * we never want to restore pgrp on exit, and this fn is not called */ |
5873 | /*maybe_set_to_sigexit(SIGHUP );*/ | ||
5861 | /*maybe_set_to_sigexit(SIGTERM);*/ | 5874 | /*maybe_set_to_sigexit(SIGTERM);*/ |
5862 | /*maybe_set_to_sigexit(SIGINT );*/ | 5875 | /*maybe_set_to_sigexit(SIGINT );*/ |
5863 | } | 5876 | } |
@@ -6350,7 +6363,7 @@ static int builtin_exec(char **argv) | |||
6350 | #if !BB_MMU | 6363 | #if !BB_MMU |
6351 | nommu_save_t dummy; | 6364 | nommu_save_t dummy; |
6352 | #endif | 6365 | #endif |
6353 | // TODO: if exec fails, bash does NOT exit! We do... | 6366 | /* TODO: if exec fails, bash does NOT exit! We do... */ |
6354 | pseudo_exec_argv(&dummy, argv, 0, NULL); | 6367 | pseudo_exec_argv(&dummy, argv, 0, NULL); |
6355 | /* never returns */ | 6368 | /* never returns */ |
6356 | } | 6369 | } |
@@ -6472,8 +6485,8 @@ static int builtin_fg_bg(char **argv) | |||
6472 | bb_error_msg("%s: %d: no such job", argv[0], jobnum); | 6485 | bb_error_msg("%s: %d: no such job", argv[0], jobnum); |
6473 | return EXIT_FAILURE; | 6486 | return EXIT_FAILURE; |
6474 | found: | 6487 | found: |
6475 | // TODO: bash prints a string representation | 6488 | /* TODO: bash prints a string representation |
6476 | // of job being foregrounded (like "sleep 1 | cat") | 6489 | * of job being foregrounded (like "sleep 1 | cat") */ |
6477 | if (argv[0][0] == 'f') { | 6490 | if (argv[0][0] == 'f') { |
6478 | /* Put the job into the foreground. */ | 6491 | /* Put the job into the foreground. */ |
6479 | tcsetpgrp(G_interactive_fd, pi->pgrp); | 6492 | tcsetpgrp(G_interactive_fd, pi->pgrp); |
@@ -6705,7 +6718,7 @@ static int builtin_source(char **argv) | |||
6705 | if (*++argv == NULL) | 6718 | if (*++argv == NULL) |
6706 | return EXIT_FAILURE; | 6719 | return EXIT_FAILURE; |
6707 | 6720 | ||
6708 | /* TODO: search through $PATH is missing */ | 6721 | // TODO: search through $PATH is missing |
6709 | input = fopen_or_warn(*argv, "r"); | 6722 | input = fopen_or_warn(*argv, "r"); |
6710 | if (!input) { | 6723 | if (!input) { |
6711 | /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */ | 6724 | /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */ |