diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-02-23 21:10:06 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-02-23 21:10:06 +0000 |
| commit | a89150733a39d25a916c28e76f530845a56342bb (patch) | |
| tree | a686bc556a68108c4c4638f5c9de8bc41abe31ab /shell | |
| parent | 99eb8500c9985a4f8b359701921f9d60f694ca26 (diff) | |
| download | busybox-w32-a89150733a39d25a916c28e76f530845a56342bb.tar.gz busybox-w32-a89150733a39d25a916c28e76f530845a56342bb.tar.bz2 busybox-w32-a89150733a39d25a916c28e76f530845a56342bb.zip | |
ash: cleanup part 2.4
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 |
