diff options
author | vda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2007-02-23 21:10:06 +0000 |
---|---|---|
committer | vda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2007-02-23 21:10:06 +0000 |
commit | 90ca2c0df223dace62cb3b2b4fa33538faa915f2 (patch) | |
tree | a686bc556a68108c4c4638f5c9de8bc41abe31ab /shell | |
parent | 92cc1717a9e2a69b24ba4ff44f1fce702d9edf88 (diff) | |
download | busybox-w32-90ca2c0df223dace62cb3b2b4fa33538faa915f2.tar.gz busybox-w32-90ca2c0df223dace62cb3b2b4fa33538faa915f2.tar.bz2 busybox-w32-90ca2c0df223dace62cb3b2b4fa33538faa915f2.zip |
ash: cleanup part 2.4
git-svn-id: svn://busybox.net/trunk/busybox@17966 69ca8d6d-28ef-0310-b511-8ec308f3f277
Diffstat (limited to 'shell')
-rw-r--r-- | shell/ash.c | 3089 |
1 files changed, 1541 insertions, 1548 deletions
diff --git a/shell/ash.c b/shell/ash.c index e3a43979a..869ed12fd 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -1583,8 +1583,18 @@ struct localvar { | |||
1583 | 1583 | ||
1584 | /* Forward decls for varinit[] */ | 1584 | /* Forward decls for varinit[] */ |
1585 | #if ENABLE_LOCALE_SUPPORT | 1585 | #if ENABLE_LOCALE_SUPPORT |
1586 | static void change_lc_all(const char *value); | 1586 | static void |
1587 | static void change_lc_ctype(const char *value); | 1587 | change_lc_all(const char *value) |
1588 | { | ||
1589 | if (value && *value != '\0') | ||
1590 | setlocale(LC_ALL, value); | ||
1591 | } | ||
1592 | static void | ||
1593 | change_lc_ctype(const char *value) | ||
1594 | { | ||
1595 | if (value && *value != '\0') | ||
1596 | setlocale(LC_CTYPE, value); | ||
1597 | } | ||
1588 | #endif | 1598 | #endif |
1589 | #if ENABLE_ASH_MAIL | 1599 | #if ENABLE_ASH_MAIL |
1590 | static void chkmail(void); | 1600 | static void chkmail(void); |
@@ -2863,72 +2873,6 @@ static unsigned long rseed; | |||
2863 | #endif | 2873 | #endif |
2864 | 2874 | ||
2865 | 2875 | ||
2866 | /* jobs.h */ | ||
2867 | |||
2868 | /* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ | ||
2869 | #define FORK_FG 0 | ||
2870 | #define FORK_BG 1 | ||
2871 | #define FORK_NOJOB 2 | ||
2872 | |||
2873 | /* mode flags for showjob(s) */ | ||
2874 | #define SHOW_PGID 0x01 /* only show pgid - for jobs -p */ | ||
2875 | #define SHOW_PID 0x04 /* include process pid */ | ||
2876 | #define SHOW_CHANGED 0x08 /* only jobs whose state has changed */ | ||
2877 | |||
2878 | |||
2879 | /* | ||
2880 | * A job structure contains information about a job. A job is either a | ||
2881 | * single process or a set of processes contained in a pipeline. In the | ||
2882 | * latter case, pidlist will be non-NULL, and will point to a -1 terminated | ||
2883 | * array of pids. | ||
2884 | */ | ||
2885 | |||
2886 | struct procstat { | ||
2887 | pid_t pid; /* process id */ | ||
2888 | int status; /* last process status from wait() */ | ||
2889 | char *cmd; /* text of command being run */ | ||
2890 | }; | ||
2891 | |||
2892 | struct job { | ||
2893 | struct procstat ps0; /* status of process */ | ||
2894 | struct procstat *ps; /* status or processes when more than one */ | ||
2895 | #if JOBS | ||
2896 | int stopstatus; /* status of a stopped job */ | ||
2897 | #endif | ||
2898 | uint32_t | ||
2899 | nprocs: 16, /* number of processes */ | ||
2900 | state: 8, | ||
2901 | #define JOBRUNNING 0 /* at least one proc running */ | ||
2902 | #define JOBSTOPPED 1 /* all procs are stopped */ | ||
2903 | #define JOBDONE 2 /* all procs are completed */ | ||
2904 | #if JOBS | ||
2905 | sigint: 1, /* job was killed by SIGINT */ | ||
2906 | jobctl: 1, /* job running under job control */ | ||
2907 | #endif | ||
2908 | waited: 1, /* true if this entry has been waited for */ | ||
2909 | used: 1, /* true if this entry is in used */ | ||
2910 | changed: 1; /* true if status has changed */ | ||
2911 | struct job *prev_job; /* previous job */ | ||
2912 | }; | ||
2913 | |||
2914 | static pid_t backgndpid; /* pid of last background process */ | ||
2915 | static int job_warning; /* user was warned about stopped jobs */ | ||
2916 | #if JOBS | ||
2917 | static int jobctl; /* true if doing job control */ | ||
2918 | #endif | ||
2919 | |||
2920 | static struct job *makejob(union node *, int); | ||
2921 | static int forkshell(struct job *, union node *, int); | ||
2922 | static int waitforjob(struct job *); | ||
2923 | |||
2924 | #if ! JOBS | ||
2925 | #define setjobctl(on) /* do nothing */ | ||
2926 | #else | ||
2927 | static void setjobctl(int); | ||
2928 | static void showjobs(FILE *, int); | ||
2929 | #endif | ||
2930 | |||
2931 | |||
2932 | /* main.h */ | 2876 | /* main.h */ |
2933 | 2877 | ||
2934 | static void readcmdfile(char *); | 2878 | static void readcmdfile(char *); |
@@ -3159,6 +3103,1535 @@ unaliascmd(int argc, char **argv) | |||
3159 | #endif /* ASH_ALIAS */ | 3103 | #endif /* ASH_ALIAS */ |
3160 | 3104 | ||
3161 | 3105 | ||
3106 | /* ============ jobs.c */ | ||
3107 | |||
3108 | /* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ | ||
3109 | #define FORK_FG 0 | ||
3110 | #define FORK_BG 1 | ||
3111 | #define FORK_NOJOB 2 | ||
3112 | |||
3113 | /* mode flags for showjob(s) */ | ||
3114 | #define SHOW_PGID 0x01 /* only show pgid - for jobs -p */ | ||
3115 | #define SHOW_PID 0x04 /* include process pid */ | ||
3116 | #define SHOW_CHANGED 0x08 /* only jobs whose state has changed */ | ||
3117 | |||
3118 | |||
3119 | /* | ||
3120 | * A job structure contains information about a job. A job is either a | ||
3121 | * single process or a set of processes contained in a pipeline. In the | ||
3122 | * latter case, pidlist will be non-NULL, and will point to a -1 terminated | ||
3123 | * array of pids. | ||
3124 | */ | ||
3125 | |||
3126 | struct procstat { | ||
3127 | pid_t pid; /* process id */ | ||
3128 | int status; /* last process status from wait() */ | ||
3129 | char *cmd; /* text of command being run */ | ||
3130 | }; | ||
3131 | |||
3132 | struct job { | ||
3133 | struct procstat ps0; /* status of process */ | ||
3134 | struct procstat *ps; /* status or processes when more than one */ | ||
3135 | #if JOBS | ||
3136 | int stopstatus; /* status of a stopped job */ | ||
3137 | #endif | ||
3138 | uint32_t | ||
3139 | nprocs: 16, /* number of processes */ | ||
3140 | state: 8, | ||
3141 | #define JOBRUNNING 0 /* at least one proc running */ | ||
3142 | #define JOBSTOPPED 1 /* all procs are stopped */ | ||
3143 | #define JOBDONE 2 /* all procs are completed */ | ||
3144 | #if JOBS | ||
3145 | sigint: 1, /* job was killed by SIGINT */ | ||
3146 | jobctl: 1, /* job running under job control */ | ||
3147 | #endif | ||
3148 | waited: 1, /* true if this entry has been waited for */ | ||
3149 | used: 1, /* true if this entry is in used */ | ||
3150 | changed: 1; /* true if status has changed */ | ||
3151 | struct job *prev_job; /* previous job */ | ||
3152 | }; | ||
3153 | |||
3154 | static pid_t backgndpid; /* pid of last background process */ | ||
3155 | static int job_warning; /* user was warned about stopped jobs */ | ||
3156 | #if JOBS | ||
3157 | static int jobctl; /* true if doing job control */ | ||
3158 | #endif | ||
3159 | |||
3160 | static struct job *makejob(union node *, int); | ||
3161 | static int forkshell(struct job *, union node *, int); | ||
3162 | static int waitforjob(struct job *); | ||
3163 | |||
3164 | #if ! JOBS | ||
3165 | #define setjobctl(on) /* do nothing */ | ||
3166 | #else | ||
3167 | static void setjobctl(int); | ||
3168 | static void showjobs(FILE *, int); | ||
3169 | #endif | ||
3170 | |||
3171 | /* | ||
3172 | * Set the signal handler for the specified signal. The routine figures | ||
3173 | * out what it should be set to. | ||
3174 | */ | ||
3175 | static void | ||
3176 | setsignal(int signo) | ||
3177 | { | ||
3178 | int action; | ||
3179 | char *t, tsig; | ||
3180 | struct sigaction act; | ||
3181 | |||
3182 | t = trap[signo]; | ||
3183 | if (t == NULL) | ||
3184 | action = S_DFL; | ||
3185 | else if (*t != '\0') | ||
3186 | action = S_CATCH; | ||
3187 | else | ||
3188 | action = S_IGN; | ||
3189 | if (rootshell && action == S_DFL) { | ||
3190 | switch (signo) { | ||
3191 | case SIGINT: | ||
3192 | if (iflag || minusc || sflag == 0) | ||
3193 | action = S_CATCH; | ||
3194 | break; | ||
3195 | case SIGQUIT: | ||
3196 | #if DEBUG | ||
3197 | if (debug) | ||
3198 | break; | ||
3199 | #endif | ||
3200 | /* FALLTHROUGH */ | ||
3201 | case SIGTERM: | ||
3202 | if (iflag) | ||
3203 | action = S_IGN; | ||
3204 | break; | ||
3205 | #if JOBS | ||
3206 | case SIGTSTP: | ||
3207 | case SIGTTOU: | ||
3208 | if (mflag) | ||
3209 | action = S_IGN; | ||
3210 | break; | ||
3211 | #endif | ||
3212 | } | ||
3213 | } | ||
3214 | |||
3215 | t = &sigmode[signo - 1]; | ||
3216 | tsig = *t; | ||
3217 | if (tsig == 0) { | ||
3218 | /* | ||
3219 | * current setting unknown | ||
3220 | */ | ||
3221 | if (sigaction(signo, 0, &act) == -1) { | ||
3222 | /* | ||
3223 | * Pretend it worked; maybe we should give a warning | ||
3224 | * here, but other shells don't. We don't alter | ||
3225 | * sigmode, so that we retry every time. | ||
3226 | */ | ||
3227 | return; | ||
3228 | } | ||
3229 | if (act.sa_handler == SIG_IGN) { | ||
3230 | if (mflag | ||
3231 | && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU) | ||
3232 | ) { | ||
3233 | tsig = S_IGN; /* don't hard ignore these */ | ||
3234 | } else | ||
3235 | tsig = S_HARD_IGN; | ||
3236 | } else { | ||
3237 | tsig = S_RESET; /* force to be set */ | ||
3238 | } | ||
3239 | } | ||
3240 | if (tsig == S_HARD_IGN || tsig == action) | ||
3241 | return; | ||
3242 | switch (action) { | ||
3243 | case S_CATCH: | ||
3244 | act.sa_handler = onsig; | ||
3245 | break; | ||
3246 | case S_IGN: | ||
3247 | act.sa_handler = SIG_IGN; | ||
3248 | break; | ||
3249 | default: | ||
3250 | act.sa_handler = SIG_DFL; | ||
3251 | } | ||
3252 | *t = action; | ||
3253 | act.sa_flags = 0; | ||
3254 | sigfillset(&act.sa_mask); | ||
3255 | sigaction(signo, &act, 0); | ||
3256 | } | ||
3257 | |||
3258 | /* mode flags for set_curjob */ | ||
3259 | #define CUR_DELETE 2 | ||
3260 | #define CUR_RUNNING 1 | ||
3261 | #define CUR_STOPPED 0 | ||
3262 | |||
3263 | /* mode flags for dowait */ | ||
3264 | #define DOWAIT_NORMAL 0 | ||
3265 | #define DOWAIT_BLOCK 1 | ||
3266 | |||
3267 | #if JOBS | ||
3268 | /* pgrp of shell on invocation */ | ||
3269 | static int initialpgrp; | ||
3270 | static int ttyfd = -1; | ||
3271 | #endif | ||
3272 | /* array of jobs */ | ||
3273 | static struct job *jobtab; | ||
3274 | /* size of array */ | ||
3275 | static unsigned njobs; | ||
3276 | /* current job */ | ||
3277 | static struct job *curjob; | ||
3278 | /* number of presumed living untracked jobs */ | ||
3279 | static int jobless; | ||
3280 | |||
3281 | static void | ||
3282 | set_curjob(struct job *jp, unsigned mode) | ||
3283 | { | ||
3284 | struct job *jp1; | ||
3285 | struct job **jpp, **curp; | ||
3286 | |||
3287 | /* first remove from list */ | ||
3288 | jpp = curp = &curjob; | ||
3289 | do { | ||
3290 | jp1 = *jpp; | ||
3291 | if (jp1 == jp) | ||
3292 | break; | ||
3293 | jpp = &jp1->prev_job; | ||
3294 | } while (1); | ||
3295 | *jpp = jp1->prev_job; | ||
3296 | |||
3297 | /* Then re-insert in correct position */ | ||
3298 | jpp = curp; | ||
3299 | switch (mode) { | ||
3300 | default: | ||
3301 | #if DEBUG | ||
3302 | abort(); | ||
3303 | #endif | ||
3304 | case CUR_DELETE: | ||
3305 | /* job being deleted */ | ||
3306 | break; | ||
3307 | case CUR_RUNNING: | ||
3308 | /* newly created job or backgrounded job, | ||
3309 | put after all stopped jobs. */ | ||
3310 | do { | ||
3311 | jp1 = *jpp; | ||
3312 | #if JOBS | ||
3313 | if (!jp1 || jp1->state != JOBSTOPPED) | ||
3314 | #endif | ||
3315 | break; | ||
3316 | jpp = &jp1->prev_job; | ||
3317 | } while (1); | ||
3318 | /* FALLTHROUGH */ | ||
3319 | #if JOBS | ||
3320 | case CUR_STOPPED: | ||
3321 | #endif | ||
3322 | /* newly stopped job - becomes curjob */ | ||
3323 | jp->prev_job = *jpp; | ||
3324 | *jpp = jp; | ||
3325 | break; | ||
3326 | } | ||
3327 | } | ||
3328 | |||
3329 | #if JOBS || DEBUG | ||
3330 | static int | ||
3331 | jobno(const struct job *jp) | ||
3332 | { | ||
3333 | return jp - jobtab + 1; | ||
3334 | } | ||
3335 | #endif | ||
3336 | |||
3337 | /* | ||
3338 | * Convert a job name to a job structure. | ||
3339 | */ | ||
3340 | static struct job * | ||
3341 | getjob(const char *name, int getctl) | ||
3342 | { | ||
3343 | struct job *jp; | ||
3344 | struct job *found; | ||
3345 | const char *err_msg = "No such job: %s"; | ||
3346 | unsigned num; | ||
3347 | int c; | ||
3348 | const char *p; | ||
3349 | char *(*match)(const char *, const char *); | ||
3350 | |||
3351 | jp = curjob; | ||
3352 | p = name; | ||
3353 | if (!p) | ||
3354 | goto currentjob; | ||
3355 | |||
3356 | if (*p != '%') | ||
3357 | goto err; | ||
3358 | |||
3359 | c = *++p; | ||
3360 | if (!c) | ||
3361 | goto currentjob; | ||
3362 | |||
3363 | if (!p[1]) { | ||
3364 | if (c == '+' || c == '%') { | ||
3365 | currentjob: | ||
3366 | err_msg = "No current job"; | ||
3367 | goto check; | ||
3368 | } | ||
3369 | if (c == '-') { | ||
3370 | if (jp) | ||
3371 | jp = jp->prev_job; | ||
3372 | err_msg = "No previous job"; | ||
3373 | check: | ||
3374 | if (!jp) | ||
3375 | goto err; | ||
3376 | goto gotit; | ||
3377 | } | ||
3378 | } | ||
3379 | |||
3380 | if (is_number(p)) { | ||
3381 | num = atoi(p); | ||
3382 | if (num < njobs) { | ||
3383 | jp = jobtab + num - 1; | ||
3384 | if (jp->used) | ||
3385 | goto gotit; | ||
3386 | goto err; | ||
3387 | } | ||
3388 | } | ||
3389 | |||
3390 | match = prefix; | ||
3391 | if (*p == '?') { | ||
3392 | match = strstr; | ||
3393 | p++; | ||
3394 | } | ||
3395 | |||
3396 | found = 0; | ||
3397 | while (1) { | ||
3398 | if (!jp) | ||
3399 | goto err; | ||
3400 | if (match(jp->ps[0].cmd, p)) { | ||
3401 | if (found) | ||
3402 | goto err; | ||
3403 | found = jp; | ||
3404 | err_msg = "%s: ambiguous"; | ||
3405 | } | ||
3406 | jp = jp->prev_job; | ||
3407 | } | ||
3408 | |||
3409 | gotit: | ||
3410 | #if JOBS | ||
3411 | err_msg = "job %s not created under job control"; | ||
3412 | if (getctl && jp->jobctl == 0) | ||
3413 | goto err; | ||
3414 | #endif | ||
3415 | return jp; | ||
3416 | err: | ||
3417 | ash_msg_and_raise_error(err_msg, name); | ||
3418 | } | ||
3419 | |||
3420 | /* | ||
3421 | * Mark a job structure as unused. | ||
3422 | */ | ||
3423 | static void | ||
3424 | freejob(struct job *jp) | ||
3425 | { | ||
3426 | struct procstat *ps; | ||
3427 | int i; | ||
3428 | |||
3429 | INT_OFF; | ||
3430 | for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) { | ||
3431 | if (ps->cmd != nullstr) | ||
3432 | free(ps->cmd); | ||
3433 | } | ||
3434 | if (jp->ps != &jp->ps0) | ||
3435 | free(jp->ps); | ||
3436 | jp->used = 0; | ||
3437 | set_curjob(jp, CUR_DELETE); | ||
3438 | INT_ON; | ||
3439 | } | ||
3440 | |||
3441 | #if JOBS | ||
3442 | static void | ||
3443 | xtcsetpgrp(int fd, pid_t pgrp) | ||
3444 | { | ||
3445 | if (tcsetpgrp(fd, pgrp)) | ||
3446 | ash_msg_and_raise_error("Cannot set tty process group (%m)"); | ||
3447 | } | ||
3448 | |||
3449 | /* | ||
3450 | * Turn job control on and off. | ||
3451 | * | ||
3452 | * Note: This code assumes that the third arg to ioctl is a character | ||
3453 | * pointer, which is true on Berkeley systems but not System V. Since | ||
3454 | * System V doesn't have job control yet, this isn't a problem now. | ||
3455 | * | ||
3456 | * Called with interrupts off. | ||
3457 | */ | ||
3458 | static void | ||
3459 | setjobctl(int on) | ||
3460 | { | ||
3461 | int fd; | ||
3462 | int pgrp; | ||
3463 | |||
3464 | if (on == jobctl || rootshell == 0) | ||
3465 | return; | ||
3466 | if (on) { | ||
3467 | int ofd; | ||
3468 | ofd = fd = open(_PATH_TTY, O_RDWR); | ||
3469 | if (fd < 0) { | ||
3470 | /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails. | ||
3471 | * That sometimes helps to acquire controlling tty. | ||
3472 | * Obviously, a workaround for bugs when someone | ||
3473 | * failed to provide a controlling tty to bash! :) */ | ||
3474 | fd += 3; | ||
3475 | while (!isatty(fd) && --fd >= 0) | ||
3476 | ; | ||
3477 | } | ||
3478 | fd = fcntl(fd, F_DUPFD, 10); | ||
3479 | close(ofd); | ||
3480 | if (fd < 0) | ||
3481 | goto out; | ||
3482 | fcntl(fd, F_SETFD, FD_CLOEXEC); | ||
3483 | do { /* while we are in the background */ | ||
3484 | pgrp = tcgetpgrp(fd); | ||
3485 | if (pgrp < 0) { | ||
3486 | out: | ||
3487 | ash_msg("can't access tty; job control turned off"); | ||
3488 | mflag = on = 0; | ||
3489 | goto close; | ||
3490 | } | ||
3491 | if (pgrp == getpgrp()) | ||
3492 | break; | ||
3493 | killpg(0, SIGTTIN); | ||
3494 | } while (1); | ||
3495 | initialpgrp = pgrp; | ||
3496 | |||
3497 | setsignal(SIGTSTP); | ||
3498 | setsignal(SIGTTOU); | ||
3499 | setsignal(SIGTTIN); | ||
3500 | pgrp = rootpid; | ||
3501 | setpgid(0, pgrp); | ||
3502 | xtcsetpgrp(fd, pgrp); | ||
3503 | } else { | ||
3504 | /* turning job control off */ | ||
3505 | fd = ttyfd; | ||
3506 | pgrp = initialpgrp; | ||
3507 | xtcsetpgrp(fd, pgrp); | ||
3508 | setpgid(0, pgrp); | ||
3509 | setsignal(SIGTSTP); | ||
3510 | setsignal(SIGTTOU); | ||
3511 | setsignal(SIGTTIN); | ||
3512 | close: | ||
3513 | close(fd); | ||
3514 | fd = -1; | ||
3515 | } | ||
3516 | ttyfd = fd; | ||
3517 | jobctl = on; | ||
3518 | } | ||
3519 | |||
3520 | static int | ||
3521 | killcmd(int argc, char **argv) | ||
3522 | { | ||
3523 | int signo = -1; | ||
3524 | int list = 0; | ||
3525 | int i; | ||
3526 | pid_t pid; | ||
3527 | struct job *jp; | ||
3528 | |||
3529 | if (argc <= 1) { | ||
3530 | usage: | ||
3531 | ash_msg_and_raise_error( | ||
3532 | "Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n" | ||
3533 | "kill -l [exitstatus]" | ||
3534 | ); | ||
3535 | } | ||
3536 | |||
3537 | if (**++argv == '-') { | ||
3538 | signo = get_signum(*argv + 1); | ||
3539 | if (signo < 0) { | ||
3540 | int c; | ||
3541 | |||
3542 | while ((c = nextopt("ls:")) != '\0') { | ||
3543 | switch (c) { | ||
3544 | default: | ||
3545 | #if DEBUG | ||
3546 | abort(); | ||
3547 | #endif | ||
3548 | case 'l': | ||
3549 | list = 1; | ||
3550 | break; | ||
3551 | case 's': | ||
3552 | signo = get_signum(optionarg); | ||
3553 | if (signo < 0) { | ||
3554 | ash_msg_and_raise_error( | ||
3555 | "invalid signal number or name: %s", | ||
3556 | optionarg | ||
3557 | ); | ||
3558 | } | ||
3559 | break; | ||
3560 | } | ||
3561 | } | ||
3562 | argv = argptr; | ||
3563 | } else | ||
3564 | argv++; | ||
3565 | } | ||
3566 | |||
3567 | if (!list && signo < 0) | ||
3568 | signo = SIGTERM; | ||
3569 | |||
3570 | if ((signo < 0 || !*argv) ^ list) { | ||
3571 | goto usage; | ||
3572 | } | ||
3573 | |||
3574 | if (list) { | ||
3575 | const char *name; | ||
3576 | |||
3577 | if (!*argv) { | ||
3578 | for (i = 1; i < NSIG; i++) { | ||
3579 | name = get_signame(i); | ||
3580 | if (isdigit(*name)) | ||
3581 | out1fmt(snlfmt, name); | ||
3582 | } | ||
3583 | return 0; | ||
3584 | } | ||
3585 | name = get_signame(signo); | ||
3586 | if (!isdigit(*name)) | ||
3587 | ash_msg_and_raise_error("invalid signal number or exit status: %s", *argptr); | ||
3588 | out1fmt(snlfmt, name); | ||
3589 | return 0; | ||
3590 | } | ||
3591 | |||
3592 | i = 0; | ||
3593 | do { | ||
3594 | if (**argv == '%') { | ||
3595 | jp = getjob(*argv, 0); | ||
3596 | pid = -jp->ps[0].pid; | ||
3597 | } else { | ||
3598 | pid = **argv == '-' ? | ||
3599 | -number(*argv + 1) : number(*argv); | ||
3600 | } | ||
3601 | if (kill(pid, signo) != 0) { | ||
3602 | ash_msg("(%d) - %m", pid); | ||
3603 | i = 1; | ||
3604 | } | ||
3605 | } while (*++argv); | ||
3606 | |||
3607 | return i; | ||
3608 | } | ||
3609 | |||
3610 | static void | ||
3611 | showpipe(struct job *jp, FILE *out) | ||
3612 | { | ||
3613 | struct procstat *sp; | ||
3614 | struct procstat *spend; | ||
3615 | |||
3616 | spend = jp->ps + jp->nprocs; | ||
3617 | for (sp = jp->ps + 1; sp < spend; sp++) | ||
3618 | fprintf(out, " | %s", sp->cmd); | ||
3619 | outcslow('\n', out); | ||
3620 | flush_stdout_stderr(); | ||
3621 | } | ||
3622 | |||
3623 | |||
3624 | static int | ||
3625 | restartjob(struct job *jp, int mode) | ||
3626 | { | ||
3627 | struct procstat *ps; | ||
3628 | int i; | ||
3629 | int status; | ||
3630 | pid_t pgid; | ||
3631 | |||
3632 | INT_OFF; | ||
3633 | if (jp->state == JOBDONE) | ||
3634 | goto out; | ||
3635 | jp->state = JOBRUNNING; | ||
3636 | pgid = jp->ps->pid; | ||
3637 | if (mode == FORK_FG) | ||
3638 | xtcsetpgrp(ttyfd, pgid); | ||
3639 | killpg(pgid, SIGCONT); | ||
3640 | ps = jp->ps; | ||
3641 | i = jp->nprocs; | ||
3642 | do { | ||
3643 | if (WIFSTOPPED(ps->status)) { | ||
3644 | ps->status = -1; | ||
3645 | } | ||
3646 | } while (ps++, --i); | ||
3647 | out: | ||
3648 | status = (mode == FORK_FG) ? waitforjob(jp) : 0; | ||
3649 | INT_ON; | ||
3650 | return status; | ||
3651 | } | ||
3652 | |||
3653 | static int | ||
3654 | fg_bgcmd(int argc, char **argv) | ||
3655 | { | ||
3656 | struct job *jp; | ||
3657 | FILE *out; | ||
3658 | int mode; | ||
3659 | int retval; | ||
3660 | |||
3661 | mode = (**argv == 'f') ? FORK_FG : FORK_BG; | ||
3662 | nextopt(nullstr); | ||
3663 | argv = argptr; | ||
3664 | out = stdout; | ||
3665 | do { | ||
3666 | jp = getjob(*argv, 1); | ||
3667 | if (mode == FORK_BG) { | ||
3668 | set_curjob(jp, CUR_RUNNING); | ||
3669 | fprintf(out, "[%d] ", jobno(jp)); | ||
3670 | } | ||
3671 | outstr(jp->ps->cmd, out); | ||
3672 | showpipe(jp, out); | ||
3673 | retval = restartjob(jp, mode); | ||
3674 | } while (*argv && *++argv); | ||
3675 | return retval; | ||
3676 | } | ||
3677 | #endif | ||
3678 | |||
3679 | static int | ||
3680 | sprint_status(char *s, int status, int sigonly) | ||
3681 | { | ||
3682 | int col; | ||
3683 | int st; | ||
3684 | |||
3685 | col = 0; | ||
3686 | if (!WIFEXITED(status)) { | ||
3687 | #if JOBS | ||
3688 | if (WIFSTOPPED(status)) | ||
3689 | st = WSTOPSIG(status); | ||
3690 | else | ||
3691 | #endif | ||
3692 | st = WTERMSIG(status); | ||
3693 | if (sigonly) { | ||
3694 | if (st == SIGINT || st == SIGPIPE) | ||
3695 | goto out; | ||
3696 | #if JOBS | ||
3697 | if (WIFSTOPPED(status)) | ||
3698 | goto out; | ||
3699 | #endif | ||
3700 | } | ||
3701 | st &= 0x7f; | ||
3702 | col = fmtstr(s, 32, strsignal(st)); | ||
3703 | if (WCOREDUMP(status)) { | ||
3704 | col += fmtstr(s + col, 16, " (core dumped)"); | ||
3705 | } | ||
3706 | } else if (!sigonly) { | ||
3707 | st = WEXITSTATUS(status); | ||
3708 | if (st) | ||
3709 | col = fmtstr(s, 16, "Done(%d)", st); | ||
3710 | else | ||
3711 | col = fmtstr(s, 16, "Done"); | ||
3712 | } | ||
3713 | out: | ||
3714 | return col; | ||
3715 | } | ||
3716 | |||
3717 | /* | ||
3718 | * Do a wait system call. If job control is compiled in, we accept | ||
3719 | * stopped processes. If block is zero, we return a value of zero | ||
3720 | * rather than blocking. | ||
3721 | * | ||
3722 | * System V doesn't have a non-blocking wait system call. It does | ||
3723 | * have a SIGCLD signal that is sent to a process when one of it's | ||
3724 | * children dies. The obvious way to use SIGCLD would be to install | ||
3725 | * a handler for SIGCLD which simply bumped a counter when a SIGCLD | ||
3726 | * was received, and have waitproc bump another counter when it got | ||
3727 | * the status of a process. Waitproc would then know that a wait | ||
3728 | * system call would not block if the two counters were different. | ||
3729 | * This approach doesn't work because if a process has children that | ||
3730 | * have not been waited for, System V will send it a SIGCLD when it | ||
3731 | * installs a signal handler for SIGCLD. What this means is that when | ||
3732 | * a child exits, the shell will be sent SIGCLD signals continuously | ||
3733 | * until is runs out of stack space, unless it does a wait call before | ||
3734 | * restoring the signal handler. The code below takes advantage of | ||
3735 | * this (mis)feature by installing a signal handler for SIGCLD and | ||
3736 | * then checking to see whether it was called. If there are any | ||
3737 | * children to be waited for, it will be. | ||
3738 | * | ||
3739 | * If neither SYSV nor BSD is defined, we don't implement nonblocking | ||
3740 | * waits at all. In this case, the user will not be informed when | ||
3741 | * a background process until the next time she runs a real program | ||
3742 | * (as opposed to running a builtin command or just typing return), | ||
3743 | * and the jobs command may give out of date information. | ||
3744 | */ | ||
3745 | static int | ||
3746 | waitproc(int block, int *status) | ||
3747 | { | ||
3748 | int flags = 0; | ||
3749 | |||
3750 | #if JOBS | ||
3751 | if (jobctl) | ||
3752 | flags |= WUNTRACED; | ||
3753 | #endif | ||
3754 | if (block == 0) | ||
3755 | flags |= WNOHANG; | ||
3756 | return wait3(status, flags, (struct rusage *)NULL); | ||
3757 | } | ||
3758 | |||
3759 | /* | ||
3760 | * Wait for a process to terminate. | ||
3761 | */ | ||
3762 | static int | ||
3763 | dowait(int block, struct job *job) | ||
3764 | { | ||
3765 | int pid; | ||
3766 | int status; | ||
3767 | struct job *jp; | ||
3768 | struct job *thisjob; | ||
3769 | int state; | ||
3770 | |||
3771 | TRACE(("dowait(%d) called\n", block)); | ||
3772 | pid = waitproc(block, &status); | ||
3773 | TRACE(("wait returns pid %d, status=%d\n", pid, status)); | ||
3774 | if (pid <= 0) | ||
3775 | return pid; | ||
3776 | INT_OFF; | ||
3777 | thisjob = NULL; | ||
3778 | for (jp = curjob; jp; jp = jp->prev_job) { | ||
3779 | struct procstat *sp; | ||
3780 | struct procstat *spend; | ||
3781 | if (jp->state == JOBDONE) | ||
3782 | continue; | ||
3783 | state = JOBDONE; | ||
3784 | spend = jp->ps + jp->nprocs; | ||
3785 | sp = jp->ps; | ||
3786 | do { | ||
3787 | if (sp->pid == pid) { | ||
3788 | TRACE(("Job %d: changing status of proc %d " | ||
3789 | "from 0x%x to 0x%x\n", | ||
3790 | jobno(jp), pid, sp->status, status)); | ||
3791 | sp->status = status; | ||
3792 | thisjob = jp; | ||
3793 | } | ||
3794 | if (sp->status == -1) | ||
3795 | state = JOBRUNNING; | ||
3796 | #if JOBS | ||
3797 | if (state == JOBRUNNING) | ||
3798 | continue; | ||
3799 | if (WIFSTOPPED(sp->status)) { | ||
3800 | jp->stopstatus = sp->status; | ||
3801 | state = JOBSTOPPED; | ||
3802 | } | ||
3803 | #endif | ||
3804 | } while (++sp < spend); | ||
3805 | if (thisjob) | ||
3806 | goto gotjob; | ||
3807 | } | ||
3808 | #if JOBS | ||
3809 | if (!WIFSTOPPED(status)) | ||
3810 | #endif | ||
3811 | |||
3812 | jobless--; | ||
3813 | goto out; | ||
3814 | |||
3815 | gotjob: | ||
3816 | if (state != JOBRUNNING) { | ||
3817 | thisjob->changed = 1; | ||
3818 | |||
3819 | if (thisjob->state != state) { | ||
3820 | TRACE(("Job %d: changing state from %d to %d\n", | ||
3821 | jobno(thisjob), thisjob->state, state)); | ||
3822 | thisjob->state = state; | ||
3823 | #if JOBS | ||
3824 | if (state == JOBSTOPPED) { | ||
3825 | set_curjob(thisjob, CUR_STOPPED); | ||
3826 | } | ||
3827 | #endif | ||
3828 | } | ||
3829 | } | ||
3830 | |||
3831 | out: | ||
3832 | INT_ON; | ||
3833 | |||
3834 | if (thisjob && thisjob == job) { | ||
3835 | char s[48 + 1]; | ||
3836 | int len; | ||
3837 | |||
3838 | len = sprint_status(s, status, 1); | ||
3839 | if (len) { | ||
3840 | s[len] = '\n'; | ||
3841 | s[len + 1] = 0; | ||
3842 | out2str(s); | ||
3843 | } | ||
3844 | } | ||
3845 | return pid; | ||
3846 | } | ||
3847 | |||
3848 | #if JOBS | ||
3849 | static void | ||
3850 | showjob(FILE *out, struct job *jp, int mode) | ||
3851 | { | ||
3852 | struct procstat *ps; | ||
3853 | struct procstat *psend; | ||
3854 | int col; | ||
3855 | int indent; | ||
3856 | char s[80]; | ||
3857 | |||
3858 | ps = jp->ps; | ||
3859 | |||
3860 | if (mode & SHOW_PGID) { | ||
3861 | /* just output process (group) id of pipeline */ | ||
3862 | fprintf(out, "%d\n", ps->pid); | ||
3863 | return; | ||
3864 | } | ||
3865 | |||
3866 | col = fmtstr(s, 16, "[%d] ", jobno(jp)); | ||
3867 | indent = col; | ||
3868 | |||
3869 | if (jp == curjob) | ||
3870 | s[col - 2] = '+'; | ||
3871 | else if (curjob && jp == curjob->prev_job) | ||
3872 | s[col - 2] = '-'; | ||
3873 | |||
3874 | if (mode & SHOW_PID) | ||
3875 | col += fmtstr(s + col, 16, "%d ", ps->pid); | ||
3876 | |||
3877 | psend = ps + jp->nprocs; | ||
3878 | |||
3879 | if (jp->state == JOBRUNNING) { | ||
3880 | strcpy(s + col, "Running"); | ||
3881 | col += sizeof("Running") - 1; | ||
3882 | } else { | ||
3883 | int status = psend[-1].status; | ||
3884 | if (jp->state == JOBSTOPPED) | ||
3885 | status = jp->stopstatus; | ||
3886 | col += sprint_status(s + col, status, 0); | ||
3887 | } | ||
3888 | |||
3889 | goto start; | ||
3890 | |||
3891 | do { | ||
3892 | /* for each process */ | ||
3893 | col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3; | ||
3894 | start: | ||
3895 | fprintf(out, "%s%*c%s", | ||
3896 | s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd | ||
3897 | ); | ||
3898 | if (!(mode & SHOW_PID)) { | ||
3899 | showpipe(jp, out); | ||
3900 | break; | ||
3901 | } | ||
3902 | if (++ps == psend) { | ||
3903 | outcslow('\n', out); | ||
3904 | break; | ||
3905 | } | ||
3906 | } while (1); | ||
3907 | |||
3908 | jp->changed = 0; | ||
3909 | |||
3910 | if (jp->state == JOBDONE) { | ||
3911 | TRACE(("showjob: freeing job %d\n", jobno(jp))); | ||
3912 | freejob(jp); | ||
3913 | } | ||
3914 | } | ||
3915 | |||
3916 | static int | ||
3917 | jobscmd(int argc, char **argv) | ||
3918 | { | ||
3919 | int mode, m; | ||
3920 | FILE *out; | ||
3921 | |||
3922 | mode = 0; | ||
3923 | while ((m = nextopt("lp"))) { | ||
3924 | if (m == 'l') | ||
3925 | mode = SHOW_PID; | ||
3926 | else | ||
3927 | mode = SHOW_PGID; | ||
3928 | } | ||
3929 | |||
3930 | out = stdout; | ||
3931 | argv = argptr; | ||
3932 | if (*argv) { | ||
3933 | do | ||
3934 | showjob(out, getjob(*argv,0), mode); | ||
3935 | while (*++argv); | ||
3936 | } else | ||
3937 | showjobs(out, mode); | ||
3938 | |||
3939 | return 0; | ||
3940 | } | ||
3941 | |||
3942 | /* | ||
3943 | * Print a list of jobs. If "change" is nonzero, only print jobs whose | ||
3944 | * statuses have changed since the last call to showjobs. | ||
3945 | */ | ||
3946 | static void | ||
3947 | showjobs(FILE *out, int mode) | ||
3948 | { | ||
3949 | struct job *jp; | ||
3950 | |||
3951 | TRACE(("showjobs(%x) called\n", mode)); | ||
3952 | |||
3953 | /* If not even one one job changed, there is nothing to do */ | ||
3954 | while (dowait(DOWAIT_NORMAL, NULL) > 0) | ||
3955 | continue; | ||
3956 | |||
3957 | for (jp = curjob; jp; jp = jp->prev_job) { | ||
3958 | if (!(mode & SHOW_CHANGED) || jp->changed) | ||
3959 | showjob(out, jp, mode); | ||
3960 | } | ||
3961 | } | ||
3962 | #endif /* JOBS */ | ||
3963 | |||
3964 | static int | ||
3965 | getstatus(struct job *job) | ||
3966 | { | ||
3967 | int status; | ||
3968 | int retval; | ||
3969 | |||
3970 | status = job->ps[job->nprocs - 1].status; | ||
3971 | retval = WEXITSTATUS(status); | ||
3972 | if (!WIFEXITED(status)) { | ||
3973 | #if JOBS | ||
3974 | retval = WSTOPSIG(status); | ||
3975 | if (!WIFSTOPPED(status)) | ||
3976 | #endif | ||
3977 | { | ||
3978 | /* XXX: limits number of signals */ | ||
3979 | retval = WTERMSIG(status); | ||
3980 | #if JOBS | ||
3981 | if (retval == SIGINT) | ||
3982 | job->sigint = 1; | ||
3983 | #endif | ||
3984 | } | ||
3985 | retval += 128; | ||
3986 | } | ||
3987 | TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n", | ||
3988 | jobno(job), job->nprocs, status, retval)); | ||
3989 | return retval; | ||
3990 | } | ||
3991 | |||
3992 | static int | ||
3993 | waitcmd(int argc, char **argv) | ||
3994 | { | ||
3995 | struct job *job; | ||
3996 | int retval; | ||
3997 | struct job *jp; | ||
3998 | |||
3999 | EXSIGON; | ||
4000 | |||
4001 | nextopt(nullstr); | ||
4002 | retval = 0; | ||
4003 | |||
4004 | argv = argptr; | ||
4005 | if (!*argv) { | ||
4006 | /* wait for all jobs */ | ||
4007 | for (;;) { | ||
4008 | jp = curjob; | ||
4009 | while (1) { | ||
4010 | if (!jp) { | ||
4011 | /* no running procs */ | ||
4012 | goto out; | ||
4013 | } | ||
4014 | if (jp->state == JOBRUNNING) | ||
4015 | break; | ||
4016 | jp->waited = 1; | ||
4017 | jp = jp->prev_job; | ||
4018 | } | ||
4019 | dowait(DOWAIT_BLOCK, 0); | ||
4020 | } | ||
4021 | } | ||
4022 | |||
4023 | retval = 127; | ||
4024 | do { | ||
4025 | if (**argv != '%') { | ||
4026 | pid_t pid = number(*argv); | ||
4027 | job = curjob; | ||
4028 | goto start; | ||
4029 | do { | ||
4030 | if (job->ps[job->nprocs - 1].pid == pid) | ||
4031 | break; | ||
4032 | job = job->prev_job; | ||
4033 | start: | ||
4034 | if (!job) | ||
4035 | goto repeat; | ||
4036 | } while (1); | ||
4037 | } else | ||
4038 | job = getjob(*argv, 0); | ||
4039 | /* loop until process terminated or stopped */ | ||
4040 | while (job->state == JOBRUNNING) | ||
4041 | dowait(DOWAIT_BLOCK, 0); | ||
4042 | job->waited = 1; | ||
4043 | retval = getstatus(job); | ||
4044 | repeat: | ||
4045 | ; | ||
4046 | } while (*++argv); | ||
4047 | |||
4048 | out: | ||
4049 | return retval; | ||
4050 | } | ||
4051 | |||
4052 | static struct job * | ||
4053 | growjobtab(void) | ||
4054 | { | ||
4055 | size_t len; | ||
4056 | ptrdiff_t offset; | ||
4057 | struct job *jp, *jq; | ||
4058 | |||
4059 | len = njobs * sizeof(*jp); | ||
4060 | jq = jobtab; | ||
4061 | jp = ckrealloc(jq, len + 4 * sizeof(*jp)); | ||
4062 | |||
4063 | offset = (char *)jp - (char *)jq; | ||
4064 | if (offset) { | ||
4065 | /* Relocate pointers */ | ||
4066 | size_t l = len; | ||
4067 | |||
4068 | jq = (struct job *)((char *)jq + l); | ||
4069 | while (l) { | ||
4070 | l -= sizeof(*jp); | ||
4071 | jq--; | ||
4072 | #define joff(p) ((struct job *)((char *)(p) + l)) | ||
4073 | #define jmove(p) (p) = (void *)((char *)(p) + offset) | ||
4074 | if (joff(jp)->ps == &jq->ps0) | ||
4075 | jmove(joff(jp)->ps); | ||
4076 | if (joff(jp)->prev_job) | ||
4077 | jmove(joff(jp)->prev_job); | ||
4078 | } | ||
4079 | if (curjob) | ||
4080 | jmove(curjob); | ||
4081 | #undef joff | ||
4082 | #undef jmove | ||
4083 | } | ||
4084 | |||
4085 | njobs += 4; | ||
4086 | jobtab = jp; | ||
4087 | jp = (struct job *)((char *)jp + len); | ||
4088 | jq = jp + 3; | ||
4089 | do { | ||
4090 | jq->used = 0; | ||
4091 | } while (--jq >= jp); | ||
4092 | return jp; | ||
4093 | } | ||
4094 | |||
4095 | /* | ||
4096 | * Return a new job structure. | ||
4097 | * Called with interrupts off. | ||
4098 | */ | ||
4099 | static struct job * | ||
4100 | makejob(union node *node, int nprocs) | ||
4101 | { | ||
4102 | int i; | ||
4103 | struct job *jp; | ||
4104 | |||
4105 | for (i = njobs, jp = jobtab; ; jp++) { | ||
4106 | if (--i < 0) { | ||
4107 | jp = growjobtab(); | ||
4108 | break; | ||
4109 | } | ||
4110 | if (jp->used == 0) | ||
4111 | break; | ||
4112 | if (jp->state != JOBDONE || !jp->waited) | ||
4113 | continue; | ||
4114 | #if JOBS | ||
4115 | if (jobctl) | ||
4116 | continue; | ||
4117 | #endif | ||
4118 | freejob(jp); | ||
4119 | break; | ||
4120 | } | ||
4121 | memset(jp, 0, sizeof(*jp)); | ||
4122 | #if JOBS | ||
4123 | if (jobctl) | ||
4124 | jp->jobctl = 1; | ||
4125 | #endif | ||
4126 | jp->prev_job = curjob; | ||
4127 | curjob = jp; | ||
4128 | jp->used = 1; | ||
4129 | jp->ps = &jp->ps0; | ||
4130 | if (nprocs > 1) { | ||
4131 | jp->ps = ckmalloc(nprocs * sizeof(struct procstat)); | ||
4132 | } | ||
4133 | TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs, | ||
4134 | jobno(jp))); | ||
4135 | return jp; | ||
4136 | } | ||
4137 | |||
4138 | #if JOBS | ||
4139 | /* | ||
4140 | * Return a string identifying a command (to be printed by the | ||
4141 | * jobs command). | ||
4142 | */ | ||
4143 | static char *cmdnextc; | ||
4144 | |||
4145 | static void | ||
4146 | cmdputs(const char *s) | ||
4147 | { | ||
4148 | const char *p, *str; | ||
4149 | char c, cc[2] = " "; | ||
4150 | char *nextc; | ||
4151 | int subtype = 0; | ||
4152 | int quoted = 0; | ||
4153 | static const char vstype[VSTYPE + 1][4] = { | ||
4154 | "", "}", "-", "+", "?", "=", | ||
4155 | "%", "%%", "#", "##" | ||
4156 | }; | ||
4157 | |||
4158 | nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc); | ||
4159 | p = s; | ||
4160 | while ((c = *p++) != 0) { | ||
4161 | str = 0; | ||
4162 | switch (c) { | ||
4163 | case CTLESC: | ||
4164 | c = *p++; | ||
4165 | break; | ||
4166 | case CTLVAR: | ||
4167 | subtype = *p++; | ||
4168 | if ((subtype & VSTYPE) == VSLENGTH) | ||
4169 | str = "${#"; | ||
4170 | else | ||
4171 | str = "${"; | ||
4172 | if (!(subtype & VSQUOTE) == !(quoted & 1)) | ||
4173 | goto dostr; | ||
4174 | quoted ^= 1; | ||
4175 | c = '"'; | ||
4176 | break; | ||
4177 | case CTLENDVAR: | ||
4178 | str = "\"}" + !(quoted & 1); | ||
4179 | quoted >>= 1; | ||
4180 | subtype = 0; | ||
4181 | goto dostr; | ||
4182 | case CTLBACKQ: | ||
4183 | str = "$(...)"; | ||
4184 | goto dostr; | ||
4185 | case CTLBACKQ+CTLQUOTE: | ||
4186 | str = "\"$(...)\""; | ||
4187 | goto dostr; | ||
4188 | #if ENABLE_ASH_MATH_SUPPORT | ||
4189 | case CTLARI: | ||
4190 | str = "$(("; | ||
4191 | goto dostr; | ||
4192 | case CTLENDARI: | ||
4193 | str = "))"; | ||
4194 | goto dostr; | ||
4195 | #endif | ||
4196 | case CTLQUOTEMARK: | ||
4197 | quoted ^= 1; | ||
4198 | c = '"'; | ||
4199 | break; | ||
4200 | case '=': | ||
4201 | if (subtype == 0) | ||
4202 | break; | ||
4203 | if ((subtype & VSTYPE) != VSNORMAL) | ||
4204 | quoted <<= 1; | ||
4205 | str = vstype[subtype & VSTYPE]; | ||
4206 | if (subtype & VSNUL) | ||
4207 | c = ':'; | ||
4208 | else | ||
4209 | goto checkstr; | ||
4210 | break; | ||
4211 | case '\'': | ||
4212 | case '\\': | ||
4213 | case '"': | ||
4214 | case '$': | ||
4215 | /* These can only happen inside quotes */ | ||
4216 | cc[0] = c; | ||
4217 | str = cc; | ||
4218 | c = '\\'; | ||
4219 | break; | ||
4220 | default: | ||
4221 | break; | ||
4222 | } | ||
4223 | USTPUTC(c, nextc); | ||
4224 | checkstr: | ||
4225 | if (!str) | ||
4226 | continue; | ||
4227 | dostr: | ||
4228 | while ((c = *str++)) { | ||
4229 | USTPUTC(c, nextc); | ||
4230 | } | ||
4231 | } | ||
4232 | if (quoted & 1) { | ||
4233 | USTPUTC('"', nextc); | ||
4234 | } | ||
4235 | *nextc = 0; | ||
4236 | cmdnextc = nextc; | ||
4237 | } | ||
4238 | |||
4239 | /* cmdtxt() and cmdlist() call each other */ | ||
4240 | static void cmdtxt(union node *n); | ||
4241 | |||
4242 | static void | ||
4243 | cmdlist(union node *np, int sep) | ||
4244 | { | ||
4245 | for (; np; np = np->narg.next) { | ||
4246 | if (!sep) | ||
4247 | cmdputs(spcstr); | ||
4248 | cmdtxt(np); | ||
4249 | if (sep && np->narg.next) | ||
4250 | cmdputs(spcstr); | ||
4251 | } | ||
4252 | } | ||
4253 | |||
4254 | static void | ||
4255 | cmdtxt(union node *n) | ||
4256 | { | ||
4257 | union node *np; | ||
4258 | struct nodelist *lp; | ||
4259 | const char *p; | ||
4260 | char s[2]; | ||
4261 | |||
4262 | if (!n) | ||
4263 | return; | ||
4264 | switch (n->type) { | ||
4265 | default: | ||
4266 | #if DEBUG | ||
4267 | abort(); | ||
4268 | #endif | ||
4269 | case NPIPE: | ||
4270 | lp = n->npipe.cmdlist; | ||
4271 | for (;;) { | ||
4272 | cmdtxt(lp->n); | ||
4273 | lp = lp->next; | ||
4274 | if (!lp) | ||
4275 | break; | ||
4276 | cmdputs(" | "); | ||
4277 | } | ||
4278 | break; | ||
4279 | case NSEMI: | ||
4280 | p = "; "; | ||
4281 | goto binop; | ||
4282 | case NAND: | ||
4283 | p = " && "; | ||
4284 | goto binop; | ||
4285 | case NOR: | ||
4286 | p = " || "; | ||
4287 | binop: | ||
4288 | cmdtxt(n->nbinary.ch1); | ||
4289 | cmdputs(p); | ||
4290 | n = n->nbinary.ch2; | ||
4291 | goto donode; | ||
4292 | case NREDIR: | ||
4293 | case NBACKGND: | ||
4294 | n = n->nredir.n; | ||
4295 | goto donode; | ||
4296 | case NNOT: | ||
4297 | cmdputs("!"); | ||
4298 | n = n->nnot.com; | ||
4299 | donode: | ||
4300 | cmdtxt(n); | ||
4301 | break; | ||
4302 | case NIF: | ||
4303 | cmdputs("if "); | ||
4304 | cmdtxt(n->nif.test); | ||
4305 | cmdputs("; then "); | ||
4306 | n = n->nif.ifpart; | ||
4307 | if (n->nif.elsepart) { | ||
4308 | cmdtxt(n); | ||
4309 | cmdputs("; else "); | ||
4310 | n = n->nif.elsepart; | ||
4311 | } | ||
4312 | p = "; fi"; | ||
4313 | goto dotail; | ||
4314 | case NSUBSHELL: | ||
4315 | cmdputs("("); | ||
4316 | n = n->nredir.n; | ||
4317 | p = ")"; | ||
4318 | goto dotail; | ||
4319 | case NWHILE: | ||
4320 | p = "while "; | ||
4321 | goto until; | ||
4322 | case NUNTIL: | ||
4323 | p = "until "; | ||
4324 | until: | ||
4325 | cmdputs(p); | ||
4326 | cmdtxt(n->nbinary.ch1); | ||
4327 | n = n->nbinary.ch2; | ||
4328 | p = "; done"; | ||
4329 | dodo: | ||
4330 | cmdputs("; do "); | ||
4331 | dotail: | ||
4332 | cmdtxt(n); | ||
4333 | goto dotail2; | ||
4334 | case NFOR: | ||
4335 | cmdputs("for "); | ||
4336 | cmdputs(n->nfor.var); | ||
4337 | cmdputs(" in "); | ||
4338 | cmdlist(n->nfor.args, 1); | ||
4339 | n = n->nfor.body; | ||
4340 | p = "; done"; | ||
4341 | goto dodo; | ||
4342 | case NDEFUN: | ||
4343 | cmdputs(n->narg.text); | ||
4344 | p = "() { ... }"; | ||
4345 | goto dotail2; | ||
4346 | case NCMD: | ||
4347 | cmdlist(n->ncmd.args, 1); | ||
4348 | cmdlist(n->ncmd.redirect, 0); | ||
4349 | break; | ||
4350 | case NARG: | ||
4351 | p = n->narg.text; | ||
4352 | dotail2: | ||
4353 | cmdputs(p); | ||
4354 | break; | ||
4355 | case NHERE: | ||
4356 | case NXHERE: | ||
4357 | p = "<<..."; | ||
4358 | goto dotail2; | ||
4359 | case NCASE: | ||
4360 | cmdputs("case "); | ||
4361 | cmdputs(n->ncase.expr->narg.text); | ||
4362 | cmdputs(" in "); | ||
4363 | for (np = n->ncase.cases; np; np = np->nclist.next) { | ||
4364 | cmdtxt(np->nclist.pattern); | ||
4365 | cmdputs(") "); | ||
4366 | cmdtxt(np->nclist.body); | ||
4367 | cmdputs(";; "); | ||
4368 | } | ||
4369 | p = "esac"; | ||
4370 | goto dotail2; | ||
4371 | case NTO: | ||
4372 | p = ">"; | ||
4373 | goto redir; | ||
4374 | case NCLOBBER: | ||
4375 | p = ">|"; | ||
4376 | goto redir; | ||
4377 | case NAPPEND: | ||
4378 | p = ">>"; | ||
4379 | goto redir; | ||
4380 | case NTOFD: | ||
4381 | p = ">&"; | ||
4382 | goto redir; | ||
4383 | case NFROM: | ||
4384 | p = "<"; | ||
4385 | goto redir; | ||
4386 | case NFROMFD: | ||
4387 | p = "<&"; | ||
4388 | goto redir; | ||
4389 | case NFROMTO: | ||
4390 | p = "<>"; | ||
4391 | redir: | ||
4392 | s[0] = n->nfile.fd + '0'; | ||
4393 | s[1] = '\0'; | ||
4394 | cmdputs(s); | ||
4395 | cmdputs(p); | ||
4396 | if (n->type == NTOFD || n->type == NFROMFD) { | ||
4397 | s[0] = n->ndup.dupfd + '0'; | ||
4398 | p = s; | ||
4399 | goto dotail2; | ||
4400 | } | ||
4401 | n = n->nfile.fname; | ||
4402 | goto donode; | ||
4403 | } | ||
4404 | } | ||
4405 | |||
4406 | static char * | ||
4407 | commandtext(union node *n) | ||
4408 | { | ||
4409 | char *name; | ||
4410 | |||
4411 | STARTSTACKSTR(cmdnextc); | ||
4412 | cmdtxt(n); | ||
4413 | name = stackblock(); | ||
4414 | TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n", | ||
4415 | name, cmdnextc, cmdnextc)); | ||
4416 | return ckstrdup(name); | ||
4417 | } | ||
4418 | #endif /* JOBS */ | ||
4419 | |||
4420 | /* | ||
4421 | * Fork off a subshell. If we are doing job control, give the subshell its | ||
4422 | * own process group. Jp is a job structure that the job is to be added to. | ||
4423 | * N is the command that will be evaluated by the child. Both jp and n may | ||
4424 | * be NULL. The mode parameter can be one of the following: | ||
4425 | * FORK_FG - Fork off a foreground process. | ||
4426 | * FORK_BG - Fork off a background process. | ||
4427 | * FORK_NOJOB - Like FORK_FG, but don't give the process its own | ||
4428 | * process group even if job control is on. | ||
4429 | * | ||
4430 | * When job control is turned off, background processes have their standard | ||
4431 | * input redirected to /dev/null (except for the second and later processes | ||
4432 | * in a pipeline). | ||
4433 | * | ||
4434 | * Called with interrupts off. | ||
4435 | */ | ||
4436 | /* | ||
4437 | * Clear traps on a fork. | ||
4438 | */ | ||
4439 | static void | ||
4440 | clear_traps(void) | ||
4441 | { | ||
4442 | char **tp; | ||
4443 | |||
4444 | for (tp = trap; tp < &trap[NSIG]; tp++) { | ||
4445 | if (*tp && **tp) { /* trap not NULL or SIG_IGN */ | ||
4446 | INT_OFF; | ||
4447 | free(*tp); | ||
4448 | *tp = NULL; | ||
4449 | if (tp != &trap[0]) | ||
4450 | setsignal(tp - trap); | ||
4451 | INT_ON; | ||
4452 | } | ||
4453 | } | ||
4454 | } | ||
4455 | /* lives far away from here, needed for forkchild */ | ||
4456 | static void closescript(void); | ||
4457 | static void | ||
4458 | forkchild(struct job *jp, union node *n, int mode) | ||
4459 | { | ||
4460 | int oldlvl; | ||
4461 | |||
4462 | TRACE(("Child shell %d\n", getpid())); | ||
4463 | oldlvl = shlvl; | ||
4464 | shlvl++; | ||
4465 | |||
4466 | closescript(); | ||
4467 | clear_traps(); | ||
4468 | #if JOBS | ||
4469 | /* do job control only in root shell */ | ||
4470 | jobctl = 0; | ||
4471 | if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) { | ||
4472 | pid_t pgrp; | ||
4473 | |||
4474 | if (jp->nprocs == 0) | ||
4475 | pgrp = getpid(); | ||
4476 | else | ||
4477 | pgrp = jp->ps[0].pid; | ||
4478 | /* This can fail because we are doing it in the parent also */ | ||
4479 | (void)setpgid(0, pgrp); | ||
4480 | if (mode == FORK_FG) | ||
4481 | xtcsetpgrp(ttyfd, pgrp); | ||
4482 | setsignal(SIGTSTP); | ||
4483 | setsignal(SIGTTOU); | ||
4484 | } else | ||
4485 | #endif | ||
4486 | if (mode == FORK_BG) { | ||
4487 | ignoresig(SIGINT); | ||
4488 | ignoresig(SIGQUIT); | ||
4489 | if (jp->nprocs == 0) { | ||
4490 | close(0); | ||
4491 | if (open(bb_dev_null, O_RDONLY) != 0) | ||
4492 | ash_msg_and_raise_error("Can't open %s", bb_dev_null); | ||
4493 | } | ||
4494 | } | ||
4495 | if (!oldlvl && iflag) { | ||
4496 | setsignal(SIGINT); | ||
4497 | setsignal(SIGQUIT); | ||
4498 | setsignal(SIGTERM); | ||
4499 | } | ||
4500 | for (jp = curjob; jp; jp = jp->prev_job) | ||
4501 | freejob(jp); | ||
4502 | jobless = 0; | ||
4503 | } | ||
4504 | |||
4505 | static void | ||
4506 | forkparent(struct job *jp, union node *n, int mode, pid_t pid) | ||
4507 | { | ||
4508 | TRACE(("In parent shell: child = %d\n", pid)); | ||
4509 | if (!jp) { | ||
4510 | while (jobless && dowait(DOWAIT_NORMAL, 0) > 0); | ||
4511 | jobless++; | ||
4512 | return; | ||
4513 | } | ||
4514 | #if JOBS | ||
4515 | if (mode != FORK_NOJOB && jp->jobctl) { | ||
4516 | int pgrp; | ||
4517 | |||
4518 | if (jp->nprocs == 0) | ||
4519 | pgrp = pid; | ||
4520 | else | ||
4521 | pgrp = jp->ps[0].pid; | ||
4522 | /* This can fail because we are doing it in the child also */ | ||
4523 | setpgid(pid, pgrp); | ||
4524 | } | ||
4525 | #endif | ||
4526 | if (mode == FORK_BG) { | ||
4527 | backgndpid = pid; /* set $! */ | ||
4528 | set_curjob(jp, CUR_RUNNING); | ||
4529 | } | ||
4530 | if (jp) { | ||
4531 | struct procstat *ps = &jp->ps[jp->nprocs++]; | ||
4532 | ps->pid = pid; | ||
4533 | ps->status = -1; | ||
4534 | ps->cmd = nullstr; | ||
4535 | #if JOBS | ||
4536 | if (jobctl && n) | ||
4537 | ps->cmd = commandtext(n); | ||
4538 | #endif | ||
4539 | } | ||
4540 | } | ||
4541 | |||
4542 | static int | ||
4543 | forkshell(struct job *jp, union node *n, int mode) | ||
4544 | { | ||
4545 | int pid; | ||
4546 | |||
4547 | TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode)); | ||
4548 | pid = fork(); | ||
4549 | if (pid < 0) { | ||
4550 | TRACE(("Fork failed, errno=%d", errno)); | ||
4551 | if (jp) | ||
4552 | freejob(jp); | ||
4553 | ash_msg_and_raise_error("Cannot fork"); | ||
4554 | } | ||
4555 | if (pid == 0) | ||
4556 | forkchild(jp, n, mode); | ||
4557 | else | ||
4558 | forkparent(jp, n, mode, pid); | ||
4559 | return pid; | ||
4560 | } | ||
4561 | |||
4562 | /* | ||
4563 | * Wait for job to finish. | ||
4564 | * | ||
4565 | * Under job control we have the problem that while a child process is | ||
4566 | * running interrupts generated by the user are sent to the child but not | ||
4567 | * to the shell. This means that an infinite loop started by an inter- | ||
4568 | * active user may be hard to kill. With job control turned off, an | ||
4569 | * interactive user may place an interactive program inside a loop. If | ||
4570 | * the interactive program catches interrupts, the user doesn't want | ||
4571 | * these interrupts to also abort the loop. The approach we take here | ||
4572 | * is to have the shell ignore interrupt signals while waiting for a | ||
4573 | * foreground process to terminate, and then send itself an interrupt | ||
4574 | * signal if the child process was terminated by an interrupt signal. | ||
4575 | * Unfortunately, some programs want to do a bit of cleanup and then | ||
4576 | * exit on interrupt; unless these processes terminate themselves by | ||
4577 | * sending a signal to themselves (instead of calling exit) they will | ||
4578 | * confuse this approach. | ||
4579 | * | ||
4580 | * Called with interrupts off. | ||
4581 | */ | ||
4582 | static int | ||
4583 | waitforjob(struct job *jp) | ||
4584 | { | ||
4585 | int st; | ||
4586 | |||
4587 | TRACE(("waitforjob(%%%d) called\n", jobno(jp))); | ||
4588 | while (jp->state == JOBRUNNING) { | ||
4589 | dowait(DOWAIT_BLOCK, jp); | ||
4590 | } | ||
4591 | st = getstatus(jp); | ||
4592 | #if JOBS | ||
4593 | if (jp->jobctl) { | ||
4594 | xtcsetpgrp(ttyfd, rootpid); | ||
4595 | /* | ||
4596 | * This is truly gross. | ||
4597 | * If we're doing job control, then we did a TIOCSPGRP which | ||
4598 | * caused us (the shell) to no longer be in the controlling | ||
4599 | * session -- so we wouldn't have seen any ^C/SIGINT. So, we | ||
4600 | * intuit from the subprocess exit status whether a SIGINT | ||
4601 | * occurred, and if so interrupt ourselves. Yuck. - mycroft | ||
4602 | */ | ||
4603 | if (jp->sigint) | ||
4604 | raise(SIGINT); | ||
4605 | } | ||
4606 | if (jp->state == JOBDONE) | ||
4607 | #endif | ||
4608 | freejob(jp); | ||
4609 | return st; | ||
4610 | } | ||
4611 | |||
4612 | /* | ||
4613 | * return 1 if there are stopped jobs, otherwise 0 | ||
4614 | */ | ||
4615 | static int | ||
4616 | stoppedjobs(void) | ||
4617 | { | ||
4618 | struct job *jp; | ||
4619 | int retval; | ||
4620 | |||
4621 | retval = 0; | ||
4622 | if (job_warning) | ||
4623 | goto out; | ||
4624 | jp = curjob; | ||
4625 | if (jp && jp->state == JOBSTOPPED) { | ||
4626 | out2str("You have stopped jobs.\n"); | ||
4627 | job_warning = 2; | ||
4628 | retval++; | ||
4629 | } | ||
4630 | out: | ||
4631 | return retval; | ||
4632 | } | ||
4633 | |||
4634 | |||
3162 | /* ============ Routines to expand arguments to commands | 4635 | /* ============ Routines to expand arguments to commands |
3163 | * | 4636 | * |
3164 | * We have to deal with backquotes, shell variables, and file metacharacters. | 4637 | * We have to deal with backquotes, shell variables, and file metacharacters. |
@@ -7333,1470 +8806,6 @@ setinputstring(char *string) | |||
7333 | } | 8806 | } |
7334 | 8807 | ||
7335 | 8808 | ||
7336 | /* ============ jobs.c */ | ||
7337 | |||
7338 | /* | ||
7339 | * Set the signal handler for the specified signal. The routine figures | ||
7340 | * out what it should be set to. | ||
7341 | */ | ||
7342 | static void | ||
7343 | setsignal(int signo) | ||
7344 | { | ||
7345 | int action; | ||
7346 | char *t, tsig; | ||
7347 | struct sigaction act; | ||
7348 | |||
7349 | t = trap[signo]; | ||
7350 | if (t == NULL) | ||
7351 | action = S_DFL; | ||
7352 | else if (*t != '\0') | ||
7353 | action = S_CATCH; | ||
7354 | else | ||
7355 | action = S_IGN; | ||
7356 | if (rootshell && action == S_DFL) { | ||
7357 | switch (signo) { | ||
7358 | case SIGINT: | ||
7359 | if (iflag || minusc || sflag == 0) | ||
7360 | action = S_CATCH; | ||
7361 | break; | ||
7362 | case SIGQUIT: | ||
7363 | #if DEBUG | ||
7364 | if (debug) | ||
7365 | break; | ||
7366 | #endif | ||
7367 | /* FALLTHROUGH */ | ||
7368 | case SIGTERM: | ||
7369 | if (iflag) | ||
7370 | action = S_IGN; | ||
7371 | break; | ||
7372 | #if JOBS | ||
7373 | case SIGTSTP: | ||
7374 | case SIGTTOU: | ||
7375 | if (mflag) | ||
7376 | action = S_IGN; | ||
7377 | break; | ||
7378 | #endif | ||
7379 | } | ||
7380 | } | ||
7381 | |||
7382 | t = &sigmode[signo - 1]; | ||
7383 | tsig = *t; | ||
7384 | if (tsig == 0) { | ||
7385 | /* | ||
7386 | * current setting unknown | ||
7387 | */ | ||
7388 | if (sigaction(signo, 0, &act) == -1) { | ||
7389 | /* | ||
7390 | * Pretend it worked; maybe we should give a warning | ||
7391 | * here, but other shells don't. We don't alter | ||
7392 | * sigmode, so that we retry every time. | ||
7393 | */ | ||
7394 | return; | ||
7395 | } | ||
7396 | if (act.sa_handler == SIG_IGN) { | ||
7397 | if (mflag | ||
7398 | && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU) | ||
7399 | ) { | ||
7400 | tsig = S_IGN; /* don't hard ignore these */ | ||
7401 | } else | ||
7402 | tsig = S_HARD_IGN; | ||
7403 | } else { | ||
7404 | tsig = S_RESET; /* force to be set */ | ||
7405 | } | ||
7406 | } | ||
7407 | if (tsig == S_HARD_IGN || tsig == action) | ||
7408 | return; | ||
7409 | switch (action) { | ||
7410 | case S_CATCH: | ||
7411 | act.sa_handler = onsig; | ||
7412 | break; | ||
7413 | case S_IGN: | ||
7414 | act.sa_handler = SIG_IGN; | ||
7415 | break; | ||
7416 | default: | ||
7417 | act.sa_handler = SIG_DFL; | ||
7418 | } | ||
7419 | *t = action; | ||
7420 | act.sa_flags = 0; | ||
7421 | sigfillset(&act.sa_mask); | ||
7422 | sigaction(signo, &act, 0); | ||
7423 | } | ||
7424 | |||
7425 | /* mode flags for set_curjob */ | ||
7426 | #define CUR_DELETE 2 | ||
7427 | #define CUR_RUNNING 1 | ||
7428 | #define CUR_STOPPED 0 | ||
7429 | |||
7430 | /* mode flags for dowait */ | ||
7431 | #define DOWAIT_NORMAL 0 | ||
7432 | #define DOWAIT_BLOCK 1 | ||
7433 | |||
7434 | #if JOBS | ||
7435 | /* pgrp of shell on invocation */ | ||
7436 | static int initialpgrp; | ||
7437 | static int ttyfd = -1; | ||
7438 | #endif | ||
7439 | /* array of jobs */ | ||
7440 | static struct job *jobtab; | ||
7441 | /* size of array */ | ||
7442 | static unsigned njobs; | ||
7443 | /* current job */ | ||
7444 | static struct job *curjob; | ||
7445 | /* number of presumed living untracked jobs */ | ||
7446 | static int jobless; | ||
7447 | |||
7448 | static void | ||
7449 | set_curjob(struct job *jp, unsigned mode) | ||
7450 | { | ||
7451 | struct job *jp1; | ||
7452 | struct job **jpp, **curp; | ||
7453 | |||
7454 | /* first remove from list */ | ||
7455 | jpp = curp = &curjob; | ||
7456 | do { | ||
7457 | jp1 = *jpp; | ||
7458 | if (jp1 == jp) | ||
7459 | break; | ||
7460 | jpp = &jp1->prev_job; | ||
7461 | } while (1); | ||
7462 | *jpp = jp1->prev_job; | ||
7463 | |||
7464 | /* Then re-insert in correct position */ | ||
7465 | jpp = curp; | ||
7466 | switch (mode) { | ||
7467 | default: | ||
7468 | #if DEBUG | ||
7469 | abort(); | ||
7470 | #endif | ||
7471 | case CUR_DELETE: | ||
7472 | /* job being deleted */ | ||
7473 | break; | ||
7474 | case CUR_RUNNING: | ||
7475 | /* newly created job or backgrounded job, | ||
7476 | put after all stopped jobs. */ | ||
7477 | do { | ||
7478 | jp1 = *jpp; | ||
7479 | #if JOBS | ||
7480 | if (!jp1 || jp1->state != JOBSTOPPED) | ||
7481 | #endif | ||
7482 | break; | ||
7483 | jpp = &jp1->prev_job; | ||
7484 | } while (1); | ||
7485 | /* FALLTHROUGH */ | ||
7486 | #if JOBS | ||
7487 | case CUR_STOPPED: | ||
7488 | #endif | ||
7489 | /* newly stopped job - becomes curjob */ | ||
7490 | jp->prev_job = *jpp; | ||
7491 | *jpp = jp; | ||
7492 | break; | ||
7493 | } | ||
7494 | } | ||
7495 | |||
7496 | #if JOBS || DEBUG | ||
7497 | static int | ||
7498 | jobno(const struct job *jp) | ||
7499 | { | ||
7500 | return jp - jobtab + 1; | ||
7501 | } | ||
7502 | #endif | ||
7503 | |||
7504 | /* | ||
7505 | * Convert a job name to a job structure. | ||
7506 | */ | ||
7507 | static struct job * | ||
7508 | getjob(const char *name, int getctl) | ||
7509 | { | ||
7510 | struct job *jp; | ||
7511 | struct job *found; | ||
7512 | const char *err_msg = "No such job: %s"; | ||
7513 | unsigned num; | ||
7514 | int c; | ||
7515 | const char *p; | ||
7516 | char *(*match)(const char *, const char *); | ||
7517 | |||
7518 | jp = curjob; | ||
7519 | p = name; | ||
7520 | if (!p) | ||
7521 | goto currentjob; | ||
7522 | |||
7523 | if (*p != '%') | ||
7524 | goto err; | ||
7525 | |||
7526 | c = *++p; | ||
7527 | if (!c) | ||
7528 | goto currentjob; | ||
7529 | |||
7530 | if (!p[1]) { | ||
7531 | if (c == '+' || c == '%') { | ||
7532 | currentjob: | ||
7533 | err_msg = "No current job"; | ||
7534 | goto check; | ||
7535 | } | ||
7536 | if (c == '-') { | ||
7537 | if (jp) | ||
7538 | jp = jp->prev_job; | ||
7539 | err_msg = "No previous job"; | ||
7540 | check: | ||
7541 | if (!jp) | ||
7542 | goto err; | ||
7543 | goto gotit; | ||
7544 | } | ||
7545 | } | ||
7546 | |||
7547 | if (is_number(p)) { | ||
7548 | num = atoi(p); | ||
7549 | if (num < njobs) { | ||
7550 | jp = jobtab + num - 1; | ||
7551 | if (jp->used) | ||
7552 | goto gotit; | ||
7553 | goto err; | ||
7554 | } | ||
7555 | } | ||
7556 | |||
7557 | match = prefix; | ||
7558 | if (*p == '?') { | ||
7559 | match = strstr; | ||
7560 | p++; | ||
7561 | } | ||
7562 | |||
7563 | found = 0; | ||
7564 | while (1) { | ||
7565 | if (!jp) | ||
7566 | goto err; | ||
7567 | if (match(jp->ps[0].cmd, p)) { | ||
7568 | if (found) | ||
7569 | goto err; | ||
7570 | found = jp; | ||
7571 | err_msg = "%s: ambiguous"; | ||
7572 | } | ||
7573 | jp = jp->prev_job; | ||
7574 | } | ||
7575 | |||
7576 | gotit: | ||
7577 | #if JOBS | ||
7578 | err_msg = "job %s not created under job control"; | ||
7579 | if (getctl && jp->jobctl == 0) | ||
7580 | goto err; | ||
7581 | #endif | ||
7582 | return jp; | ||
7583 | err: | ||
7584 | ash_msg_and_raise_error(err_msg, name); | ||
7585 | } | ||
7586 | |||
7587 | /* | ||
7588 | * Mark a job structure as unused. | ||
7589 | */ | ||
7590 | static void | ||
7591 | freejob(struct job *jp) | ||
7592 | { | ||
7593 | struct procstat *ps; | ||
7594 | int i; | ||
7595 | |||
7596 | INT_OFF; | ||
7597 | for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) { | ||
7598 | if (ps->cmd != nullstr) | ||
7599 | free(ps->cmd); | ||
7600 | } | ||
7601 | if (jp->ps != &jp->ps0) | ||
7602 | free(jp->ps); | ||
7603 | jp->used = 0; | ||
7604 | set_curjob(jp, CUR_DELETE); | ||
7605 | INT_ON; | ||
7606 | } | ||
7607 | |||
7608 | #if JOBS | ||
7609 | static void | ||
7610 | xtcsetpgrp(int fd, pid_t pgrp) | ||
7611 | { | ||
7612 | if (tcsetpgrp(fd, pgrp)) | ||
7613 | ash_msg_and_raise_error("Cannot set tty process group (%m)"); | ||
7614 | } | ||
7615 | |||
7616 | /* | ||
7617 | * Turn job control on and off. | ||
7618 | * | ||
7619 | * Note: This code assumes that the third arg to ioctl is a character | ||
7620 | * pointer, which is true on Berkeley systems but not System V. Since | ||
7621 | * System V doesn't have job control yet, this isn't a problem now. | ||
7622 | * | ||
7623 | * Called with interrupts off. | ||
7624 | */ | ||
7625 | static void | ||
7626 | setjobctl(int on) | ||
7627 | { | ||
7628 | int fd; | ||
7629 | int pgrp; | ||
7630 | |||
7631 | if (on == jobctl || rootshell == 0) | ||
7632 | return; | ||
7633 | if (on) { | ||
7634 | int ofd; | ||
7635 | ofd = fd = open(_PATH_TTY, O_RDWR); | ||
7636 | if (fd < 0) { | ||
7637 | /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails. | ||
7638 | * That sometimes helps to acquire controlling tty. | ||
7639 | * Obviously, a workaround for bugs when someone | ||
7640 | * failed to provide a controlling tty to bash! :) */ | ||
7641 | fd += 3; | ||
7642 | while (!isatty(fd) && --fd >= 0) | ||
7643 | ; | ||
7644 | } | ||
7645 | fd = fcntl(fd, F_DUPFD, 10); | ||
7646 | close(ofd); | ||
7647 | if (fd < 0) | ||
7648 | goto out; | ||
7649 | fcntl(fd, F_SETFD, FD_CLOEXEC); | ||
7650 | do { /* while we are in the background */ | ||
7651 | pgrp = tcgetpgrp(fd); | ||
7652 | if (pgrp < 0) { | ||
7653 | out: | ||
7654 | ash_msg("can't access tty; job control turned off"); | ||
7655 | mflag = on = 0; | ||
7656 | goto close; | ||
7657 | } | ||
7658 | if (pgrp == getpgrp()) | ||
7659 | break; | ||
7660 | killpg(0, SIGTTIN); | ||
7661 | } while (1); | ||
7662 | initialpgrp = pgrp; | ||
7663 | |||
7664 | setsignal(SIGTSTP); | ||
7665 | setsignal(SIGTTOU); | ||
7666 | setsignal(SIGTTIN); | ||
7667 | pgrp = rootpid; | ||
7668 | setpgid(0, pgrp); | ||
7669 | xtcsetpgrp(fd, pgrp); | ||
7670 | } else { | ||
7671 | /* turning job control off */ | ||
7672 | fd = ttyfd; | ||
7673 | pgrp = initialpgrp; | ||
7674 | xtcsetpgrp(fd, pgrp); | ||
7675 | setpgid(0, pgrp); | ||
7676 | setsignal(SIGTSTP); | ||
7677 | setsignal(SIGTTOU); | ||
7678 | setsignal(SIGTTIN); | ||
7679 | close: | ||
7680 | close(fd); | ||
7681 | fd = -1; | ||
7682 | } | ||
7683 | ttyfd = fd; | ||
7684 | jobctl = on; | ||
7685 | } | ||
7686 | |||
7687 | static int | ||
7688 | killcmd(int argc, char **argv) | ||
7689 | { | ||
7690 | int signo = -1; | ||
7691 | int list = 0; | ||
7692 | int i; | ||
7693 | pid_t pid; | ||
7694 | struct job *jp; | ||
7695 | |||
7696 | if (argc <= 1) { | ||
7697 | usage: | ||
7698 | ash_msg_and_raise_error( | ||
7699 | "Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n" | ||
7700 | "kill -l [exitstatus]" | ||
7701 | ); | ||
7702 | } | ||
7703 | |||
7704 | if (**++argv == '-') { | ||
7705 | signo = get_signum(*argv + 1); | ||
7706 | if (signo < 0) { | ||
7707 | int c; | ||
7708 | |||
7709 | while ((c = nextopt("ls:")) != '\0') { | ||
7710 | switch (c) { | ||
7711 | default: | ||
7712 | #if DEBUG | ||
7713 | abort(); | ||
7714 | #endif | ||
7715 | case 'l': | ||
7716 | list = 1; | ||
7717 | break; | ||
7718 | case 's': | ||
7719 | signo = get_signum(optionarg); | ||
7720 | if (signo < 0) { | ||
7721 | ash_msg_and_raise_error( | ||
7722 | "invalid signal number or name: %s", | ||
7723 | optionarg | ||
7724 | ); | ||
7725 | } | ||
7726 | break; | ||
7727 | } | ||
7728 | } | ||
7729 | argv = argptr; | ||
7730 | } else | ||
7731 | argv++; | ||
7732 | } | ||
7733 | |||
7734 | if (!list && signo < 0) | ||
7735 | signo = SIGTERM; | ||
7736 | |||
7737 | if ((signo < 0 || !*argv) ^ list) { | ||
7738 | goto usage; | ||
7739 | } | ||
7740 | |||
7741 | if (list) { | ||
7742 | const char *name; | ||
7743 | |||
7744 | if (!*argv) { | ||
7745 | for (i = 1; i < NSIG; i++) { | ||
7746 | name = get_signame(i); | ||
7747 | if (isdigit(*name)) | ||
7748 | out1fmt(snlfmt, name); | ||
7749 | } | ||
7750 | return 0; | ||
7751 | } | ||
7752 | name = get_signame(signo); | ||
7753 | if (!isdigit(*name)) | ||
7754 | ash_msg_and_raise_error("invalid signal number or exit status: %s", *argptr); | ||
7755 | out1fmt(snlfmt, name); | ||
7756 | return 0; | ||
7757 | } | ||
7758 | |||
7759 | i = 0; | ||
7760 | do { | ||
7761 | if (**argv == '%') { | ||
7762 | jp = getjob(*argv, 0); | ||
7763 | pid = -jp->ps[0].pid; | ||
7764 | } else { | ||
7765 | pid = **argv == '-' ? | ||
7766 | -number(*argv + 1) : number(*argv); | ||
7767 | } | ||
7768 | if (kill(pid, signo) != 0) { | ||
7769 | ash_msg("(%d) - %m", pid); | ||
7770 | i = 1; | ||
7771 | } | ||
7772 | } while (*++argv); | ||
7773 | |||
7774 | return i; | ||
7775 | } | ||
7776 | |||
7777 | static void | ||
7778 | showpipe(struct job *jp, FILE *out) | ||
7779 | { | ||
7780 | struct procstat *sp; | ||
7781 | struct procstat *spend; | ||
7782 | |||
7783 | spend = jp->ps + jp->nprocs; | ||
7784 | for (sp = jp->ps + 1; sp < spend; sp++) | ||
7785 | fprintf(out, " | %s", sp->cmd); | ||
7786 | outcslow('\n', out); | ||
7787 | flush_stdout_stderr(); | ||
7788 | } | ||
7789 | |||
7790 | |||
7791 | static int | ||
7792 | restartjob(struct job *jp, int mode) | ||
7793 | { | ||
7794 | struct procstat *ps; | ||
7795 | int i; | ||
7796 | int status; | ||
7797 | pid_t pgid; | ||
7798 | |||
7799 | INT_OFF; | ||
7800 | if (jp->state == JOBDONE) | ||
7801 | goto out; | ||
7802 | jp->state = JOBRUNNING; | ||
7803 | pgid = jp->ps->pid; | ||
7804 | if (mode == FORK_FG) | ||
7805 | xtcsetpgrp(ttyfd, pgid); | ||
7806 | killpg(pgid, SIGCONT); | ||
7807 | ps = jp->ps; | ||
7808 | i = jp->nprocs; | ||
7809 | do { | ||
7810 | if (WIFSTOPPED(ps->status)) { | ||
7811 | ps->status = -1; | ||
7812 | } | ||
7813 | } while (ps++, --i); | ||
7814 | out: | ||
7815 | status = (mode == FORK_FG) ? waitforjob(jp) : 0; | ||
7816 | INT_ON; | ||
7817 | return status; | ||
7818 | } | ||
7819 | |||
7820 | static int | ||
7821 | fg_bgcmd(int argc, char **argv) | ||
7822 | { | ||
7823 | struct job *jp; | ||
7824 | FILE *out; | ||
7825 | int mode; | ||
7826 | int retval; | ||
7827 | |||
7828 | mode = (**argv == 'f') ? FORK_FG : FORK_BG; | ||
7829 | nextopt(nullstr); | ||
7830 | argv = argptr; | ||
7831 | out = stdout; | ||
7832 | do { | ||
7833 | jp = getjob(*argv, 1); | ||
7834 | if (mode == FORK_BG) { | ||
7835 | set_curjob(jp, CUR_RUNNING); | ||
7836 | fprintf(out, "[%d] ", jobno(jp)); | ||
7837 | } | ||
7838 | outstr(jp->ps->cmd, out); | ||
7839 | showpipe(jp, out); | ||
7840 | retval = restartjob(jp, mode); | ||
7841 | } while (*argv && *++argv); | ||
7842 | return retval; | ||
7843 | } | ||
7844 | #endif | ||
7845 | |||
7846 | static int | ||
7847 | sprint_status(char *s, int status, int sigonly) | ||
7848 | { | ||
7849 | int col; | ||
7850 | int st; | ||
7851 | |||
7852 | col = 0; | ||
7853 | if (!WIFEXITED(status)) { | ||
7854 | #if JOBS | ||
7855 | if (WIFSTOPPED(status)) | ||
7856 | st = WSTOPSIG(status); | ||
7857 | else | ||
7858 | #endif | ||
7859 | st = WTERMSIG(status); | ||
7860 | if (sigonly) { | ||
7861 | if (st == SIGINT || st == SIGPIPE) | ||
7862 | goto out; | ||
7863 | #if JOBS | ||
7864 | if (WIFSTOPPED(status)) | ||
7865 | goto out; | ||
7866 | #endif | ||
7867 | } | ||
7868 | st &= 0x7f; | ||
7869 | col = fmtstr(s, 32, strsignal(st)); | ||
7870 | if (WCOREDUMP(status)) { | ||
7871 | col += fmtstr(s + col, 16, " (core dumped)"); | ||
7872 | } | ||
7873 | } else if (!sigonly) { | ||
7874 | st = WEXITSTATUS(status); | ||
7875 | if (st) | ||
7876 | col = fmtstr(s, 16, "Done(%d)", st); | ||
7877 | else | ||
7878 | col = fmtstr(s, 16, "Done"); | ||
7879 | } | ||
7880 | out: | ||
7881 | return col; | ||
7882 | } | ||
7883 | |||
7884 | /* | ||
7885 | * Do a wait system call. If job control is compiled in, we accept | ||
7886 | * stopped processes. If block is zero, we return a value of zero | ||
7887 | * rather than blocking. | ||
7888 | * | ||
7889 | * System V doesn't have a non-blocking wait system call. It does | ||
7890 | * have a SIGCLD signal that is sent to a process when one of it's | ||
7891 | * children dies. The obvious way to use SIGCLD would be to install | ||
7892 | * a handler for SIGCLD which simply bumped a counter when a SIGCLD | ||
7893 | * was received, and have waitproc bump another counter when it got | ||
7894 | * the status of a process. Waitproc would then know that a wait | ||
7895 | * system call would not block if the two counters were different. | ||
7896 | * This approach doesn't work because if a process has children that | ||
7897 | * have not been waited for, System V will send it a SIGCLD when it | ||
7898 | * installs a signal handler for SIGCLD. What this means is that when | ||
7899 | * a child exits, the shell will be sent SIGCLD signals continuously | ||
7900 | * until is runs out of stack space, unless it does a wait call before | ||
7901 | * restoring the signal handler. The code below takes advantage of | ||
7902 | * this (mis)feature by installing a signal handler for SIGCLD and | ||
7903 | * then checking to see whether it was called. If there are any | ||
7904 | * children to be waited for, it will be. | ||
7905 | * | ||
7906 | * If neither SYSV nor BSD is defined, we don't implement nonblocking | ||
7907 | * waits at all. In this case, the user will not be informed when | ||
7908 | * a background process until the next time she runs a real program | ||
7909 | * (as opposed to running a builtin command or just typing return), | ||
7910 | * and the jobs command may give out of date information. | ||
7911 | */ | ||
7912 | static int | ||
7913 | waitproc(int block, int *status) | ||
7914 | { | ||
7915 | int flags = 0; | ||
7916 | |||
7917 | #if JOBS | ||
7918 | if (jobctl) | ||
7919 | flags |= WUNTRACED; | ||
7920 | #endif | ||
7921 | if (block == 0) | ||
7922 | flags |= WNOHANG; | ||
7923 | return wait3(status, flags, (struct rusage *)NULL); | ||
7924 | } | ||
7925 | |||
7926 | /* | ||
7927 | * Wait for a process to terminate. | ||
7928 | */ | ||
7929 | static int | ||
7930 | dowait(int block, struct job *job) | ||
7931 | { | ||
7932 | int pid; | ||
7933 | int status; | ||
7934 | struct job *jp; | ||
7935 | struct job *thisjob; | ||
7936 | int state; | ||
7937 | |||
7938 | TRACE(("dowait(%d) called\n", block)); | ||
7939 | pid = waitproc(block, &status); | ||
7940 | TRACE(("wait returns pid %d, status=%d\n", pid, status)); | ||
7941 | if (pid <= 0) | ||
7942 | return pid; | ||
7943 | INT_OFF; | ||
7944 | thisjob = NULL; | ||
7945 | for (jp = curjob; jp; jp = jp->prev_job) { | ||
7946 | struct procstat *sp; | ||
7947 | struct procstat *spend; | ||
7948 | if (jp->state == JOBDONE) | ||
7949 | continue; | ||
7950 | state = JOBDONE; | ||
7951 | spend = jp->ps + jp->nprocs; | ||
7952 | sp = jp->ps; | ||
7953 | do { | ||
7954 | if (sp->pid == pid) { | ||
7955 | TRACE(("Job %d: changing status of proc %d " | ||
7956 | "from 0x%x to 0x%x\n", | ||
7957 | jobno(jp), pid, sp->status, status)); | ||
7958 | sp->status = status; | ||
7959 | thisjob = jp; | ||
7960 | } | ||
7961 | if (sp->status == -1) | ||
7962 | state = JOBRUNNING; | ||
7963 | #if JOBS | ||
7964 | if (state == JOBRUNNING) | ||
7965 | continue; | ||
7966 | if (WIFSTOPPED(sp->status)) { | ||
7967 | jp->stopstatus = sp->status; | ||
7968 | state = JOBSTOPPED; | ||
7969 | } | ||
7970 | #endif | ||
7971 | } while (++sp < spend); | ||
7972 | if (thisjob) | ||
7973 | goto gotjob; | ||
7974 | } | ||
7975 | #if JOBS | ||
7976 | if (!WIFSTOPPED(status)) | ||
7977 | #endif | ||
7978 | |||
7979 | jobless--; | ||
7980 | goto out; | ||
7981 | |||
7982 | gotjob: | ||
7983 | if (state != JOBRUNNING) { | ||
7984 | thisjob->changed = 1; | ||
7985 | |||
7986 | if (thisjob->state != state) { | ||
7987 | TRACE(("Job %d: changing state from %d to %d\n", | ||
7988 | jobno(thisjob), thisjob->state, state)); | ||
7989 | thisjob->state = state; | ||
7990 | #if JOBS | ||
7991 | if (state == JOBSTOPPED) { | ||
7992 | set_curjob(thisjob, CUR_STOPPED); | ||
7993 | } | ||
7994 | #endif | ||
7995 | } | ||
7996 | } | ||
7997 | |||
7998 | out: | ||
7999 | INT_ON; | ||
8000 | |||
8001 | if (thisjob && thisjob == job) { | ||
8002 | char s[48 + 1]; | ||
8003 | int len; | ||
8004 | |||
8005 | len = sprint_status(s, status, 1); | ||
8006 | if (len) { | ||
8007 | s[len] = '\n'; | ||
8008 | s[len + 1] = 0; | ||
8009 | out2str(s); | ||
8010 | } | ||
8011 | } | ||
8012 | return pid; | ||
8013 | } | ||
8014 | |||
8015 | #if JOBS | ||
8016 | static void | ||
8017 | showjob(FILE *out, struct job *jp, int mode) | ||
8018 | { | ||
8019 | struct procstat *ps; | ||
8020 | struct procstat *psend; | ||
8021 | int col; | ||
8022 | int indent; | ||
8023 | char s[80]; | ||
8024 | |||
8025 | ps = jp->ps; | ||
8026 | |||
8027 | if (mode & SHOW_PGID) { | ||
8028 | /* just output process (group) id of pipeline */ | ||
8029 | fprintf(out, "%d\n", ps->pid); | ||
8030 | return; | ||
8031 | } | ||
8032 | |||
8033 | col = fmtstr(s, 16, "[%d] ", jobno(jp)); | ||
8034 | indent = col; | ||
8035 | |||
8036 | if (jp == curjob) | ||
8037 | s[col - 2] = '+'; | ||
8038 | else if (curjob && jp == curjob->prev_job) | ||
8039 | s[col - 2] = '-'; | ||
8040 | |||
8041 | if (mode & SHOW_PID) | ||
8042 | col += fmtstr(s + col, 16, "%d ", ps->pid); | ||
8043 | |||
8044 | psend = ps + jp->nprocs; | ||
8045 | |||
8046 | if (jp->state == JOBRUNNING) { | ||
8047 | strcpy(s + col, "Running"); | ||
8048 | col += sizeof("Running") - 1; | ||
8049 | } else { | ||
8050 | int status = psend[-1].status; | ||
8051 | if (jp->state == JOBSTOPPED) | ||
8052 | status = jp->stopstatus; | ||
8053 | col += sprint_status(s + col, status, 0); | ||
8054 | } | ||
8055 | |||
8056 | goto start; | ||
8057 | |||
8058 | do { | ||
8059 | /* for each process */ | ||
8060 | col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3; | ||
8061 | start: | ||
8062 | fprintf(out, "%s%*c%s", | ||
8063 | s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd | ||
8064 | ); | ||
8065 | if (!(mode & SHOW_PID)) { | ||
8066 | showpipe(jp, out); | ||
8067 | break; | ||
8068 | } | ||
8069 | if (++ps == psend) { | ||
8070 | outcslow('\n', out); | ||
8071 | break; | ||
8072 | } | ||
8073 | } while (1); | ||
8074 | |||
8075 | jp->changed = 0; | ||
8076 | |||
8077 | if (jp->state == JOBDONE) { | ||
8078 | TRACE(("showjob: freeing job %d\n", jobno(jp))); | ||
8079 | freejob(jp); | ||
8080 | } | ||
8081 | } | ||
8082 | |||
8083 | static int | ||
8084 | jobscmd(int argc, char **argv) | ||
8085 | { | ||
8086 | int mode, m; | ||
8087 | FILE *out; | ||
8088 | |||
8089 | mode = 0; | ||
8090 | while ((m = nextopt("lp"))) { | ||
8091 | if (m == 'l') | ||
8092 | mode = SHOW_PID; | ||
8093 | else | ||
8094 | mode = SHOW_PGID; | ||
8095 | } | ||
8096 | |||
8097 | out = stdout; | ||
8098 | argv = argptr; | ||
8099 | if (*argv) { | ||
8100 | do | ||
8101 | showjob(out, getjob(*argv,0), mode); | ||
8102 | while (*++argv); | ||
8103 | } else | ||
8104 | showjobs(out, mode); | ||
8105 | |||
8106 | return 0; | ||
8107 | } | ||
8108 | |||
8109 | /* | ||
8110 | * Print a list of jobs. If "change" is nonzero, only print jobs whose | ||
8111 | * statuses have changed since the last call to showjobs. | ||
8112 | */ | ||
8113 | static void | ||
8114 | showjobs(FILE *out, int mode) | ||
8115 | { | ||
8116 | struct job *jp; | ||
8117 | |||
8118 | TRACE(("showjobs(%x) called\n", mode)); | ||
8119 | |||
8120 | /* If not even one one job changed, there is nothing to do */ | ||
8121 | while (dowait(DOWAIT_NORMAL, NULL) > 0) | ||
8122 | continue; | ||
8123 | |||
8124 | for (jp = curjob; jp; jp = jp->prev_job) { | ||
8125 | if (!(mode & SHOW_CHANGED) || jp->changed) | ||
8126 | showjob(out, jp, mode); | ||
8127 | } | ||
8128 | } | ||
8129 | #endif /* JOBS */ | ||
8130 | |||
8131 | static int | ||
8132 | getstatus(struct job *job) | ||
8133 | { | ||
8134 | int status; | ||
8135 | int retval; | ||
8136 | |||
8137 | status = job->ps[job->nprocs - 1].status; | ||
8138 | retval = WEXITSTATUS(status); | ||
8139 | if (!WIFEXITED(status)) { | ||
8140 | #if JOBS | ||
8141 | retval = WSTOPSIG(status); | ||
8142 | if (!WIFSTOPPED(status)) | ||
8143 | #endif | ||
8144 | { | ||
8145 | /* XXX: limits number of signals */ | ||
8146 | retval = WTERMSIG(status); | ||
8147 | #if JOBS | ||
8148 | if (retval == SIGINT) | ||
8149 | job->sigint = 1; | ||
8150 | #endif | ||
8151 | } | ||
8152 | retval += 128; | ||
8153 | } | ||
8154 | TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n", | ||
8155 | jobno(job), job->nprocs, status, retval)); | ||
8156 | return retval; | ||
8157 | } | ||
8158 | |||
8159 | static int | ||
8160 | waitcmd(int argc, char **argv) | ||
8161 | { | ||
8162 | struct job *job; | ||
8163 | int retval; | ||
8164 | struct job *jp; | ||
8165 | |||
8166 | EXSIGON; | ||
8167 | |||
8168 | nextopt(nullstr); | ||
8169 | retval = 0; | ||
8170 | |||
8171 | argv = argptr; | ||
8172 | if (!*argv) { | ||
8173 | /* wait for all jobs */ | ||
8174 | for (;;) { | ||
8175 | jp = curjob; | ||
8176 | while (1) { | ||
8177 | if (!jp) { | ||
8178 | /* no running procs */ | ||
8179 | goto out; | ||
8180 | } | ||
8181 | if (jp->state == JOBRUNNING) | ||
8182 | break; | ||
8183 | jp->waited = 1; | ||
8184 | jp = jp->prev_job; | ||
8185 | } | ||
8186 | dowait(DOWAIT_BLOCK, 0); | ||
8187 | } | ||
8188 | } | ||
8189 | |||
8190 | retval = 127; | ||
8191 | do { | ||
8192 | if (**argv != '%') { | ||
8193 | pid_t pid = number(*argv); | ||
8194 | job = curjob; | ||
8195 | goto start; | ||
8196 | do { | ||
8197 | if (job->ps[job->nprocs - 1].pid == pid) | ||
8198 | break; | ||
8199 | job = job->prev_job; | ||
8200 | start: | ||
8201 | if (!job) | ||
8202 | goto repeat; | ||
8203 | } while (1); | ||
8204 | } else | ||
8205 | job = getjob(*argv, 0); | ||
8206 | /* loop until process terminated or stopped */ | ||
8207 | while (job->state == JOBRUNNING) | ||
8208 | dowait(DOWAIT_BLOCK, 0); | ||
8209 | job->waited = 1; | ||
8210 | retval = getstatus(job); | ||
8211 | repeat: | ||
8212 | ; | ||
8213 | } while (*++argv); | ||
8214 | |||
8215 | out: | ||
8216 | return retval; | ||
8217 | } | ||
8218 | |||
8219 | static struct job * | ||
8220 | growjobtab(void) | ||
8221 | { | ||
8222 | size_t len; | ||
8223 | ptrdiff_t offset; | ||
8224 | struct job *jp, *jq; | ||
8225 | |||
8226 | len = njobs * sizeof(*jp); | ||
8227 | jq = jobtab; | ||
8228 | jp = ckrealloc(jq, len + 4 * sizeof(*jp)); | ||
8229 | |||
8230 | offset = (char *)jp - (char *)jq; | ||
8231 | if (offset) { | ||
8232 | /* Relocate pointers */ | ||
8233 | size_t l = len; | ||
8234 | |||
8235 | jq = (struct job *)((char *)jq + l); | ||
8236 | while (l) { | ||
8237 | l -= sizeof(*jp); | ||
8238 | jq--; | ||
8239 | #define joff(p) ((struct job *)((char *)(p) + l)) | ||
8240 | #define jmove(p) (p) = (void *)((char *)(p) + offset) | ||
8241 | if (joff(jp)->ps == &jq->ps0) | ||
8242 | jmove(joff(jp)->ps); | ||
8243 | if (joff(jp)->prev_job) | ||
8244 | jmove(joff(jp)->prev_job); | ||
8245 | } | ||
8246 | if (curjob) | ||
8247 | jmove(curjob); | ||
8248 | #undef joff | ||
8249 | #undef jmove | ||
8250 | } | ||
8251 | |||
8252 | njobs += 4; | ||
8253 | jobtab = jp; | ||
8254 | jp = (struct job *)((char *)jp + len); | ||
8255 | jq = jp + 3; | ||
8256 | do { | ||
8257 | jq->used = 0; | ||
8258 | } while (--jq >= jp); | ||
8259 | return jp; | ||
8260 | } | ||
8261 | |||
8262 | /* | ||
8263 | * Return a new job structure. | ||
8264 | * Called with interrupts off. | ||
8265 | */ | ||
8266 | static struct job * | ||
8267 | makejob(union node *node, int nprocs) | ||
8268 | { | ||
8269 | int i; | ||
8270 | struct job *jp; | ||
8271 | |||
8272 | for (i = njobs, jp = jobtab; ; jp++) { | ||
8273 | if (--i < 0) { | ||
8274 | jp = growjobtab(); | ||
8275 | break; | ||
8276 | } | ||
8277 | if (jp->used == 0) | ||
8278 | break; | ||
8279 | if (jp->state != JOBDONE || !jp->waited) | ||
8280 | continue; | ||
8281 | #if JOBS | ||
8282 | if (jobctl) | ||
8283 | continue; | ||
8284 | #endif | ||
8285 | freejob(jp); | ||
8286 | break; | ||
8287 | } | ||
8288 | memset(jp, 0, sizeof(*jp)); | ||
8289 | #if JOBS | ||
8290 | if (jobctl) | ||
8291 | jp->jobctl = 1; | ||
8292 | #endif | ||
8293 | jp->prev_job = curjob; | ||
8294 | curjob = jp; | ||
8295 | jp->used = 1; | ||
8296 | jp->ps = &jp->ps0; | ||
8297 | if (nprocs > 1) { | ||
8298 | jp->ps = ckmalloc(nprocs * sizeof(struct procstat)); | ||
8299 | } | ||
8300 | TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs, | ||
8301 | jobno(jp))); | ||
8302 | return jp; | ||
8303 | } | ||
8304 | |||
8305 | #if JOBS | ||
8306 | /* | ||
8307 | * Return a string identifying a command (to be printed by the | ||
8308 | * jobs command). | ||
8309 | */ | ||
8310 | static char *cmdnextc; | ||
8311 | |||
8312 | static void | ||
8313 | cmdputs(const char *s) | ||
8314 | { | ||
8315 | const char *p, *str; | ||
8316 | char c, cc[2] = " "; | ||
8317 | char *nextc; | ||
8318 | int subtype = 0; | ||
8319 | int quoted = 0; | ||
8320 | static const char vstype[VSTYPE + 1][4] = { | ||
8321 | "", "}", "-", "+", "?", "=", | ||
8322 | "%", "%%", "#", "##" | ||
8323 | }; | ||
8324 | |||
8325 | nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc); | ||
8326 | p = s; | ||
8327 | while ((c = *p++) != 0) { | ||
8328 | str = 0; | ||
8329 | switch (c) { | ||
8330 | case CTLESC: | ||
8331 | c = *p++; | ||
8332 | break; | ||
8333 | case CTLVAR: | ||
8334 | subtype = *p++; | ||
8335 | if ((subtype & VSTYPE) == VSLENGTH) | ||
8336 | str = "${#"; | ||
8337 | else | ||
8338 | str = "${"; | ||
8339 | if (!(subtype & VSQUOTE) == !(quoted & 1)) | ||
8340 | goto dostr; | ||
8341 | quoted ^= 1; | ||
8342 | c = '"'; | ||
8343 | break; | ||
8344 | case CTLENDVAR: | ||
8345 | str = "\"}" + !(quoted & 1); | ||
8346 | quoted >>= 1; | ||
8347 | subtype = 0; | ||
8348 | goto dostr; | ||
8349 | case CTLBACKQ: | ||
8350 | str = "$(...)"; | ||
8351 | goto dostr; | ||
8352 | case CTLBACKQ+CTLQUOTE: | ||
8353 | str = "\"$(...)\""; | ||
8354 | goto dostr; | ||
8355 | #if ENABLE_ASH_MATH_SUPPORT | ||
8356 | case CTLARI: | ||
8357 | str = "$(("; | ||
8358 | goto dostr; | ||
8359 | case CTLENDARI: | ||
8360 | str = "))"; | ||
8361 | goto dostr; | ||
8362 | #endif | ||
8363 | case CTLQUOTEMARK: | ||
8364 | quoted ^= 1; | ||
8365 | c = '"'; | ||
8366 | break; | ||
8367 | case '=': | ||
8368 | if (subtype == 0) | ||
8369 | break; | ||
8370 | if ((subtype & VSTYPE) != VSNORMAL) | ||
8371 | quoted <<= 1; | ||
8372 | str = vstype[subtype & VSTYPE]; | ||
8373 | if (subtype & VSNUL) | ||
8374 | c = ':'; | ||
8375 | else | ||
8376 | goto checkstr; | ||
8377 | break; | ||
8378 | case '\'': | ||
8379 | case '\\': | ||
8380 | case '"': | ||
8381 | case '$': | ||
8382 | /* These can only happen inside quotes */ | ||
8383 | cc[0] = c; | ||
8384 | str = cc; | ||
8385 | c = '\\'; | ||
8386 | break; | ||
8387 | default: | ||
8388 | break; | ||
8389 | } | ||
8390 | USTPUTC(c, nextc); | ||
8391 | checkstr: | ||
8392 | if (!str) | ||
8393 | continue; | ||
8394 | dostr: | ||
8395 | while ((c = *str++)) { | ||
8396 | USTPUTC(c, nextc); | ||
8397 | } | ||
8398 | } | ||
8399 | if (quoted & 1) { | ||
8400 | USTPUTC('"', nextc); | ||
8401 | } | ||
8402 | *nextc = 0; | ||
8403 | cmdnextc = nextc; | ||
8404 | } | ||
8405 | |||
8406 | /* cmdtxt() and cmdlist() call each other */ | ||
8407 | static void cmdtxt(union node *n); | ||
8408 | |||
8409 | static void | ||
8410 | cmdlist(union node *np, int sep) | ||
8411 | { | ||
8412 | for (; np; np = np->narg.next) { | ||
8413 | if (!sep) | ||
8414 | cmdputs(spcstr); | ||
8415 | cmdtxt(np); | ||
8416 | if (sep && np->narg.next) | ||
8417 | cmdputs(spcstr); | ||
8418 | } | ||
8419 | } | ||
8420 | |||
8421 | static void | ||
8422 | cmdtxt(union node *n) | ||
8423 | { | ||
8424 | union node *np; | ||
8425 | struct nodelist *lp; | ||
8426 | const char *p; | ||
8427 | char s[2]; | ||
8428 | |||
8429 | if (!n) | ||
8430 | return; | ||
8431 | switch (n->type) { | ||
8432 | default: | ||
8433 | #if DEBUG | ||
8434 | abort(); | ||
8435 | #endif | ||
8436 | case NPIPE: | ||
8437 | lp = n->npipe.cmdlist; | ||
8438 | for (;;) { | ||
8439 | cmdtxt(lp->n); | ||
8440 | lp = lp->next; | ||
8441 | if (!lp) | ||
8442 | break; | ||
8443 | cmdputs(" | "); | ||
8444 | } | ||
8445 | break; | ||
8446 | case NSEMI: | ||
8447 | p = "; "; | ||
8448 | goto binop; | ||
8449 | case NAND: | ||
8450 | p = " && "; | ||
8451 | goto binop; | ||
8452 | case NOR: | ||
8453 | p = " || "; | ||
8454 | binop: | ||
8455 | cmdtxt(n->nbinary.ch1); | ||
8456 | cmdputs(p); | ||
8457 | n = n->nbinary.ch2; | ||
8458 | goto donode; | ||
8459 | case NREDIR: | ||
8460 | case NBACKGND: | ||
8461 | n = n->nredir.n; | ||
8462 | goto donode; | ||
8463 | case NNOT: | ||
8464 | cmdputs("!"); | ||
8465 | n = n->nnot.com; | ||
8466 | donode: | ||
8467 | cmdtxt(n); | ||
8468 | break; | ||
8469 | case NIF: | ||
8470 | cmdputs("if "); | ||
8471 | cmdtxt(n->nif.test); | ||
8472 | cmdputs("; then "); | ||
8473 | n = n->nif.ifpart; | ||
8474 | if (n->nif.elsepart) { | ||
8475 | cmdtxt(n); | ||
8476 | cmdputs("; else "); | ||
8477 | n = n->nif.elsepart; | ||
8478 | } | ||
8479 | p = "; fi"; | ||
8480 | goto dotail; | ||
8481 | case NSUBSHELL: | ||
8482 | cmdputs("("); | ||
8483 | n = n->nredir.n; | ||
8484 | p = ")"; | ||
8485 | goto dotail; | ||
8486 | case NWHILE: | ||
8487 | p = "while "; | ||
8488 | goto until; | ||
8489 | case NUNTIL: | ||
8490 | p = "until "; | ||
8491 | until: | ||
8492 | cmdputs(p); | ||
8493 | cmdtxt(n->nbinary.ch1); | ||
8494 | n = n->nbinary.ch2; | ||
8495 | p = "; done"; | ||
8496 | dodo: | ||
8497 | cmdputs("; do "); | ||
8498 | dotail: | ||
8499 | cmdtxt(n); | ||
8500 | goto dotail2; | ||
8501 | case NFOR: | ||
8502 | cmdputs("for "); | ||
8503 | cmdputs(n->nfor.var); | ||
8504 | cmdputs(" in "); | ||
8505 | cmdlist(n->nfor.args, 1); | ||
8506 | n = n->nfor.body; | ||
8507 | p = "; done"; | ||
8508 | goto dodo; | ||
8509 | case NDEFUN: | ||
8510 | cmdputs(n->narg.text); | ||
8511 | p = "() { ... }"; | ||
8512 | goto dotail2; | ||
8513 | case NCMD: | ||
8514 | cmdlist(n->ncmd.args, 1); | ||
8515 | cmdlist(n->ncmd.redirect, 0); | ||
8516 | break; | ||
8517 | case NARG: | ||
8518 | p = n->narg.text; | ||
8519 | dotail2: | ||
8520 | cmdputs(p); | ||
8521 | break; | ||
8522 | case NHERE: | ||
8523 | case NXHERE: | ||
8524 | p = "<<..."; | ||
8525 | goto dotail2; | ||
8526 | case NCASE: | ||
8527 | cmdputs("case "); | ||
8528 | cmdputs(n->ncase.expr->narg.text); | ||
8529 | cmdputs(" in "); | ||
8530 | for (np = n->ncase.cases; np; np = np->nclist.next) { | ||
8531 | cmdtxt(np->nclist.pattern); | ||
8532 | cmdputs(") "); | ||
8533 | cmdtxt(np->nclist.body); | ||
8534 | cmdputs(";; "); | ||
8535 | } | ||
8536 | p = "esac"; | ||
8537 | goto dotail2; | ||
8538 | case NTO: | ||
8539 | p = ">"; | ||
8540 | goto redir; | ||
8541 | case NCLOBBER: | ||
8542 | p = ">|"; | ||
8543 | goto redir; | ||
8544 | case NAPPEND: | ||
8545 | p = ">>"; | ||
8546 | goto redir; | ||
8547 | case NTOFD: | ||
8548 | p = ">&"; | ||
8549 | goto redir; | ||
8550 | case NFROM: | ||
8551 | p = "<"; | ||
8552 | goto redir; | ||
8553 | case NFROMFD: | ||
8554 | p = "<&"; | ||
8555 | goto redir; | ||
8556 | case NFROMTO: | ||
8557 | p = "<>"; | ||
8558 | redir: | ||
8559 | s[0] = n->nfile.fd + '0'; | ||
8560 | s[1] = '\0'; | ||
8561 | cmdputs(s); | ||
8562 | cmdputs(p); | ||
8563 | if (n->type == NTOFD || n->type == NFROMFD) { | ||
8564 | s[0] = n->ndup.dupfd + '0'; | ||
8565 | p = s; | ||
8566 | goto dotail2; | ||
8567 | } | ||
8568 | n = n->nfile.fname; | ||
8569 | goto donode; | ||
8570 | } | ||
8571 | } | ||
8572 | |||
8573 | static char * | ||
8574 | commandtext(union node *n) | ||
8575 | { | ||
8576 | char *name; | ||
8577 | |||
8578 | STARTSTACKSTR(cmdnextc); | ||
8579 | cmdtxt(n); | ||
8580 | name = stackblock(); | ||
8581 | TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n", | ||
8582 | name, cmdnextc, cmdnextc)); | ||
8583 | return ckstrdup(name); | ||
8584 | } | ||
8585 | #endif /* JOBS */ | ||
8586 | |||
8587 | /* | ||
8588 | * Fork off a subshell. If we are doing job control, give the subshell its | ||
8589 | * own process group. Jp is a job structure that the job is to be added to. | ||
8590 | * N is the command that will be evaluated by the child. Both jp and n may | ||
8591 | * be NULL. The mode parameter can be one of the following: | ||
8592 | * FORK_FG - Fork off a foreground process. | ||
8593 | * FORK_BG - Fork off a background process. | ||
8594 | * FORK_NOJOB - Like FORK_FG, but don't give the process its own | ||
8595 | * process group even if job control is on. | ||
8596 | * | ||
8597 | * When job control is turned off, background processes have their standard | ||
8598 | * input redirected to /dev/null (except for the second and later processes | ||
8599 | * in a pipeline). | ||
8600 | * | ||
8601 | * Called with interrupts off. | ||
8602 | */ | ||
8603 | /* | ||
8604 | * Clear traps on a fork. | ||
8605 | */ | ||
8606 | static void | ||
8607 | clear_traps(void) | ||
8608 | { | ||
8609 | char **tp; | ||
8610 | |||
8611 | for (tp = trap; tp < &trap[NSIG]; tp++) { | ||
8612 | if (*tp && **tp) { /* trap not NULL or SIG_IGN */ | ||
8613 | INT_OFF; | ||
8614 | free(*tp); | ||
8615 | *tp = NULL; | ||
8616 | if (tp != &trap[0]) | ||
8617 | setsignal(tp - trap); | ||
8618 | INT_ON; | ||
8619 | } | ||
8620 | } | ||
8621 | } | ||
8622 | static void | ||
8623 | forkchild(struct job *jp, union node *n, int mode) | ||
8624 | { | ||
8625 | int oldlvl; | ||
8626 | |||
8627 | TRACE(("Child shell %d\n", getpid())); | ||
8628 | oldlvl = shlvl; | ||
8629 | shlvl++; | ||
8630 | |||
8631 | closescript(); | ||
8632 | clear_traps(); | ||
8633 | #if JOBS | ||
8634 | /* do job control only in root shell */ | ||
8635 | jobctl = 0; | ||
8636 | if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) { | ||
8637 | pid_t pgrp; | ||
8638 | |||
8639 | if (jp->nprocs == 0) | ||
8640 | pgrp = getpid(); | ||
8641 | else | ||
8642 | pgrp = jp->ps[0].pid; | ||
8643 | /* This can fail because we are doing it in the parent also */ | ||
8644 | (void)setpgid(0, pgrp); | ||
8645 | if (mode == FORK_FG) | ||
8646 | xtcsetpgrp(ttyfd, pgrp); | ||
8647 | setsignal(SIGTSTP); | ||
8648 | setsignal(SIGTTOU); | ||
8649 | } else | ||
8650 | #endif | ||
8651 | if (mode == FORK_BG) { | ||
8652 | ignoresig(SIGINT); | ||
8653 | ignoresig(SIGQUIT); | ||
8654 | if (jp->nprocs == 0) { | ||
8655 | close(0); | ||
8656 | if (open(bb_dev_null, O_RDONLY) != 0) | ||
8657 | ash_msg_and_raise_error("Can't open %s", bb_dev_null); | ||
8658 | } | ||
8659 | } | ||
8660 | if (!oldlvl && iflag) { | ||
8661 | setsignal(SIGINT); | ||
8662 | setsignal(SIGQUIT); | ||
8663 | setsignal(SIGTERM); | ||
8664 | } | ||
8665 | for (jp = curjob; jp; jp = jp->prev_job) | ||
8666 | freejob(jp); | ||
8667 | jobless = 0; | ||
8668 | } | ||
8669 | |||
8670 | static void | ||
8671 | forkparent(struct job *jp, union node *n, int mode, pid_t pid) | ||
8672 | { | ||
8673 | TRACE(("In parent shell: child = %d\n", pid)); | ||
8674 | if (!jp) { | ||
8675 | while (jobless && dowait(DOWAIT_NORMAL, 0) > 0); | ||
8676 | jobless++; | ||
8677 | return; | ||
8678 | } | ||
8679 | #if JOBS | ||
8680 | if (mode != FORK_NOJOB && jp->jobctl) { | ||
8681 | int pgrp; | ||
8682 | |||
8683 | if (jp->nprocs == 0) | ||
8684 | pgrp = pid; | ||
8685 | else | ||
8686 | pgrp = jp->ps[0].pid; | ||
8687 | /* This can fail because we are doing it in the child also */ | ||
8688 | setpgid(pid, pgrp); | ||
8689 | } | ||
8690 | #endif | ||
8691 | if (mode == FORK_BG) { | ||
8692 | backgndpid = pid; /* set $! */ | ||
8693 | set_curjob(jp, CUR_RUNNING); | ||
8694 | } | ||
8695 | if (jp) { | ||
8696 | struct procstat *ps = &jp->ps[jp->nprocs++]; | ||
8697 | ps->pid = pid; | ||
8698 | ps->status = -1; | ||
8699 | ps->cmd = nullstr; | ||
8700 | #if JOBS | ||
8701 | if (jobctl && n) | ||
8702 | ps->cmd = commandtext(n); | ||
8703 | #endif | ||
8704 | } | ||
8705 | } | ||
8706 | |||
8707 | static int | ||
8708 | forkshell(struct job *jp, union node *n, int mode) | ||
8709 | { | ||
8710 | int pid; | ||
8711 | |||
8712 | TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode)); | ||
8713 | pid = fork(); | ||
8714 | if (pid < 0) { | ||
8715 | TRACE(("Fork failed, errno=%d", errno)); | ||
8716 | if (jp) | ||
8717 | freejob(jp); | ||
8718 | ash_msg_and_raise_error("Cannot fork"); | ||
8719 | } | ||
8720 | if (pid == 0) | ||
8721 | forkchild(jp, n, mode); | ||
8722 | else | ||
8723 | forkparent(jp, n, mode, pid); | ||
8724 | return pid; | ||
8725 | } | ||
8726 | |||
8727 | /* | ||
8728 | * Wait for job to finish. | ||
8729 | * | ||
8730 | * Under job control we have the problem that while a child process is | ||
8731 | * running interrupts generated by the user are sent to the child but not | ||
8732 | * to the shell. This means that an infinite loop started by an inter- | ||
8733 | * active user may be hard to kill. With job control turned off, an | ||
8734 | * interactive user may place an interactive program inside a loop. If | ||
8735 | * the interactive program catches interrupts, the user doesn't want | ||
8736 | * these interrupts to also abort the loop. The approach we take here | ||
8737 | * is to have the shell ignore interrupt signals while waiting for a | ||
8738 | * foreground process to terminate, and then send itself an interrupt | ||
8739 | * signal if the child process was terminated by an interrupt signal. | ||
8740 | * Unfortunately, some programs want to do a bit of cleanup and then | ||
8741 | * exit on interrupt; unless these processes terminate themselves by | ||
8742 | * sending a signal to themselves (instead of calling exit) they will | ||
8743 | * confuse this approach. | ||
8744 | * | ||
8745 | * Called with interrupts off. | ||
8746 | */ | ||
8747 | static int | ||
8748 | waitforjob(struct job *jp) | ||
8749 | { | ||
8750 | int st; | ||
8751 | |||
8752 | TRACE(("waitforjob(%%%d) called\n", jobno(jp))); | ||
8753 | while (jp->state == JOBRUNNING) { | ||
8754 | dowait(DOWAIT_BLOCK, jp); | ||
8755 | } | ||
8756 | st = getstatus(jp); | ||
8757 | #if JOBS | ||
8758 | if (jp->jobctl) { | ||
8759 | xtcsetpgrp(ttyfd, rootpid); | ||
8760 | /* | ||
8761 | * This is truly gross. | ||
8762 | * If we're doing job control, then we did a TIOCSPGRP which | ||
8763 | * caused us (the shell) to no longer be in the controlling | ||
8764 | * session -- so we wouldn't have seen any ^C/SIGINT. So, we | ||
8765 | * intuit from the subprocess exit status whether a SIGINT | ||
8766 | * occurred, and if so interrupt ourselves. Yuck. - mycroft | ||
8767 | */ | ||
8768 | if (jp->sigint) | ||
8769 | raise(SIGINT); | ||
8770 | } | ||
8771 | if (jp->state == JOBDONE) | ||
8772 | #endif | ||
8773 | freejob(jp); | ||
8774 | return st; | ||
8775 | } | ||
8776 | |||
8777 | /* | ||
8778 | * return 1 if there are stopped jobs, otherwise 0 | ||
8779 | */ | ||
8780 | static int | ||
8781 | stoppedjobs(void) | ||
8782 | { | ||
8783 | struct job *jp; | ||
8784 | int retval; | ||
8785 | |||
8786 | retval = 0; | ||
8787 | if (job_warning) | ||
8788 | goto out; | ||
8789 | jp = curjob; | ||
8790 | if (jp && jp->state == JOBSTOPPED) { | ||
8791 | out2str("You have stopped jobs.\n"); | ||
8792 | job_warning = 2; | ||
8793 | retval++; | ||
8794 | } | ||
8795 | out: | ||
8796 | return retval; | ||
8797 | } | ||
8798 | |||
8799 | |||
8800 | /* ============ mail.c | 8809 | /* ============ mail.c |
8801 | * | 8810 | * |
8802 | * Routines to check for mail. | 8811 | * Routines to check for mail. |
@@ -9157,22 +9166,6 @@ setcmd(int argc, char **argv) | |||
9157 | return 0; | 9166 | return 0; |
9158 | } | 9167 | } |
9159 | 9168 | ||
9160 | #if ENABLE_LOCALE_SUPPORT | ||
9161 | static void | ||
9162 | change_lc_all(const char *value) | ||
9163 | { | ||
9164 | if (value && *value != '\0') | ||
9165 | setlocale(LC_ALL, value); | ||
9166 | } | ||
9167 | |||
9168 | static void | ||
9169 | change_lc_ctype(const char *value) | ||
9170 | { | ||
9171 | if (value && *value != '\0') | ||
9172 | setlocale(LC_CTYPE, value); | ||
9173 | } | ||
9174 | #endif | ||
9175 | |||
9176 | #if ENABLE_ASH_RANDOM_SUPPORT | 9169 | #if ENABLE_ASH_RANDOM_SUPPORT |
9177 | /* Roughly copied from bash.. */ | 9170 | /* Roughly copied from bash.. */ |
9178 | static void | 9171 | static void |