diff options
Diffstat (limited to 'shell/ash.c')
-rw-r--r-- | shell/ash.c | 122 |
1 files changed, 92 insertions, 30 deletions
diff --git a/shell/ash.c b/shell/ash.c index 28f988698..fd9141661 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -111,6 +111,13 @@ | |||
111 | //config: help | 111 | //config: help |
112 | //config: Enable bash-compatible extensions. | 112 | //config: Enable bash-compatible extensions. |
113 | //config: | 113 | //config: |
114 | //config:config ASH_IDLE_TIMEOUT | ||
115 | //config: bool "Idle timeout variable" | ||
116 | //config: default n | ||
117 | //config: depends on ASH | ||
118 | //config: help | ||
119 | //config: Enables bash-like auto-logout after $TMOUT seconds of idle time. | ||
120 | //config: | ||
114 | //config:config ASH_JOB_CONTROL | 121 | //config:config ASH_JOB_CONTROL |
115 | //config: bool "Job control" | 122 | //config: bool "Job control" |
116 | //config: default y | 123 | //config: default y |
@@ -119,7 +126,7 @@ | |||
119 | //config: Enable job control in the ash shell. | 126 | //config: Enable job control in the ash shell. |
120 | //config: | 127 | //config: |
121 | //config:config ASH_ALIAS | 128 | //config:config ASH_ALIAS |
122 | //config: bool "alias support" | 129 | //config: bool "Alias support" |
123 | //config: default y | 130 | //config: default y |
124 | //config: depends on ASH | 131 | //config: depends on ASH |
125 | //config: help | 132 | //config: help |
@@ -130,28 +137,28 @@ | |||
130 | //config: default y | 137 | //config: default y |
131 | //config: depends on ASH | 138 | //config: depends on ASH |
132 | //config: help | 139 | //config: help |
133 | //config: Enable getopts builtin in the ash shell. | 140 | //config: Enable support for getopts builtin in ash. |
134 | //config: | 141 | //config: |
135 | //config:config ASH_BUILTIN_ECHO | 142 | //config:config ASH_BUILTIN_ECHO |
136 | //config: bool "Builtin version of 'echo'" | 143 | //config: bool "Builtin version of 'echo'" |
137 | //config: default y | 144 | //config: default y |
138 | //config: depends on ASH | 145 | //config: depends on ASH |
139 | //config: help | 146 | //config: help |
140 | //config: Enable support for echo, builtin to ash. | 147 | //config: Enable support for echo builtin in ash. |
141 | //config: | 148 | //config: |
142 | //config:config ASH_BUILTIN_PRINTF | 149 | //config:config ASH_BUILTIN_PRINTF |
143 | //config: bool "Builtin version of 'printf'" | 150 | //config: bool "Builtin version of 'printf'" |
144 | //config: default y | 151 | //config: default y |
145 | //config: depends on ASH | 152 | //config: depends on ASH |
146 | //config: help | 153 | //config: help |
147 | //config: Enable support for printf, builtin to ash. | 154 | //config: Enable support for printf builtin in ash. |
148 | //config: | 155 | //config: |
149 | //config:config ASH_BUILTIN_TEST | 156 | //config:config ASH_BUILTIN_TEST |
150 | //config: bool "Builtin version of 'test'" | 157 | //config: bool "Builtin version of 'test'" |
151 | //config: default y | 158 | //config: default y |
152 | //config: depends on ASH | 159 | //config: depends on ASH |
153 | //config: help | 160 | //config: help |
154 | //config: Enable support for test, builtin to ash. | 161 | //config: Enable support for test builtin in ash. |
155 | //config: | 162 | //config: |
156 | //config:config ASH_CMDCMD | 163 | //config:config ASH_CMDCMD |
157 | //config: bool "'command' command to override shell builtins" | 164 | //config: bool "'command' command to override shell builtins" |
@@ -167,7 +174,7 @@ | |||
167 | //config: default n | 174 | //config: default n |
168 | //config: depends on ASH | 175 | //config: depends on ASH |
169 | //config: help | 176 | //config: help |
170 | //config: Enable "check for new mail" in the ash shell. | 177 | //config: Enable "check for new mail" function in the ash shell. |
171 | //config: | 178 | //config: |
172 | //config:config ASH_OPTIMIZE_FOR_SIZE | 179 | //config:config ASH_OPTIMIZE_FOR_SIZE |
173 | //config: bool "Optimize for size instead of speed" | 180 | //config: bool "Optimize for size instead of speed" |
@@ -449,6 +456,9 @@ static const char *var_end(const char *var) | |||
449 | 456 | ||
450 | 457 | ||
451 | /* ============ Interrupts / exceptions */ | 458 | /* ============ Interrupts / exceptions */ |
459 | |||
460 | static void exitshell(void) NORETURN; | ||
461 | |||
452 | /* | 462 | /* |
453 | * These macros allow the user to suspend the handling of interrupt signals | 463 | * These macros allow the user to suspend the handling of interrupt signals |
454 | * over a period of time. This is similar to SIGHOLD or to sigblock, but | 464 | * over a period of time. This is similar to SIGHOLD or to sigblock, but |
@@ -1929,7 +1939,9 @@ change_lc_ctype(const char *value) | |||
1929 | #endif | 1939 | #endif |
1930 | #if ENABLE_ASH_MAIL | 1940 | #if ENABLE_ASH_MAIL |
1931 | static void chkmail(void); | 1941 | static void chkmail(void); |
1932 | static void changemail(const char *) FAST_FUNC; | 1942 | static void changemail(const char *var_value) FAST_FUNC; |
1943 | #else | ||
1944 | # define chkmail() ((void)0) | ||
1933 | #endif | 1945 | #endif |
1934 | static void changepath(const char *) FAST_FUNC; | 1946 | static void changepath(const char *) FAST_FUNC; |
1935 | #if ENABLE_ASH_RANDOM_SUPPORT | 1947 | #if ENABLE_ASH_RANDOM_SUPPORT |
@@ -3937,18 +3949,51 @@ setjobctl(int on) | |||
3937 | static int FAST_FUNC | 3949 | static int FAST_FUNC |
3938 | killcmd(int argc, char **argv) | 3950 | killcmd(int argc, char **argv) |
3939 | { | 3951 | { |
3940 | int i = 1; | ||
3941 | if (argv[1] && strcmp(argv[1], "-l") != 0) { | 3952 | if (argv[1] && strcmp(argv[1], "-l") != 0) { |
3953 | int i = 1; | ||
3942 | do { | 3954 | do { |
3943 | if (argv[i][0] == '%') { | 3955 | if (argv[i][0] == '%') { |
3944 | struct job *jp = getjob(argv[i], 0); | 3956 | /* |
3945 | unsigned pid = jp->ps[0].ps_pid; | 3957 | * "kill %N" - job kill |
3946 | /* Enough space for ' -NNN<nul>' */ | 3958 | * Converting to pgrp / pid kill |
3947 | argv[i] = alloca(sizeof(int)*3 + 3); | 3959 | */ |
3948 | /* kill_main has matching code to expect | 3960 | struct job *jp; |
3949 | * leading space. Needed to not confuse | 3961 | char *dst; |
3950 | * negative pids with "kill -SIGNAL_NO" syntax */ | 3962 | int j, n; |
3951 | sprintf(argv[i], " -%u", pid); | 3963 | |
3964 | jp = getjob(argv[i], 0); | ||
3965 | /* | ||
3966 | * In jobs started under job control, we signal | ||
3967 | * entire process group by kill -PGRP_ID. | ||
3968 | * This happens, f.e., in interactive shell. | ||
3969 | * | ||
3970 | * Otherwise, we signal each child via | ||
3971 | * kill PID1 PID2 PID3. | ||
3972 | * Testcases: | ||
3973 | * sh -c 'sleep 1|sleep 1 & kill %1' | ||
3974 | * sh -c 'true|sleep 2 & sleep 1; kill %1' | ||
3975 | * sh -c 'true|sleep 1 & sleep 2; kill %1' | ||
3976 | */ | ||
3977 | n = jp->nprocs; /* can't be 0 (I hope) */ | ||
3978 | if (jp->jobctl) | ||
3979 | n = 1; | ||
3980 | dst = alloca(n * sizeof(int)*4); | ||
3981 | argv[i] = dst; | ||
3982 | for (j = 0; j < n; j++) { | ||
3983 | struct procstat *ps = &jp->ps[j]; | ||
3984 | /* Skip non-running and not-stopped members | ||
3985 | * (i.e. dead members) of the job | ||
3986 | */ | ||
3987 | if (ps->ps_status != -1 && !WIFSTOPPED(ps->ps_status)) | ||
3988 | continue; | ||
3989 | /* | ||
3990 | * kill_main has matching code to expect | ||
3991 | * leading space. Needed to not confuse | ||
3992 | * negative pids with "kill -SIGNAL_NO" syntax | ||
3993 | */ | ||
3994 | dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid); | ||
3995 | } | ||
3996 | *dst = '\0'; | ||
3952 | } | 3997 | } |
3953 | } while (argv[++i]); | 3998 | } while (argv[++i]); |
3954 | } | 3999 | } |
@@ -4046,6 +4091,7 @@ sprint_status(char *s, int status, int sigonly) | |||
4046 | #endif | 4091 | #endif |
4047 | } | 4092 | } |
4048 | st &= 0x7f; | 4093 | st &= 0x7f; |
4094 | //TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata | ||
4049 | col = fmtstr(s, 32, strsignal(st)); | 4095 | col = fmtstr(s, 32, strsignal(st)); |
4050 | if (WCOREDUMP(status)) { | 4096 | if (WCOREDUMP(status)) { |
4051 | col += fmtstr(s + col, 16, " (core dumped)"); | 4097 | col += fmtstr(s + col, 16, " (core dumped)"); |
@@ -4462,8 +4508,9 @@ waitcmd(int argc UNUSED_PARAM, char **argv) | |||
4462 | break; | 4508 | break; |
4463 | job = job->prev_job; | 4509 | job = job->prev_job; |
4464 | } | 4510 | } |
4465 | } else | 4511 | } else { |
4466 | job = getjob(*argv, 0); | 4512 | job = getjob(*argv, 0); |
4513 | } | ||
4467 | /* loop until process terminated or stopped */ | 4514 | /* loop until process terminated or stopped */ |
4468 | while (job->state == JOBRUNNING) | 4515 | while (job->state == JOBRUNNING) |
4469 | blocking_wait_with_raise_on_sig(); | 4516 | blocking_wait_with_raise_on_sig(); |
@@ -4959,7 +5006,7 @@ forkchild(struct job *jp, union node *n, int mode) | |||
4959 | #if JOBS | 5006 | #if JOBS |
4960 | /* do job control only in root shell */ | 5007 | /* do job control only in root shell */ |
4961 | doing_jobctl = 0; | 5008 | doing_jobctl = 0; |
4962 | if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) { | 5009 | if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) { |
4963 | pid_t pgrp; | 5010 | pid_t pgrp; |
4964 | 5011 | ||
4965 | if (jp->nprocs == 0) | 5012 | if (jp->nprocs == 0) |
@@ -4985,7 +5032,7 @@ forkchild(struct job *jp, union node *n, int mode) | |||
4985 | ash_msg_and_raise_error("can't open '%s'", bb_dev_null); | 5032 | ash_msg_and_raise_error("can't open '%s'", bb_dev_null); |
4986 | } | 5033 | } |
4987 | } | 5034 | } |
4988 | if (!oldlvl) { | 5035 | if (oldlvl == 0) { |
4989 | if (iflag) { /* why if iflag only? */ | 5036 | if (iflag) { /* why if iflag only? */ |
4990 | setsignal(SIGINT); | 5037 | setsignal(SIGINT); |
4991 | setsignal(SIGTERM); | 5038 | setsignal(SIGTERM); |
@@ -10065,10 +10112,21 @@ preadfd(void) | |||
10065 | if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) | 10112 | if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) |
10066 | nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); | 10113 | nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); |
10067 | else { | 10114 | else { |
10068 | #if ENABLE_FEATURE_TAB_COMPLETION | 10115 | int timeout = -1; |
10116 | # if ENABLE_ASH_IDLE_TIMEOUT | ||
10117 | if (iflag) { | ||
10118 | const char *tmout_var = lookupvar("TMOUT"); | ||
10119 | if (tmout_var) { | ||
10120 | timeout = atoi(tmout_var) * 1000; | ||
10121 | if (timeout <= 0) | ||
10122 | timeout = -1; | ||
10123 | } | ||
10124 | } | ||
10125 | # endif | ||
10126 | # if ENABLE_FEATURE_TAB_COMPLETION | ||
10069 | line_input_state->path_lookup = pathval(); | 10127 | line_input_state->path_lookup = pathval(); |
10070 | #endif | 10128 | # endif |
10071 | nr = read_line_input(cmdedit_prompt, buf, IBUFSIZ, line_input_state); | 10129 | nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout); |
10072 | if (nr == 0) { | 10130 | if (nr == 0) { |
10073 | /* Ctrl+C pressed */ | 10131 | /* Ctrl+C pressed */ |
10074 | if (trap[SIGINT]) { | 10132 | if (trap[SIGINT]) { |
@@ -10079,17 +10137,24 @@ preadfd(void) | |||
10079 | } | 10137 | } |
10080 | goto retry; | 10138 | goto retry; |
10081 | } | 10139 | } |
10082 | if (nr < 0 && errno == 0) { | 10140 | if (nr < 0) { |
10083 | /* Ctrl+D pressed */ | 10141 | if (errno == 0) { |
10084 | nr = 0; | 10142 | /* Ctrl+D pressed */ |
10143 | nr = 0; | ||
10144 | } | ||
10145 | # if ENABLE_ASH_IDLE_TIMEOUT | ||
10146 | else if (errno == EAGAIN && timeout > 0) { | ||
10147 | printf("\007timed out waiting for input: auto-logout\n"); | ||
10148 | exitshell(); | ||
10149 | } | ||
10150 | # endif | ||
10085 | } | 10151 | } |
10086 | } | 10152 | } |
10087 | #else | 10153 | #else |
10088 | nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); | 10154 | nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); |
10089 | #endif | 10155 | #endif |
10090 | 10156 | ||
10091 | #if 0 | 10157 | #if 0 /* disabled: nonblock_safe_read() handles this problem */ |
10092 | /* nonblock_safe_read() handles this problem */ | ||
10093 | if (nr < 0) { | 10158 | if (nr < 0) { |
10094 | if (parsefile->fd == 0 && errno == EWOULDBLOCK) { | 10159 | if (parsefile->fd == 0 && errno == EWOULDBLOCK) { |
10095 | int flags = fcntl(0, F_GETFL); | 10160 | int flags = fcntl(0, F_GETFL); |
@@ -12574,9 +12639,7 @@ cmdloop(int top) | |||
12574 | inter = 0; | 12639 | inter = 0; |
12575 | if (iflag && top) { | 12640 | if (iflag && top) { |
12576 | inter++; | 12641 | inter++; |
12577 | #if ENABLE_ASH_MAIL | ||
12578 | chkmail(); | 12642 | chkmail(); |
12579 | #endif | ||
12580 | } | 12643 | } |
12581 | n = parsecmd(inter); | 12644 | n = parsecmd(inter); |
12582 | #if DEBUG | 12645 | #if DEBUG |
@@ -13346,7 +13409,6 @@ ulimitcmd(int argc UNUSED_PARAM, char **argv) | |||
13346 | /* | 13409 | /* |
13347 | * Called to exit the shell. | 13410 | * Called to exit the shell. |
13348 | */ | 13411 | */ |
13349 | static void exitshell(void) NORETURN; | ||
13350 | static void | 13412 | static void |
13351 | exitshell(void) | 13413 | exitshell(void) |
13352 | { | 13414 | { |