aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-04-18 01:23:21 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-04-18 01:23:21 +0000
commit6b9e05392b4457f0ea808810761f8fb9efdffc44 (patch)
tree22f987ef8ae40bcee3cfb2b54dd19afbd1dc9e07 /shell
parent7b9e5c57ae388be642234ee89c75b733711f905d (diff)
downloadbusybox-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.c203
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
1098static 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. */
1157static void sigexit(int sig) NORETURN; 1110static void sigexit(int sig) NORETURN;
@@ -1201,6 +1154,65 @@ static void hush_exit(int exitcode)
1201#endif 1154#endif
1202} 1155}
1203 1156
1157static 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
1205static const char *set_cwd(void) 1217static 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
3075static void insert_bg_job(struct pipe *pi) 3093static 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
3117static void remove_bg_job(struct pipe *pi) 3127static 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{
3956debug_printf_exec("insert_bg_job1\n");
3945 insert_bg_job(pi); 3957 insert_bg_job(pi);
3958debug_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 */