diff options
Diffstat (limited to 'dotfiles/system/.zsh/modules/Src/jobs.c')
| -rw-r--r-- | dotfiles/system/.zsh/modules/Src/jobs.c | 2894 |
1 files changed, 0 insertions, 2894 deletions
diff --git a/dotfiles/system/.zsh/modules/Src/jobs.c b/dotfiles/system/.zsh/modules/Src/jobs.c deleted file mode 100644 index 38b3d89..0000000 --- a/dotfiles/system/.zsh/modules/Src/jobs.c +++ /dev/null @@ -1,2894 +0,0 @@ -/* - * jobs.c - job control - * - * This file is part of zsh, the Z shell. - * - * Copyright (c) 1992-1997 Paul Falstad - * All rights reserved. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and to distribute modified versions of this software for any - * purpose, provided that the above copyright notice and the following - * two paragraphs appear in all copies of this software. - * - * In no event shall Paul Falstad or the Zsh Development Group be liable - * to any party for direct, indirect, special, incidental, or consequential - * damages arising out of the use of this software and its documentation, - * even if Paul Falstad and the Zsh Development Group have been advised of - * the possibility of such damage. - * - * Paul Falstad and the Zsh Development Group specifically disclaim any - * warranties, including, but not limited to, the implied warranties of - * merchantability and fitness for a particular purpose. The software - * provided hereunder is on an "as is" basis, and Paul Falstad and the - * Zsh Development Group have no obligation to provide maintenance, - * support, updates, enhancements, or modifications. - * - */ - -#include "zsh.mdh" -#include "jobs.pro" - -/* the process group of the shell at startup (equal to mypgprp, except - when we started without being process group leader */ - -/**/ -mod_export pid_t origpgrp; - -/* the process group of the shell */ - -/**/ -mod_export pid_t mypgrp; - -/* the job we are working on */ - -/**/ -mod_export int thisjob; - -/* the current job (+) */ - -/**/ -mod_export int curjob; - -/* the previous job (-) */ - -/**/ -mod_export int prevjob; - -/* the job table */ - -/**/ -mod_export struct job *jobtab; - -/* Size of the job table. */ - -/**/ -mod_export int jobtabsize; - -/* The highest numbered job in the jobtable */ - -/**/ -mod_export int maxjob; - -/* If we have entered a subshell, the original shell's job table. */ -static struct job *oldjobtab; - -/* The size of that. */ -static int oldmaxjob; - -/* shell timings */ - -/**/ -#ifdef HAVE_GETRUSAGE -/**/ -static struct rusage child_usage; -/**/ -#else -/**/ -static struct tms shtms; -/**/ -#endif - -/* 1 if ttyctl -f has been executed */ - -/**/ -mod_export int ttyfrozen; - -/* Previous values of errflag and breaks if the signal handler had to - * change them. And a flag saying if it did that. */ - -/**/ -int prev_errflag, prev_breaks, errbrk_saved; - -/**/ -int numpipestats, pipestats[MAX_PIPESTATS]; - -/* Diff two timevals for elapsed-time computations */ - -/**/ -static struct timeval * -dtime(struct timeval *dt, struct timeval *t1, struct timeval *t2) -{ - dt->tv_sec = t2->tv_sec - t1->tv_sec; - dt->tv_usec = t2->tv_usec - t1->tv_usec; - if (dt->tv_usec < 0) { - dt->tv_usec += 1000000.0; - dt->tv_sec -= 1.0; - } - return dt; -} - -/* change job table entry from stopped to running */ - -/**/ -void -makerunning(Job jn) -{ - Process pn; - - jn->stat &= ~STAT_STOPPED; - for (pn = jn->procs; pn; pn = pn->next) { -#if 0 - if (WIFSTOPPED(pn->status) && - (!(jn->stat & STAT_SUPERJOB) || pn->next)) - pn->status = SP_RUNNING; -#endif - if (WIFSTOPPED(pn->status)) - pn->status = SP_RUNNING; - } - - if (jn->stat & STAT_SUPERJOB) - makerunning(jobtab + jn->other); -} - -/* Find process and job associated with pid. * - * Return 1 if search was successful, else return 0. */ - -/**/ -int -findproc(pid_t pid, Job *jptr, Process *pptr, int aux) -{ - Process pn; - int i; - - *jptr = NULL; - *pptr = NULL; - for (i = 1; i <= maxjob; i++) - { - /* - * We are only interested in jobs with processes still - * marked as live. Careful in case there's an identical - * process number in a job we haven't quite got around - * to deleting. - */ - if (jobtab[i].stat & STAT_DONE) - continue; - - for (pn = aux ? jobtab[i].auxprocs : jobtab[i].procs; - pn; pn = pn->next) - { - /* - * Make sure we match a process that's still running. - * - * When a job contains two pids, one terminated pid and one - * running pid, then the condition (jobtab[i].stat & - * STAT_DONE) will not stop these pids from being candidates - * for the findproc result (which is supposed to be a - * RUNNING pid), and if the terminated pid is an identical - * process number for the pid identifying the running - * process we are trying to find (after pid number - * wrapping), then we need to avoid returning the terminated - * pid, otherwise the shell would block and wait forever for - * the termination of the process which pid we were supposed - * to return in a different job. - */ - if (pn->pid == pid) { - *pptr = pn; - *jptr = jobtab + i; - if (pn->status == SP_RUNNING) - return 1; - } - } - } - - return (*pptr && *jptr); -} - -/* Does the given job number have any processes? */ - -/**/ -int -hasprocs(int job) -{ - Job jn; - - if (job < 0) { - DPUTS(1, "job number invalid in hasprocs"); - return 0; - } - jn = jobtab + job; - - return jn->procs || jn->auxprocs; -} - -/* Find the super-job of a sub-job. */ - -/**/ -static int -super_job(int sub) -{ - int i; - - for (i = 1; i <= maxjob; i++) - if ((jobtab[i].stat & STAT_SUPERJOB) && - jobtab[i].other == sub && - jobtab[i].gleader) - return i; - return 0; -} - -/**/ -static int -handle_sub(int job, int fg) -{ - /* job: superjob; sj: subjob. */ - Job jn = jobtab + job, sj = jobtab + jn->other; - - if ((sj->stat & STAT_DONE) || (!sj->procs && !sj->auxprocs)) { - struct process *p; - - for (p = sj->procs; p; p = p->next) { - if (WIFSIGNALED(p->status)) { - if (jn->gleader != mypgrp && jn->procs->next) - killpg(jn->gleader, WTERMSIG(p->status)); - else - kill(jn->procs->pid, WTERMSIG(p->status)); - kill(sj->other, SIGCONT); - kill(sj->other, WTERMSIG(p->status)); - break; - } - } - if (!p) { - int cp; - - jn->stat &= ~STAT_SUPERJOB; - jn->stat |= STAT_WASSUPER; - - if ((cp = ((WIFEXITED(jn->procs->status) || - WIFSIGNALED(jn->procs->status)) && - killpg(jn->gleader, 0) == -1))) { - Process p; - for (p = jn->procs; p->next; p = p->next); - jn->gleader = p->pid; - } - /* This deleted the job too early if the parent - shell waited for a command in a list that will - be executed by the sub-shell (e.g.: if we have - `ls|if true;then sleep 20;cat;fi' and ^Z the - sleep, the rest will be executed by a sub-shell, - but the parent shell gets notified for the - sleep. - deletejob(sj, 0); */ - /* If this super-job contains only the sub-shell, - we have to attach the tty to its process group - now. */ - if ((fg || thisjob == job) && - (!jn->procs->next || cp || jn->procs->pid != jn->gleader)) - attachtty(jn->gleader); - kill(sj->other, SIGCONT); - if (jn->stat & STAT_DISOWN) - { - deletejob(jn, 1); - } - } - curjob = jn - jobtab; - } else if (sj->stat & STAT_STOPPED) { - struct process *p; - - jn->stat |= STAT_STOPPED; - for (p = jn->procs; p; p = p->next) - if (p->status == SP_RUNNING || - (!WIFEXITED(p->status) && !WIFSIGNALED(p->status))) - p->status = sj->procs->status; - curjob = jn - jobtab; - printjob(jn, !!isset(LONGLISTJOBS), 1); - return 1; - } - return 0; -} - - -/* Get the latest usage information */ - -/**/ -void -get_usage(void) -{ -#ifdef HAVE_GETRUSAGE - getrusage(RUSAGE_CHILDREN, &child_usage); -#else - times(&shtms); -#endif -} - - -#if !defined HAVE_WAIT3 || !defined HAVE_GETRUSAGE -/* Update status of process that we have just WAIT'ed for */ - -/**/ -void -update_process(Process pn, int status) -{ - struct timezone dummy_tz; -#ifdef HAVE_GETRUSAGE - struct timeval childs = child_usage.ru_stime; - struct timeval childu = child_usage.ru_utime; -#else - long childs = shtms.tms_cstime; - long childu = shtms.tms_cutime; -#endif - - /* get time-accounting info */ - get_usage(); - gettimeofday(&pn->endtime, &dummy_tz); /* record time process exited */ - - pn->status = status; /* save the status returned by WAIT */ -#ifdef HAVE_GETRUSAGE - dtime(&pn->ti.ru_stime, &childs, &child_usage.ru_stime); - dtime(&pn->ti.ru_utime, &childu, &child_usage.ru_utime); -#else - pn->ti.st = shtms.tms_cstime - childs; /* compute process system space time */ - pn->ti.ut = shtms.tms_cutime - childu; /* compute process user space time */ -#endif -} -#endif - -/* - * Called when the current shell is behaving as if it received - * a interactively generated signal (sig). - * - * As we got the signal or are pretending we did, we need to pretend - * anything attached to a CURSH process got it, too. - */ -/**/ -void -check_cursh_sig(int sig) -{ - int i, j; - - if (!errflag) - return; - for (i = 1; i <= maxjob; i++) { - if ((jobtab[i].stat & (STAT_CURSH|STAT_DONE)) == - STAT_CURSH) { - for (j = 0; j < 2; j++) { - Process pn = j ? jobtab[i].auxprocs : jobtab[i].procs; - for (; pn; pn = pn->next) { - if (pn->status == SP_RUNNING) { - kill(pn->pid, sig); - } - } - } - } - } -} - -/**/ -void -storepipestats(Job jn, int inforeground, int fixlastval) -{ - int i, pipefail = 0, jpipestats[MAX_PIPESTATS]; - Process p; - - for (p = jn->procs, i = 0; p && i < MAX_PIPESTATS; p = p->next, i++) { - jpipestats[i] = (WIFSIGNALED(p->status) ? - 0200 | WTERMSIG(p->status) : - (WIFSTOPPED(p->status) ? - 0200 | WEXITSTATUS(p->status) : - WEXITSTATUS(p->status))); - if (jpipestats[i]) - pipefail = jpipestats[i]; - } - if (inforeground) { - memcpy(pipestats, jpipestats, sizeof(int)*i); - if ((jn->stat & STAT_CURSH) && i < MAX_PIPESTATS) - pipestats[i++] = lastval; - numpipestats = i; - } - - if (fixlastval) { - if (jn->stat & STAT_CURSH) { - if (!lastval && isset(PIPEFAIL)) - lastval = pipefail; - } else if (isset(PIPEFAIL)) - lastval = pipefail; - } -} - -/* Update status of job, possibly printing it */ - -/**/ -void -update_job(Job jn) -{ - Process pn; - int job; - int val = 0, status = 0; - int somestopped = 0, inforeground = 0; - - for (pn = jn->auxprocs; pn; pn = pn->next) { -#ifdef WIFCONTINUED - if (WIFCONTINUED(pn->status)) - pn->status = SP_RUNNING; -#endif - if (pn->status == SP_RUNNING) - return; - } - - for (pn = jn->procs; pn; pn = pn->next) { -#ifdef WIFCONTINUED - if (WIFCONTINUED(pn->status)) { - jn->stat &= ~STAT_STOPPED; - pn->status = SP_RUNNING; - } -#endif - if (pn->status == SP_RUNNING) /* some processes in this job are running */ - return; /* so no need to update job table entry */ - if (WIFSTOPPED(pn->status)) /* some processes are stopped */ - somestopped = 1; /* so job is not done, but entry needs updating */ - if (!pn->next) /* last job in pipeline determines exit status */ - val = (WIFSIGNALED(pn->status) ? - 0200 | WTERMSIG(pn->status) : - (WIFSTOPPED(pn->status) ? - 0200 | WEXITSTATUS(pn->status) : - WEXITSTATUS(pn->status))); - if (pn->pid == jn->gleader) /* if this process is process group leader */ - status = pn->status; - } - - job = jn - jobtab; /* compute job number */ - - if (somestopped) { - if (jn->stty_in_env && !jn->ty) { - jn->ty = (struct ttyinfo *) zalloc(sizeof(struct ttyinfo)); - gettyinfo(jn->ty); - } - if (jn->stat & STAT_STOPPED) { - if (jn->stat & STAT_SUBJOB) { - /* If we have `cat foo|while read a; grep $a bar;done' - * and have hit ^Z, the sub-job is stopped, but the - * super-job may still be running, waiting to be stopped - * or to exit. So we have to send it a SIGTSTP. */ - int i; - - if ((i = super_job(job))) - killpg(jobtab[i].gleader, SIGTSTP); - } - return; - } - } - { /* job is done or stopped, remember return value */ - lastval2 = val; - /* If last process was run in the current shell, keep old status - * and let it handle its own traps, but always allow the test - * for the pgrp. - */ - if (jn->stat & STAT_CURSH) - inforeground = 1; - else if (job == thisjob) { - lastval = val; - inforeground = 2; - } - } - - if (shout && shout != stderr && !ttyfrozen && !jn->stty_in_env && - !zleactive && job == thisjob && !somestopped && - !(jn->stat & STAT_NOSTTY)) - gettyinfo(&shttyinfo); - - if (isset(MONITOR)) { - pid_t pgrp = gettygrp(); /* get process group of tty */ - - /* is this job in the foreground of an interactive shell? */ - if (mypgrp != pgrp && inforeground && - (jn->gleader == pgrp || (pgrp > 1 && kill(-pgrp, 0) == -1))) { - if (list_pipe) { - if (somestopped || (pgrp > 1 && kill(-pgrp, 0) == -1)) { - attachtty(mypgrp); - /* check window size and adjust if necessary */ - adjustwinsize(0); - } else { - /* - * Oh, dear, we're right in the middle of some confusion - * of shell jobs on the righthand side of a pipeline, so - * it's death to call attachtty() just yet. Mark the - * fact in the job, so that the attachtty() will be called - * when the job is finally deleted. - */ - jn->stat |= STAT_ATTACH; - } - /* If we have `foo|while true; (( x++ )); done', and hit - * ^C, we have to stop the loop, too. */ - if ((val & 0200) && inforeground == 1 && - ((val & ~0200) == SIGINT || (val & ~0200) == SIGQUIT)) { - if (!errbrk_saved) { - errbrk_saved = 1; - prev_breaks = breaks; - prev_errflag = errflag; - } - breaks = loops; - errflag |= ERRFLAG_INT; - inerrflush(); - } - } else { - attachtty(mypgrp); - /* check window size and adjust if necessary */ - adjustwinsize(0); - } - } - } else if (list_pipe && (val & 0200) && inforeground == 1 && - ((val & ~0200) == SIGINT || (val & ~0200) == SIGQUIT)) { - if (!errbrk_saved) { - errbrk_saved = 1; - prev_breaks = breaks; - prev_errflag = errflag; - } - breaks = loops; - errflag |= ERRFLAG_INT; - inerrflush(); - } - if (somestopped && jn->stat & STAT_SUPERJOB) - return; - jn->stat |= (somestopped) ? STAT_CHANGED | STAT_STOPPED : - STAT_CHANGED | STAT_DONE; - if (jn->stat & (STAT_DONE|STAT_STOPPED)) { - /* This may be redundant with printjob() but note that inforeground - * is true here for STAT_CURSH jobs even when job != thisjob, most - * likely because thisjob = -1 from exec.c:execsimple() trickery. - * However, if we reset lastval here we break it for printjob(). - */ - storepipestats(jn, inforeground, 0); - } - if (!inforeground && - (jn->stat & (STAT_SUBJOB | STAT_DONE)) == (STAT_SUBJOB | STAT_DONE)) { - int su; - - if ((su = super_job(jn - jobtab))) - handle_sub(su, 0); - } - if ((jn->stat & (STAT_DONE | STAT_STOPPED)) == STAT_STOPPED) { - prevjob = curjob; - curjob = job; - } - if ((isset(NOTIFY) || job == thisjob) && (jn->stat & STAT_LOCKED)) { - if (printjob(jn, !!isset(LONGLISTJOBS), 0) && - zleactive) - zleentry(ZLE_CMD_REFRESH); - } - if (sigtrapped[SIGCHLD] && job != thisjob) - dotrap(SIGCHLD); - - /* When MONITOR is set, the foreground process runs in a different * - * process group from the shell, so the shell will not receive * - * terminal signals, therefore we pretend that the shell got * - * the signal too. */ - if (inforeground == 2 && isset(MONITOR) && WIFSIGNALED(status)) { - int sig = WTERMSIG(status); - - if (sig == SIGINT || sig == SIGQUIT) { - if (sigtrapped[sig]) { - dotrap(sig); - /* We keep the errflag as set or not by dotrap. - * This is to fulfil the promise to carry on - * with the jobs if trap returns zero. - * Setting breaks = loops ensures a consistent return - * status if inside a loop. Maybe the code in loops - * should be changed. - */ - if (errflag) - breaks = loops; - } else { - breaks = loops; - errflag |= ERRFLAG_INT; - } - check_cursh_sig(sig); - } - } -} - -/* set the previous job to something reasonable */ - -/**/ -static void -setprevjob(void) -{ - int i; - - for (i = maxjob; i; i--) - if ((jobtab[i].stat & STAT_INUSE) && (jobtab[i].stat & STAT_STOPPED) && - !(jobtab[i].stat & STAT_SUBJOB) && i != curjob && i != thisjob) { - prevjob = i; - return; - } - - for (i = maxjob; i; i--) - if ((jobtab[i].stat & STAT_INUSE) && !(jobtab[i].stat & STAT_SUBJOB) && - i != curjob && i != thisjob) { - prevjob = i; - return; - } - - prevjob = -1; -} - -/**/ -long -get_clktck(void) -{ - static long clktck; - -#ifdef _SC_CLK_TCK - if (!clktck) - /* fetch clock ticks per second from * - * sysconf only the first time */ - clktck = sysconf(_SC_CLK_TCK); -#else -# ifdef __NeXT__ - /* NeXTStep 3.3 defines CLK_TCK wrongly */ - clktck = 60; -# else -# ifdef CLK_TCK - clktck = CLK_TCK; -# else -# ifdef HZ - clktck = HZ; -# else - clktck = 60; -# endif -# endif -# endif -#endif - - return clktck; -} - -/**/ -static void -printhhmmss(double secs) -{ - int mins = (int) secs / 60; - int hours = mins / 60; - - secs -= 60 * mins; - mins -= 60 * hours; - if (hours) - fprintf(stderr, "%d:%02d:%05.2f", hours, mins, secs); - else if (mins) - fprintf(stderr, "%d:%05.2f", mins, secs); - else - fprintf(stderr, "%.3f", secs); -} - -static void -printtime(struct timeval *real, child_times_t *ti, char *desc) -{ - char *s; - double elapsed_time, user_time, system_time; -#ifdef HAVE_GETRUSAGE - double total_time; -#endif - int percent, desclen; - - if (!desc) - { - desc = ""; - desclen = 0; - } - else - { - desc = dupstring(desc); - unmetafy(desc, &desclen); - } - - /* go ahead and compute these, since almost every TIMEFMT will have them */ - elapsed_time = real->tv_sec + real->tv_usec / 1000000.0; - -#ifdef HAVE_GETRUSAGE - user_time = ti->ru_utime.tv_sec + ti->ru_utime.tv_usec / 1000000.0; - system_time = ti->ru_stime.tv_sec + ti->ru_stime.tv_usec / 1000000.0; - total_time = user_time + system_time; - percent = 100.0 * total_time - / (real->tv_sec + real->tv_usec / 1000000.0); -#else - { - long clktck = get_clktck(); - user_time = ti->ut / (double) clktck; - system_time = ti->st / (double) clktck; - percent = 100.0 * (ti->ut + ti->st) - / (clktck * real->tv_sec + clktck * real->tv_usec / 1000000.0); - } -#endif - - queue_signals(); - if (!(s = getsparam("TIMEFMT"))) - s = DEFAULT_TIMEFMT; - else - s = unmetafy(s, NULL); - - for (; *s; s++) - if (*s == '%') - switch (*++s) { - case 'E': - fprintf(stderr, "%4.2fs", elapsed_time); - break; - case 'U': - fprintf(stderr, "%4.2fs", user_time); - break; - case 'S': - fprintf(stderr, "%4.2fs", system_time); - break; - case 'm': - switch (*++s) { - case 'E': - fprintf(stderr, "%0.fms", elapsed_time * 1000.0); - break; - case 'U': - fprintf(stderr, "%0.fms", user_time * 1000.0); - break; - case 'S': - fprintf(stderr, "%0.fms", system_time * 1000.0); - break; - default: - fprintf(stderr, "%%m"); - s--; - break; - } - break; - case 'u': - switch (*++s) { - case 'E': - fprintf(stderr, "%0.fus", elapsed_time * 1000000.0); - break; - case 'U': - fprintf(stderr, "%0.fus", user_time * 1000000.0); - break; - case 'S': - fprintf(stderr, "%0.fus", system_time * 1000000.0); - break; - default: - fprintf(stderr, "%%u"); - s--; - break; - } - break; - case '*': - switch (*++s) { - case 'E': - printhhmmss(elapsed_time); - break; - case 'U': - printhhmmss(user_time); - break; - case 'S': - printhhmmss(system_time); - break; - default: - fprintf(stderr, "%%*"); - s--; - break; - } - break; - case 'P': - fprintf(stderr, "%d%%", percent); - break; -#ifdef HAVE_STRUCT_RUSAGE_RU_NSWAP - case 'W': - fprintf(stderr, "%ld", ti->ru_nswap); - break; -#endif -#ifdef HAVE_STRUCT_RUSAGE_RU_IXRSS - case 'X': - fprintf(stderr, "%ld", - total_time ? - (long)(ti->ru_ixrss / total_time) : - (long)0); - break; -#endif -#ifdef HAVE_STRUCT_RUSAGE_RU_IDRSS - case 'D': - fprintf(stderr, "%ld", - total_time ? - (long) ((ti->ru_idrss -#ifdef HAVE_STRUCT_RUSAGE_RU_ISRSS - + ti->ru_isrss -#endif - ) / total_time) : - (long)0); - break; -#endif -#if defined(HAVE_STRUCT_RUSAGE_RU_IDRSS) || \ - defined(HAVE_STRUCT_RUSAGE_RU_ISRSS) || \ - defined(HAVE_STRUCT_RUSAGE_RU_IXRSS) - case 'K': - /* treat as D if X not available */ - fprintf(stderr, "%ld", - total_time ? - (long) (( -#ifdef HAVE_STRUCT_RUSAGE_RU_IXRSS - ti->ru_ixrss -#else - 0 -#endif -#ifdef HAVE_STRUCT_RUSAGE_RU_IDRSS - + ti->ru_idrss -#endif -#ifdef HAVE_STRUCT_RUSAGE_RU_ISRSS - + ti->ru_isrss -#endif - ) / total_time) : - (long)0); - break; -#endif -#ifdef HAVE_STRUCT_RUSAGE_RU_MAXRSS - case 'M': - fprintf(stderr, "%ld", ti->ru_maxrss / 1024); - break; -#endif -#ifdef HAVE_STRUCT_RUSAGE_RU_MAJFLT - case 'F': - fprintf(stderr, "%ld", ti->ru_majflt); - break; -#endif -#ifdef HAVE_STRUCT_RUSAGE_RU_MINFLT - case 'R': - fprintf(stderr, "%ld", ti->ru_minflt); - break; -#endif -#ifdef HAVE_STRUCT_RUSAGE_RU_INBLOCK - case 'I': - fprintf(stderr, "%ld", ti->ru_inblock); - break; -#endif -#ifdef HAVE_STRUCT_RUSAGE_RU_OUBLOCK - case 'O': - fprintf(stderr, "%ld", ti->ru_oublock); - break; -#endif -#ifdef HAVE_STRUCT_RUSAGE_RU_MSGRCV - case 'r': - fprintf(stderr, "%ld", ti->ru_msgrcv); - break; -#endif -#ifdef HAVE_STRUCT_RUSAGE_RU_MSGSND - case 's': - fprintf(stderr, "%ld", ti->ru_msgsnd); - break; -#endif -#ifdef HAVE_STRUCT_RUSAGE_RU_NSIGNALS - case 'k': - fprintf(stderr, "%ld", ti->ru_nsignals); - break; -#endif -#ifdef HAVE_STRUCT_RUSAGE_RU_NVCSW - case 'w': - fprintf(stderr, "%ld", ti->ru_nvcsw); - break; -#endif -#ifdef HAVE_STRUCT_RUSAGE_RU_NIVCSW - case 'c': - fprintf(stderr, "%ld", ti->ru_nivcsw); - break; -#endif - case 'J': - fwrite(desc, sizeof(char), desclen, stderr); - break; - case '%': - putc('%', stderr); - break; - case '\0': - s--; - break; - default: - fprintf(stderr, "%%%c", *s); - break; - } else - putc(*s, stderr); - unqueue_signals(); - putc('\n', stderr); - fflush(stderr); -} - -/**/ -static void -dumptime(Job jn) -{ - Process pn; - struct timeval dtimeval; - - if (!jn->procs) - return; - for (pn = jn->procs; pn; pn = pn->next) - printtime(dtime(&dtimeval, &pn->bgtime, &pn->endtime), &pn->ti, - pn->text); -} - -/* Check whether shell should report the amount of time consumed * - * by job. This will be the case if we have preceded the command * - * with the keyword time, or if REPORTTIME is non-negative and the * - * amount of time consumed by the job is greater than REPORTTIME */ - -/**/ -static int -should_report_time(Job j) -{ - struct value vbuf; - Value v; - char *s = "REPORTTIME"; - int save_errflag = errflag; - zlong reporttime = -1; -#ifdef HAVE_GETRUSAGE - char *sm = "REPORTMEMORY"; - zlong reportmemory = -1; -#endif - - /* if the time keyword was used */ - if (j->stat & STAT_TIMED) - return 1; - - queue_signals(); - errflag = 0; - if ((v = getvalue(&vbuf, &s, 0))) - reporttime = getintvalue(v); -#ifdef HAVE_GETRUSAGE - if ((v = getvalue(&vbuf, &sm, 0))) - reportmemory = getintvalue(v); -#endif - errflag = save_errflag; - unqueue_signals(); - if (reporttime < 0 -#ifdef HAVE_GETRUSAGE - && reportmemory < 0 -#endif - ) - return 0; - /* can this ever happen? */ - if (!j->procs) - return 0; - if (zleactive) - return 0; - - if (reporttime >= 0) - { -#ifdef HAVE_GETRUSAGE - reporttime -= j->procs->ti.ru_utime.tv_sec + - j->procs->ti.ru_stime.tv_sec; - if (j->procs->ti.ru_utime.tv_usec + - j->procs->ti.ru_stime.tv_usec >= 1000000) - reporttime--; - if (reporttime <= 0) - return 1; -#else - { - clktck = get_clktck(); - if ((j->procs->ti.ut + j->procs->ti.st) / clktck >= reporttime) - return 1; - } -#endif - } - -#ifdef HAVE_GETRUSAGE - if (reportmemory >= 0 && - j->procs->ti.ru_maxrss / 1024 > reportmemory) - return 1; -#endif - - return 0; -} - -/* !(lng & 3) means jobs * - * (lng & 1) means jobs -l * - * (lng & 2) means jobs -p - * (lng & 4) means jobs -d - * - * synch = 0 means asynchronous - * synch = 1 means synchronous - * synch = 2 means called synchronously from jobs - * synch = 3 means called synchronously from bg or fg - * - * Returns 1 if some output was done. - * - * The function also deletes the job if it was done, even it - * is not printed. - */ - -/**/ -int -printjob(Job jn, int lng, int synch) -{ - Process pn; - int job, len = 9, sig, sflag = 0, llen; - int conted = 0, lineleng = zterm_columns, skip = 0, doputnl = 0; - int doneprint = 0, skip_print = 0; - FILE *fout = (synch == 2 || !shout) ? stdout : shout; - - if (synch > 1 && oldjobtab != NULL) - job = jn - oldjobtab; - else - job = jn - jobtab; - DPUTS3(job < 0 || job > (oldjobtab && synch > 1 ? oldmaxjob : maxjob), - "bogus job number, jn = %L, jobtab = %L, oldjobtab = %L", - (long)jn, (long)jobtab, (long)oldjobtab); - - if (jn->stat & STAT_NOPRINT) { - skip_print = 1; - } - - if (lng < 0) { - conted = 1; - lng = !!isset(LONGLISTJOBS); - } - -/* find length of longest signame, check to see */ -/* if we really need to print this job */ - - for (pn = jn->procs; pn; pn = pn->next) { - if (jn->stat & STAT_SUPERJOB && - jn->procs->status == SP_RUNNING && !pn->next) - pn->status = SP_RUNNING; - if (pn->status != SP_RUNNING) { - if (WIFSIGNALED(pn->status)) { - sig = WTERMSIG(pn->status); - llen = strlen(sigmsg(sig)); - if (WCOREDUMP(pn->status)) - llen += 14; - if (llen > len) - len = llen; - if (sig != SIGINT && sig != SIGPIPE) - sflag = 1; - if (job == thisjob && sig == SIGINT) - doputnl = 1; - if (isset(PRINTEXITVALUE) && isset(SHINSTDIN)) { - sflag = 1; - skip_print = 0; - } - } else if (WIFSTOPPED(pn->status)) { - sig = WSTOPSIG(pn->status); - if ((int)strlen(sigmsg(sig)) > len) - len = strlen(sigmsg(sig)); - if (job == thisjob && sig == SIGTSTP) - doputnl = 1; - } else if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) && - WEXITSTATUS(pn->status)) { - sflag = 1; - skip_print = 0; - } - } - } - - if (skip_print) { - if (jn->stat & STAT_DONE) { - /* This looks silly, but see update_job() */ - if (synch <= 1) - storepipestats(jn, job == thisjob, job == thisjob); - if (should_report_time(jn)) - dumptime(jn); - deletejob(jn, 0); - if (job == curjob) { - curjob = prevjob; - prevjob = job; - } - if (job == prevjob) - setprevjob(); - } - return 0; - } - - /* - * - Always print if called from jobs - * - Otherwise, require MONITOR option ("jobbing") and some - * change of state - * - also either the shell is interactive or this is synchronous. - */ - if (synch == 2 || - ((interact || synch) && jobbing && - ((jn->stat & STAT_STOPPED) || sflag || job != thisjob))) { - int len2, fline = 1; - /* POSIX requires just the job text for bg and fg */ - int plainfmt = (synch == 3) && isset(POSIXJOBS); - /* use special format for current job, except in `jobs' */ - int thisfmt = job == thisjob && synch != 2; - Process qn; - - if (!synch) - zleentry(ZLE_CMD_TRASH); - if (doputnl && !synch) { - doneprint = 1; - putc('\n', fout); - } - for (pn = jn->procs; pn;) { - len2 = (thisfmt ? 5 : 10) + len; /* 2 spaces */ - if (lng & 3) - qn = pn->next; - else - for (qn = pn->next; qn; qn = qn->next) { - if (qn->status != pn->status) - break; - if ((int)strlen(qn->text) + len2 + ((qn->next) ? 3 : 0) - > lineleng) - break; - len2 += strlen(qn->text) + 2; - } - doneprint = 1; - if (!plainfmt) { - if (!thisfmt || lng) { - if (fline) - fprintf(fout, "[%ld] %c ", - (long)job, - (job == curjob) ? '+' - : (job == prevjob) ? '-' : ' '); - else - fprintf(fout, (job > 9) ? " " : " "); - } else - fprintf(fout, "zsh: "); - if (lng & 1) - fprintf(fout, "%ld ", (long) pn->pid); - else if (lng & 2) { - pid_t x = jn->gleader; - - fprintf(fout, "%ld ", (long) x); - do - skip++; - while ((x /= 10)); - skip++; - lng &= ~3; - } else - fprintf(fout, "%*s", skip, ""); - if (pn->status == SP_RUNNING) { - if (!conted) - fprintf(fout, "running%*s", len - 7 + 2, ""); - else - fprintf(fout, "continued%*s", len - 9 + 2, ""); - } - else if (WIFEXITED(pn->status)) { - if (WEXITSTATUS(pn->status)) - fprintf(fout, "exit %-4d%*s", WEXITSTATUS(pn->status), - len - 9 + 2, ""); - else - fprintf(fout, "done%*s", len - 4 + 2, ""); - } else if (WIFSTOPPED(pn->status)) - fprintf(fout, "%-*s", len + 2, - sigmsg(WSTOPSIG(pn->status))); - else if (WCOREDUMP(pn->status)) - fprintf(fout, "%s (core dumped)%*s", - sigmsg(WTERMSIG(pn->status)), - (int)(len - 14 + 2 - - strlen(sigmsg(WTERMSIG(pn->status)))), ""); - else - fprintf(fout, "%-*s", len + 2, - sigmsg(WTERMSIG(pn->status))); - } - for (; pn != qn; pn = pn->next) { - char *txt = dupstring(pn->text); - int txtlen; - unmetafy(txt, &txtlen); - fwrite(txt, sizeof(char), txtlen, fout); - if (pn->next) - fputs(" | ", fout); - } - putc('\n', fout); - fline = 0; - } - fflush(fout); - } else if (doputnl && interact && !synch) { - doneprint = 1; - putc('\n', fout); - fflush(fout); - } - - /* print "(pwd now: foo)" messages: with (lng & 4) we are printing - * the directory where the job is running, otherwise the current directory - */ - - if ((lng & 4) || (interact && job == thisjob && - jn->pwd && strcmp(jn->pwd, pwd))) { - doneprint = 1; - fprintf(fout, "(pwd %s: ", (lng & 4) ? "" : "now"); - fprintdir(((lng & 4) && jn->pwd) ? jn->pwd : pwd, fout); - fprintf(fout, ")\n"); - fflush(fout); - } - - /* delete job if done */ - - if (jn->stat & STAT_DONE) { - /* This looks silly, but see update_job() */ - if (synch <= 1) - storepipestats(jn, job == thisjob, job == thisjob); - if (should_report_time(jn)) - dumptime(jn); - deletejob(jn, 0); - if (job == curjob) { - curjob = prevjob; - prevjob = job; - } - if (job == prevjob) - setprevjob(); - } else - jn->stat &= ~STAT_CHANGED; - - return doneprint; -} - -/* Add a file to be deleted or fd to be closed to the current job */ - -/**/ -void -addfilelist(const char *name, int fd) -{ - Jobfile jf = (Jobfile)zalloc(sizeof(struct jobfile)); - LinkList ll = jobtab[thisjob].filelist; - - if (!ll) - ll = jobtab[thisjob].filelist = znewlinklist(); - if (name) - { - jf->u.name = ztrdup(name); - jf->is_fd = 0; - } - else - { - jf->u.fd = fd; - jf->is_fd = 1; - } - zaddlinknode(ll, jf); -} - -/* Clean up pipes no longer needed associated with a job */ - -/**/ -void -pipecleanfilelist(LinkList filelist, int proc_subst_only) -{ - LinkNode node; - - if (!filelist) - return; - node = firstnode(filelist); - while (node) { - Jobfile jf = (Jobfile)getdata(node); - if (jf->is_fd && - (!proc_subst_only || fdtable[jf->u.fd] == FDT_PROC_SUBST)) { - LinkNode next = nextnode(node); - zclose(jf->u.fd); - (void)remnode(filelist, node); - zfree(jf, sizeof(*jf)); - node = next; - } else - incnode(node); - } -} - -/* Finished with list of files for a job */ - -/**/ -void -deletefilelist(LinkList file_list, int disowning) -{ - Jobfile jf; - if (file_list) { - while ((jf = (Jobfile)getlinknode(file_list))) { - if (jf->is_fd) { - if (!disowning) - zclose(jf->u.fd); - } else { - if (!disowning) - unlink(jf->u.name); - zsfree(jf->u.name); - } - zfree(jf, sizeof(*jf)); - } - zfree(file_list, sizeof(struct linklist)); - } -} - -/**/ -void -freejob(Job jn, int deleting) -{ - struct process *pn, *nx; - - pn = jn->procs; - jn->procs = NULL; - for (; pn; pn = nx) { - nx = pn->next; - zfree(pn, sizeof(struct process)); - } - - pn = jn->auxprocs; - jn->auxprocs = NULL; - for (; pn; pn = nx) { - nx = pn->next; - zfree(pn, sizeof(struct process)); - } - - if (jn->ty) - zfree(jn->ty, sizeof(struct ttyinfo)); - if (jn->pwd) - zsfree(jn->pwd); - jn->pwd = NULL; - if (jn->stat & STAT_WASSUPER) { - /* careful in case we shrink and move the job table */ - int job = jn - jobtab; - if (deleting) - deletejob(jobtab + jn->other, 0); - else - freejob(jobtab + jn->other, 0); - jn = jobtab + job; - } - jn->gleader = jn->other = 0; - jn->stat = jn->stty_in_env = 0; - jn->filelist = NULL; - jn->ty = NULL; - - /* Find the new highest job number. */ - if (maxjob == jn - jobtab) { - while (maxjob && !(jobtab[maxjob].stat & STAT_INUSE)) - maxjob--; - } -} - -/* - * We are actually finished with this job, rather - * than freeing it to make space. - * - * If "disowning" is set, files associated with the job are not - * actually deleted --- and won't be as there is nothing left - * to clear up. - */ - -/**/ -void -deletejob(Job jn, int disowning) -{ - deletefilelist(jn->filelist, disowning); - if (jn->stat & STAT_ATTACH) { - attachtty(mypgrp); - adjustwinsize(0); - } - if (jn->stat & STAT_SUPERJOB) { - Job jno = jobtab + jn->other; - if (jno->stat & STAT_SUBJOB) - jno->stat |= STAT_SUBJOB_ORPHANED; - } - - freejob(jn, 1); -} - -/* - * Add a process to the current job. - * The third argument is 1 if we are adding a process which is not - * part of the main pipeline but an auxiliary process used for - * handling MULTIOS or process substitution. We will wait for it - * but not display job information about it. - */ - -/**/ -void -addproc(pid_t pid, char *text, int aux, struct timeval *bgtime) -{ - Process pn, *pnlist; - - DPUTS(thisjob == -1, "No valid job in addproc."); - pn = (Process) zshcalloc(sizeof *pn); - pn->pid = pid; - if (text) - strcpy(pn->text, text); - else - *pn->text = '\0'; - pn->status = SP_RUNNING; - pn->next = NULL; - - if (!aux) - { - pn->bgtime = *bgtime; - /* if this is the first process we are adding to * - * the job, then it's the group leader. */ - if (!jobtab[thisjob].gleader) - jobtab[thisjob].gleader = pid; - /* attach this process to end of process list of current job */ - pnlist = &jobtab[thisjob].procs; - } - else - pnlist = &jobtab[thisjob].auxprocs; - - if (*pnlist) { - Process n; - - for (n = *pnlist; n->next; n = n->next); - n->next = pn; - } else { - /* first process for this job */ - *pnlist = pn; - } - /* If the first process in the job finished before any others were * - * added, maybe STAT_DONE got set incorrectly. This can happen if * - * a $(...) was waited for and the last existing job in the * - * pipeline was already finished. We need to be very careful that * - * there was no call to printjob() between then and now, else * - * the job will already have been deleted from the table. */ - jobtab[thisjob].stat &= ~STAT_DONE; -} - -/* Check if we have files to delete. We need to check this to see * - * if it's all right to exec a command without forking in the last * - * component of subshells or after the `-c' option. */ - -/**/ -int -havefiles(void) -{ - int i; - - for (i = 1; i <= maxjob; i++) - if (jobtab[i].stat && jobtab[i].filelist) - return 1; - return 0; - -} - -/* - * Wait for a particular process. - * wait_cmd indicates this is from the interactive wait command, - * in which case the behaviour is a little different: the command - * itself can be interrupted by a trapped signal. - */ - -/**/ -int -waitforpid(pid_t pid, int wait_cmd) -{ - int first = 1, q = queue_signal_level(); - - /* child_block() around this loop in case #ifndef WNOHANG */ - dont_queue_signals(); - child_block(); /* unblocked in signal_suspend() */ - queue_traps(wait_cmd); - - /* This function should never be called with a pid that is not a - * child of the current shell. Consequently, if kill(0, pid) - * fails here with ESRCH, the child has already been reaped. In - * the loop body, we expect this to happen in signal_suspend() - * via zhandler(), after which this test terminates the loop. - */ - while (!errflag && (kill(pid, 0) >= 0 || errno != ESRCH)) { - if (first) - first = 0; - else if (!wait_cmd) - kill(pid, SIGCONT); - - last_signal = -1; - signal_suspend(SIGCHLD, wait_cmd); - if (last_signal != SIGCHLD && wait_cmd && last_signal >= 0 && - (sigtrapped[last_signal] & ZSIG_TRAPPED)) { - /* wait command interrupted, but no error: return */ - restore_queue_signals(q); - return 128 + last_signal; - } - child_block(); - } - unqueue_traps(); - child_unblock(); - restore_queue_signals(q); - - return 0; -} - -/* - * Wait for a job to finish. - * wait_cmd indicates this is from the wait builtin; see - * wait_cmd in waitforpid(). - */ - -/**/ -static int -zwaitjob(int job, int wait_cmd) -{ - int q = queue_signal_level(); - Job jn = jobtab + job; - - child_block(); /* unblocked during signal_suspend() */ - queue_traps(wait_cmd); - dont_queue_signals(); - if (jn->procs || jn->auxprocs) { /* if any forks were done */ - jn->stat |= STAT_LOCKED; - if (jn->stat & STAT_CHANGED) - printjob(jn, !!isset(LONGLISTJOBS), 1); - if (jn->filelist) { - /* - * The main shell is finished with any file descriptors used - * for process substitution associated with this job: close - * them to indicate to listeners there's no more input. - * - * Note we can't safely delete temporary files yet as these - * are directly visible to other processes. However, - * we can't deadlock on the fact that those still exist, so - * that's not a problem. - */ - pipecleanfilelist(jn->filelist, 0); - } - while (!(errflag & ERRFLAG_ERROR) && jn->stat && - !(jn->stat & STAT_DONE) && - !(interact && (jn->stat & STAT_STOPPED))) { - signal_suspend(SIGCHLD, wait_cmd); - if (last_signal != SIGCHLD && wait_cmd && last_signal >= 0 && - (sigtrapped[last_signal] & ZSIG_TRAPPED)) - { - /* builtin wait interrupted by trapped signal */ - restore_queue_signals(q); - return 128 + last_signal; - } - /* Commenting this out makes ^C-ing a job started by a function - stop the whole function again. But I guess it will stop - something else from working properly, we have to find out - what this might be. --oberon - - When attempting to separate errors and interrupts, we - assumed because of the previous comment it would be OK - to remove ERRFLAG_ERROR and leave ERRFLAG_INT set, since - that's the one related to ^C. But that doesn't work. - There's something more here we don't understand. --pws - - The change above to ignore ERRFLAG_INT in the loop test - solves a problem wherein child processes that ignore the - INT signal were never waited-for. Clearing the flag here - still seems the wrong thing, but perhaps ERRFLAG_INT - should be saved and restored around signal_suspend() to - prevent it being lost within a signal trap? --Bart - - errflag = 0; */ - - if (subsh) { - killjb(jn, SIGCONT); - jn->stat &= ~STAT_STOPPED; - } - if (jn->stat & STAT_SUPERJOB) - if (handle_sub(jn - jobtab, 1)) - break; - child_block(); - } - } else { - deletejob(jn, 0); - pipestats[0] = lastval; - numpipestats = 1; - } - restore_queue_signals(q); - unqueue_traps(); - child_unblock(); - - return 0; -} - -/* wait for running job to finish */ - -/**/ -void -waitjobs(void) -{ - Job jn = jobtab + thisjob; - DPUTS(thisjob == -1, "No valid job in waitjobs."); - - if (jn->procs || jn->auxprocs) - zwaitjob(thisjob, 0); - else { - deletejob(jn, 0); - pipestats[0] = lastval; - numpipestats = 1; - } - thisjob = -1; -} - -/* clear job table when entering subshells */ - -/**/ -mod_export void -clearjobtab(int monitor) -{ - int i; - - if (isset(POSIXJOBS)) - oldmaxjob = 0; - for (i = 1; i <= maxjob; i++) { - /* - * See if there is a jobtable worth saving. - * We never free the saved version; it only happens - * once for each subshell of a shell with job control, - * so doesn't create a leak. - */ - if (monitor && !isset(POSIXJOBS) && jobtab[i].stat) - oldmaxjob = i+1; - else if (jobtab[i].stat & STAT_INUSE) - freejob(jobtab + i, 0); - } - - if (monitor && oldmaxjob) { - int sz = oldmaxjob * sizeof(struct job); - if (oldjobtab) - free(oldjobtab); - oldjobtab = (struct job *)zalloc(sz); - memcpy(oldjobtab, jobtab, sz); - - /* Don't report any job we're part of */ - if (thisjob != -1 && thisjob < oldmaxjob) - memset(oldjobtab+thisjob, 0, sizeof(struct job)); - } - - memset(jobtab, 0, jobtabsize * sizeof(struct job)); /* zero out table */ - maxjob = 0; - - /* - * Although we don't have job control in subshells, we - * sometimes needs control structures for other purposes such - * as multios. Grab a job for this purpose; any will do - * since we've freed them all up (so there's no question - * of problems with the job table size here). - */ - thisjob = initjob(); -} - -static int initnewjob(int i) -{ - jobtab[i].stat = STAT_INUSE; - if (jobtab[i].pwd) { - zsfree(jobtab[i].pwd); - jobtab[i].pwd = NULL; - } - jobtab[i].gleader = 0; - - if (i > maxjob) - maxjob = i; - - return i; -} - -/* Get a free entry in the job table and initialize it. */ - -/**/ -int -initjob(void) -{ - int i; - - for (i = 1; i <= maxjob; i++) - if (!jobtab[i].stat) - return initnewjob(i); - if (maxjob + 1 < jobtabsize) - return initnewjob(maxjob+1); - - if (expandjobtab()) - return initnewjob(i); - - zerr("job table full or recursion limit exceeded"); - return -1; -} - -/**/ -void -setjobpwd(void) -{ - int i; - - for (i = 1; i <= maxjob; i++) - if (jobtab[i].stat && !jobtab[i].pwd) - jobtab[i].pwd = ztrdup(pwd); -} - -/* print pids for & */ - -/**/ -void -spawnjob(void) -{ - Process pn; - - DPUTS(thisjob == -1, "No valid job in spawnjob."); - /* if we are not in a subshell */ - if (!subsh) { - if (curjob == -1 || !(jobtab[curjob].stat & STAT_STOPPED)) { - curjob = thisjob; - setprevjob(); - } else if (prevjob == -1 || !(jobtab[prevjob].stat & STAT_STOPPED)) - prevjob = thisjob; - if (jobbing && jobtab[thisjob].procs) { - FILE *fout = shout ? shout : stdout; - fprintf(fout, "[%d]", thisjob); - for (pn = jobtab[thisjob].procs; pn; pn = pn->next) - fprintf(fout, " %ld", (long) pn->pid); - fprintf(fout, "\n"); - fflush(fout); - } - } - if (!hasprocs(thisjob)) - deletejob(jobtab + thisjob, 0); - else { - jobtab[thisjob].stat |= STAT_LOCKED; - pipecleanfilelist(jobtab[thisjob].filelist, 0); - } - thisjob = -1; -} - -/**/ -void -shelltime(void) -{ - struct timezone dummy_tz; - struct timeval dtimeval, now; - child_times_t ti; -#ifndef HAVE_GETRUSAGE - struct tms buf; -#endif - - gettimeofday(&now, &dummy_tz); - -#ifdef HAVE_GETRUSAGE - getrusage(RUSAGE_SELF, &ti); -#else - times(&buf); - - ti.ut = buf.tms_utime; - ti.st = buf.tms_stime; -#endif - printtime(dtime(&dtimeval, &shtimer, &now), &ti, "shell"); - -#ifdef HAVE_GETRUSAGE - getrusage(RUSAGE_CHILDREN, &ti); -#else - ti.ut = buf.tms_cutime; - ti.st = buf.tms_cstime; -#endif - printtime(&dtimeval, &ti, "children"); - -} - -/* see if jobs need printing */ - -/**/ -void -scanjobs(void) -{ - int i; - - for (i = 1; i <= maxjob; i++) - if (jobtab[i].stat & STAT_CHANGED) - printjob(jobtab + i, !!isset(LONGLISTJOBS), 1); -} - -/**** job control builtins ****/ - -/* This simple function indicates whether or not s may represent * - * a number. It returns true iff s consists purely of digits and * - * minuses. Note that minus may appear more than once, and the empty * - * string will produce a `true' response. */ - -/**/ -static int -isanum(char *s) -{ - while (*s == '-' || idigit(*s)) - s++; - return *s == '\0'; -} - -/* Make sure we have a suitable current and previous job set. */ - -/**/ -static void -setcurjob(void) -{ - if (curjob == thisjob || - (curjob != -1 && !(jobtab[curjob].stat & STAT_INUSE))) { - curjob = prevjob; - setprevjob(); - if (curjob == thisjob || - (curjob != -1 && !((jobtab[curjob].stat & STAT_INUSE) && - curjob != thisjob))) { - curjob = prevjob; - setprevjob(); - } - } -} - -/* Convert a job specifier ("%%", "%1", "%foo", "%?bar?", etc.) * - * to a job number. */ - -/**/ -mod_export int -getjob(const char *s, const char *prog) -{ - int jobnum, returnval, mymaxjob; - Job myjobtab; - - if (oldjobtab) { - myjobtab = oldjobtab; - mymaxjob = oldmaxjob; - } else { - myjobtab= jobtab; - mymaxjob = maxjob; - } - - /* if there is no %, treat as a name */ - if (*s != '%') - goto jump; - s++; - /* "%%", "%+" and "%" all represent the current job */ - if (*s == '%' || *s == '+' || !*s) { - if (curjob == -1) { - if (prog) - zwarnnam(prog, "no current job"); - returnval = -1; - goto done; - } - returnval = curjob; - goto done; - } - /* "%-" represents the previous job */ - if (*s == '-') { - if (prevjob == -1) { - if (prog) - zwarnnam(prog, "no previous job"); - returnval = -1; - goto done; - } - returnval = prevjob; - goto done; - } - /* a digit here means we have a job number */ - if (idigit(*s)) { - jobnum = atoi(s); - if (jobnum && jobnum <= mymaxjob && myjobtab[jobnum].stat && - !(myjobtab[jobnum].stat & STAT_SUBJOB) && - /* - * If running jobs in a subshell, we are allowed to - * refer to the "current" job (it's not really the - * current job in the subshell). It's possible we - * should reset thisjob to -1 on entering the subshell. - */ - (myjobtab == oldjobtab || jobnum != thisjob)) { - returnval = jobnum; - goto done; - } - if (prog) - zwarnnam(prog, "%%%s: no such job", s); - returnval = -1; - goto done; - } - /* "%?" introduces a search string */ - if (*s == '?') { - struct process *pn; - - for (jobnum = mymaxjob; jobnum >= 0; jobnum--) - if (myjobtab[jobnum].stat && - !(myjobtab[jobnum].stat & STAT_SUBJOB) && - jobnum != thisjob) - for (pn = myjobtab[jobnum].procs; pn; pn = pn->next) - if (strstr(pn->text, s + 1)) { - returnval = jobnum; - goto done; - } - if (prog) - zwarnnam(prog, "job not found: %s", s); - returnval = -1; - goto done; - } - jump: - /* anything else is a job name, specified as a string that begins the - job's command */ - if ((jobnum = findjobnam(s)) != -1) { - returnval = jobnum; - goto done; - } - /* if we get here, it is because none of the above succeeded and went - to done */ - zwarnnam(prog, "job not found: %s", s); - returnval = -1; - done: - return returnval; -} - -#ifndef HAVE_SETPROCTITLE -/* For jobs -Z (which modifies the shell's name as seen in ps listings). * - * hackzero is the start of the safely writable space, and hackspace is * - * its length, excluding a final NUL terminator that will always be left. */ - -static char *hackzero; -static int hackspace; -#endif - - -/* Initialise job handling. */ - -/**/ -void -init_jobs(char **argv, char **envp) -{ -#ifndef HAVE_SETPROCTITLE - char *p, *q; -#endif - size_t init_bytes = MAXJOBS_ALLOC*sizeof(struct job); - - /* - * Initialise the job table. If this fails, we're in trouble. - */ - jobtab = (struct job *)zalloc(init_bytes); - if (!jobtab) { - zerr("failed to allocate job table, aborting."); - exit(1); - } - jobtabsize = MAXJOBS_ALLOC; - memset(jobtab, 0, init_bytes); - -#ifndef HAVE_SETPROCTITLE - /* - * Initialise the jobs -Z system. The technique is borrowed from - * perl: check through the argument and environment space, to see - * how many of the strings are in contiguous space. This determines - * the value of hackspace. - */ - hackzero = *argv; - p = strchr(hackzero, 0); - while(*++argv) { - q = *argv; - if(q != p+1) - goto done; - p = strchr(q, 0); - } -#if !defined(HAVE_PUTENV) && !defined(USE_SET_UNSET_ENV) - for(; *envp; envp++) { - q = *envp; - if(q != p+1) - goto done; - p = strchr(q, 0); - } -#endif - done: - hackspace = p - hackzero; -#endif -} - - -/* - * We have run out of space in the job table. - * Expand it by an additional MAXJOBS_ALLOC slots. - */ - -/* - * An arbitrary limit on the absolute maximum size of the job table. - * This prevents us taking over the entire universe. - * Ought to be a multiple of MAXJOBS_ALLOC, but doesn't need to be. - */ -#define MAX_MAXJOBS 1000 - -/**/ -int -expandjobtab(void) -{ - int newsize = jobtabsize + MAXJOBS_ALLOC; - struct job *newjobtab; - - if (newsize > MAX_MAXJOBS) - return 0; - - newjobtab = (struct job *)zrealloc(jobtab, newsize * sizeof(struct job)); - if (!newjobtab) - return 0; - - /* - * Clear the new section of the table; this is necessary for - * the jobs to appear unused. - */ - memset(newjobtab + jobtabsize, 0, MAXJOBS_ALLOC * sizeof(struct job)); - - jobtab = newjobtab; - jobtabsize = newsize; - - return 1; -} - - -/* - * See if we can reduce the job table. We can if we go over - * a MAXJOBS_ALLOC boundary. However, we leave a boundary, - * currently 20 jobs, so that we have a place for immediate - * expansion and don't play ping pong with the job table size. - */ - -/**/ -void -maybeshrinkjobtab(void) -{ - int jobbound; - - queue_signals(); - jobbound = maxjob + MAXJOBS_ALLOC - (maxjob % MAXJOBS_ALLOC); - if (jobbound < jobtabsize && jobbound > maxjob + 20) { - struct job *newjobtab; - - /* Hope this can't fail, but anyway... */ - newjobtab = (struct job *)zrealloc(jobtab, - jobbound*sizeof(struct job)); - - if (newjobtab) { - jobtab = newjobtab; - jobtabsize = jobbound; - } - } - unqueue_signals(); -} - -/* - * Definitions for the background process stuff recorded below. - * This would be more efficient as a hash, but - * - that's quite heavyweight for something not needed very often - * - we need some kind of ordering as POSIX allows us to limit - * the size of the list to the value of _SC_CHILD_MAX and clearly - * we want to clear the oldest first - * - cases with a long list of background jobs where the user doesn't - * wait for a large number, and then does wait for one (the only - * inefficient case) are rare - * - in the context of waiting for an external process, looping - * over a list isn't so very inefficient. - * Enough excuses already. - */ - -/* Data in the link list, a key (process ID) / value (exit status) pair. */ -struct bgstatus { - pid_t pid; - int status; -}; -typedef struct bgstatus *Bgstatus; -/* The list of those entries */ -static LinkList bgstatus_list; -/* Count of entries. Reaches value of _SC_CHILD_MAX and stops. */ -static long bgstatus_count; - -/* - * Remove and free a bgstatus entry. - */ -static void rembgstatus(LinkNode node) -{ - zfree(remnode(bgstatus_list, node), sizeof(struct bgstatus)); - bgstatus_count--; -} - -/* - * Record the status of a background process that exited so we - * can execute the builtin wait for it. - * - * We can't execute the wait builtin for something that exited in the - * foreground as it's not visible to the user, so don't bother recording. - */ - -/**/ -void -addbgstatus(pid_t pid, int status) -{ - static long child_max; - Bgstatus bgstatus_entry; - - if (!child_max) { -#ifdef _SC_CHILD_MAX - child_max = sysconf(_SC_CHILD_MAX); - if (!child_max) /* paranoia */ -#endif - { - /* Be inventive */ - child_max = 1024L; - } - } - - if (!bgstatus_list) { - bgstatus_list = znewlinklist(); - /* - * We're not always robust about memory failures, but - * this is pretty deep in the shell basics to be failing owing - * to memory, and a failure to wait is reported loudly, so test - * and fail silently here. - */ - if (!bgstatus_list) - return; - } - if (bgstatus_count == child_max) { - /* Overflow. List is in order, remove first */ - rembgstatus(firstnode(bgstatus_list)); - } - bgstatus_entry = (Bgstatus)zalloc(sizeof(*bgstatus_entry)); - if (!bgstatus_entry) { - /* See note above */ - return; - } - bgstatus_entry->pid = pid; - bgstatus_entry->status = status; - if (!zaddlinknode(bgstatus_list, bgstatus_entry)) { - zfree(bgstatus_entry, sizeof(*bgstatus_entry)); - return; - } - bgstatus_count++; -} - -/* - * See if pid has a recorded exit status. - * Note we make no guarantee that the PIDs haven't wrapped, so this - * may not be the right process. - * - * This is only used by wait, which must only work on each - * pid once, so we need to remove the entry if we find it. - */ - -static int getbgstatus(pid_t pid) -{ - LinkNode node; - Bgstatus bgstatus_entry; - - if (!bgstatus_list) - return -1; - for (node = firstnode(bgstatus_list); node; incnode(node)) { - bgstatus_entry = (Bgstatus)getdata(node); - if (bgstatus_entry->pid == pid) { - int status = bgstatus_entry->status; - rembgstatus(node); - return status; - } - } - return -1; -} - -/* bg, disown, fg, jobs, wait: most of the job control commands are * - * here. They all take the same type of argument. Exception: wait can * - * take a pid or a job specifier, whereas the others only work on jobs. */ - -/**/ -int -bin_fg(char *name, char **argv, Options ops, int func) -{ - int job, lng, firstjob = -1, retval = 0, ofunc = func; - - if (OPT_ISSET(ops,'Z')) { - int len; - - if(isset(RESTRICTED)) { - zwarnnam(name, "-Z is restricted"); - return 1; - } - if(!argv[0] || argv[1]) { - zwarnnam(name, "-Z requires one argument"); - return 1; - } - queue_signals(); - unmetafy(*argv, &len); -#ifdef HAVE_SETPROCTITLE - setproctitle("%s", *argv); -#else - if(len > hackspace) - len = hackspace; - memcpy(hackzero, *argv, len); - memset(hackzero + len, 0, hackspace - len); -#endif - unqueue_signals(); - return 0; - } - - if (func == BIN_JOBS) { - lng = (OPT_ISSET(ops,'l')) ? 1 : (OPT_ISSET(ops,'p')) ? 2 : 0; - if (OPT_ISSET(ops,'d')) - lng |= 4; - } else { - lng = !!isset(LONGLISTJOBS); - } - - if ((func == BIN_FG || func == BIN_BG) && !jobbing) { - /* oops... maybe bg and fg should have been disabled? */ - zwarnnam(name, "no job control in this shell."); - return 1; - } - - queue_signals(); - /* - * In case any processes changed state recently, wait for them. - * This updates stopped processes (but we should have been - * signalled about those, up to inevitable races), and also - * continued processes if that feature is available. - */ - wait_for_processes(); - - /* If necessary, update job table. */ - if (unset(NOTIFY)) - scanjobs(); - - if (func != BIN_JOBS || isset(MONITOR) || !oldmaxjob) - setcurjob(); - - if (func == BIN_JOBS) - /* If you immediately type "exit" after "jobs", this * - * will prevent zexit from complaining about stopped jobs */ - stopmsg = 2; - if (!*argv) { - /* This block handles all of the default cases (no arguments). bg, - fg and disown act on the current job, and jobs and wait act on all the - jobs. */ - if (func == BIN_FG || func == BIN_BG || func == BIN_DISOWN) { - /* W.r.t. the above comment, we'd better have a current job at this - point or else. */ - if (curjob == -1 || (jobtab[curjob].stat & STAT_NOPRINT)) { - zwarnnam(name, "no current job"); - unqueue_signals(); - return 1; - } - firstjob = curjob; - } else if (func == BIN_JOBS) { - /* List jobs. */ - struct job *jobptr; - int curmaxjob, ignorejob; - if (unset(MONITOR) && oldmaxjob) { - jobptr = oldjobtab; - curmaxjob = oldmaxjob ? oldmaxjob - 1 : 0; - ignorejob = 0; - } else { - jobptr = jobtab; - curmaxjob = maxjob; - ignorejob = thisjob; - } - for (job = 0; job <= curmaxjob; job++, jobptr++) - if (job != ignorejob && jobptr->stat) { - if ((!OPT_ISSET(ops,'r') && !OPT_ISSET(ops,'s')) || - (OPT_ISSET(ops,'r') && OPT_ISSET(ops,'s')) || - (OPT_ISSET(ops,'r') && - !(jobptr->stat & STAT_STOPPED)) || - (OPT_ISSET(ops,'s') && jobptr->stat & STAT_STOPPED)) - printjob(jobptr, lng, 2); - } - unqueue_signals(); - return 0; - } else { /* Must be BIN_WAIT, so wait for all jobs */ - for (job = 0; job <= maxjob; job++) - if (job != thisjob && jobtab[job].stat && - !(jobtab[job].stat & STAT_NOPRINT)) - retval = zwaitjob(job, 1); - unqueue_signals(); - return retval; - } - } - - /* Defaults have been handled. We now have an argument or two, or three... - In the default case for bg, fg and disown, the argument will be provided by - the above routine. We now loop over the arguments. */ - for (; (firstjob != -1) || *argv; (void)(*argv && argv++)) { - int stopped, ocj = thisjob, jstat; - - func = ofunc; - - if (func == BIN_WAIT && isanum(*argv)) { - /* wait can take a pid; the others can't. */ - pid_t pid = (long)atoi(*argv); - Job j; - Process p; - - if (findproc(pid, &j, &p, 0)) { - if (j->stat & STAT_STOPPED) { - retval = (killjb(j, SIGCONT) != 0); - if (retval == 0) - makerunning(j); - } - if (retval == 0) { - /* - * returns 0 for normal exit, else signal+128 - * in which case we should return that status. - */ - retval = waitforpid(pid, 1); - } - if (retval == 0) { - if ((retval = getbgstatus(pid)) < 0) { - retval = lastval2; - } - } - } else if ((retval = getbgstatus(pid)) < 0) { - zwarnnam(name, "pid %d is not a child of this shell", pid); - /* presumably lastval2 doesn't tell us a heck of a lot? */ - retval = 1; - } - thisjob = ocj; - continue; - } - if (func != BIN_JOBS && oldjobtab != NULL) { - zwarnnam(name, "can't manipulate jobs in subshell"); - unqueue_signals(); - return 1; - } - /* The only type of argument allowed now is a job spec. Check it. */ - job = (*argv) ? getjob(*argv, name) : firstjob; - firstjob = -1; - if (job == -1) { - retval = 1; - break; - } - jstat = oldjobtab ? oldjobtab[job].stat : jobtab[job].stat; - if (!(jstat & STAT_INUSE) || - (jstat & STAT_NOPRINT)) { - zwarnnam(name, "%s: no such job", *argv); - unqueue_signals(); - return 1; - } - /* If AUTO_CONTINUE is set (automatically make stopped jobs running - * on disown), we actually do a bg and then delete the job table entry. */ - - if (isset(AUTOCONTINUE) && func == BIN_DISOWN && - jstat & STAT_STOPPED) - func = BIN_BG; - - /* We have a job number. Now decide what to do with it. */ - switch (func) { - case BIN_FG: - case BIN_BG: - case BIN_WAIT: - if (func == BIN_BG) { - jobtab[job].stat |= STAT_NOSTTY; - jobtab[job].stat &= ~STAT_CURSH; - } - if ((stopped = (jobtab[job].stat & STAT_STOPPED))) { - makerunning(jobtab + job); - if (func == BIN_BG) { - /* Set $! to indicate this was backgrounded */ - Process pn = jobtab[job].procs; - for (;;) { - Process next = pn->next; - if (!next) { - lastpid = (zlong) pn->pid; - break; - } - pn = next; - } - } - } else if (func == BIN_BG) { - /* Silly to bg a job already running. */ - zwarnnam(name, "job already in background"); - thisjob = ocj; - unqueue_signals(); - return 1; - } - /* It's time to shuffle the jobs around! Reset the current job, - and pick a sensible secondary job. */ - if (curjob == job) { - curjob = prevjob; - prevjob = (func == BIN_BG) ? -1 : job; - } - if (prevjob == job || prevjob == -1) - setprevjob(); - if (curjob == -1) { - curjob = prevjob; - setprevjob(); - } - if (func != BIN_WAIT) - /* for bg and fg -- show the job we are operating on */ - printjob(jobtab + job, (stopped) ? -1 : lng, 3); - if (func != BIN_BG) { /* fg or wait */ - if (jobtab[job].pwd && strcmp(jobtab[job].pwd, pwd)) { - FILE *fout = (func == BIN_JOBS || !shout) ? stdout : shout; - fprintf(fout, "(pwd : "); - fprintdir(jobtab[job].pwd, fout); - fprintf(fout, ")\n"); - fflush(fout); - } - if (func != BIN_WAIT) { /* fg */ - thisjob = job; - if ((jobtab[job].stat & STAT_SUPERJOB) && - ((!jobtab[job].procs->next || - (jobtab[job].stat & STAT_SUBLEADER) || - killpg(jobtab[job].gleader, 0) == -1)) && - jobtab[jobtab[job].other].gleader) - attachtty(jobtab[jobtab[job].other].gleader); - else - attachtty(jobtab[job].gleader); - } - } - if (stopped) { - if (func != BIN_BG && jobtab[job].ty) - settyinfo(jobtab[job].ty); - killjb(jobtab + job, SIGCONT); - } - if (func == BIN_WAIT) - { - retval = zwaitjob(job, 1); - if (!retval) - retval = lastval2; - } - else if (func != BIN_BG) { - /* - * HERE: there used not to be an "else" above. How - * could it be right to wait for the foreground job - * when we've just been told to wait for another - * job (and done it)? - */ - waitjobs(); - retval = lastval2; - } else if (ofunc == BIN_DISOWN) - deletejob(jobtab + job, 1); - break; - case BIN_JOBS: - printjob(job + (oldjobtab ? oldjobtab : jobtab), lng, 2); - break; - case BIN_DISOWN: - if (jobtab[job].stat & STAT_SUPERJOB) { - jobtab[job].stat |= STAT_DISOWN; - continue; - } - if (jobtab[job].stat & STAT_STOPPED) { - char buf[20], *pids = ""; - - if (jobtab[job].stat & STAT_SUPERJOB) { - Process pn; - - for (pn = jobtab[jobtab[job].other].procs; pn; pn = pn->next) { - sprintf(buf, " -%d", pn->pid); - pids = dyncat(pids, buf); - } - for (pn = jobtab[job].procs; pn->next; pn = pn->next) { - sprintf(buf, " %d", pn->pid); - pids = dyncat(pids, buf); - } - if (!jobtab[jobtab[job].other].procs && pn) { - sprintf(buf, " %d", pn->pid); - pids = dyncat(pids, buf); - } - } else { - sprintf(buf, " -%d", jobtab[job].gleader); - pids = buf; - } - zwarnnam(name, -#ifdef USE_SUSPENDED - "warning: job is suspended, use `kill -CONT%s' to resume", -#else - "warning: job is stopped, use `kill -CONT%s' to resume", -#endif - pids); - } - deletejob(jobtab + job, 1); - break; - } - thisjob = ocj; - } - unqueue_signals(); - return retval; -} - -static const struct { - const char *name; - int num; -} alt_sigs[] = { -#if defined(SIGCHLD) && defined(SIGCLD) -#if SIGCHLD == SIGCLD - { "CLD", SIGCLD }, -#endif -#endif -#if defined(SIGPOLL) && defined(SIGIO) -#if SIGPOLL == SIGIO - { "IO", SIGIO }, -#endif -#endif -#if !defined(SIGERR) - /* - * If SIGERR is not defined by the operating system, use it - * as an alias for SIGZERR. - */ - { "ERR", SIGZERR }, -#endif - { NULL, 0 } -}; - -/* kill: send a signal to a process. The process(es) may be specified * - * by job specifier (see above) or pid. A signal, defaulting to * - * SIGTERM, may be specified by name or number, preceded by a dash. */ - -/**/ -int -bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) -{ - int sig = SIGTERM; - int returnval = 0; - - /* check for, and interpret, a signal specifier */ - if (*argv && **argv == '-') { - if (idigit((*argv)[1])) { - char *endp; - /* signal specified by number */ - sig = zstrtol(*argv + 1, &endp, 10); - if (*endp) { - zwarnnam(nam, "invalid signal number: %s", *argv); - return 1; - } - } else if ((*argv)[1] != '-' || (*argv)[2]) { - char *signame; - - /* with argument "-l" display the list of signal names */ - if ((*argv)[1] == 'l' && (*argv)[2] == '\0') { - if (argv[1]) { - while (*++argv) { - sig = zstrtol(*argv, &signame, 10); - if (signame == *argv) { - if (!strncmp(signame, "SIG", 3)) - signame += 3; - for (sig = 1; sig <= SIGCOUNT; sig++) - if (!strcasecmp(sigs[sig], signame)) - break; - if (sig > SIGCOUNT) { - int i; - - for (i = 0; alt_sigs[i].name; i++) - if (!strcasecmp(alt_sigs[i].name, signame)) - { - sig = alt_sigs[i].num; - break; - } - } - if (sig > SIGCOUNT) { - zwarnnam(nam, "unknown signal: SIG%s", - signame); - returnval++; - } else - printf("%d\n", sig); - } else { - if (*signame) { - zwarnnam(nam, "unknown signal: SIG%s", - signame); - returnval++; - } else { - if (WIFSIGNALED(sig)) - sig = WTERMSIG(sig); - else if (WIFSTOPPED(sig)) - sig = WSTOPSIG(sig); - if (1 <= sig && sig <= SIGCOUNT) - printf("%s\n", sigs[sig]); - else - printf("%d\n", sig); - } - } - } - return returnval; - } - printf("%s", sigs[1]); - for (sig = 2; sig <= SIGCOUNT; sig++) - printf(" %s", sigs[sig]); - putchar('\n'); - return 0; - } - - if ((*argv)[1] == 'n' && (*argv)[2] == '\0') { - char *endp; - - if (!*++argv) { - zwarnnam(nam, "-n: argument expected"); - return 1; - } - sig = zstrtol(*argv, &endp, 10); - if (*endp) { - zwarnnam(nam, "invalid signal number: %s", *argv); - return 1; - } - } else { - if (!((*argv)[1] == 's' && (*argv)[2] == '\0')) - signame = *argv + 1; - else if (!(*++argv)) { - zwarnnam(nam, "-s: argument expected"); - return 1; - } else - signame = *argv; - if (!*signame) { - zwarnnam(nam, "-: signal name expected"); - return 1; - } - signame = casemodify(signame, CASMOD_UPPER); - if (!strncmp(signame, "SIG", 3)) - signame+=3; - - /* check for signal matching specified name */ - for (sig = 1; sig <= SIGCOUNT; sig++) - if (!strcmp(*(sigs + sig), signame)) - break; - if (*signame == '0' && !signame[1]) - sig = 0; - if (sig > SIGCOUNT) { - int i; - - for (i = 0; alt_sigs[i].name; i++) - if (!strcmp(alt_sigs[i].name, signame)) - { - sig = alt_sigs[i].num; - break; - } - } - if (sig > SIGCOUNT) { - zwarnnam(nam, "unknown signal: SIG%s", signame); - zwarnnam(nam, "type kill -l for a list of signals"); - return 1; - } - } - } - argv++; - } - - /* Discard the standard "-" and "--" option breaks */ - if (*argv && (*argv)[0] == '-' && (!(*argv)[1] || (*argv)[1] == '-')) - argv++; - - if (!*argv) { - zwarnnam(nam, "not enough arguments"); - return 1; - } - - queue_signals(); - setcurjob(); - - /* Remaining arguments specify processes. Loop over them, and send the - signal (number sig) to each process. */ - for (; *argv; argv++) { - if (**argv == '%') { - /* job specifier introduced by '%' */ - int p; - - if ((p = getjob(*argv, nam)) == -1) { - returnval++; - continue; - } - if (killjb(jobtab + p, sig) == -1) { - zwarnnam("kill", "kill %s failed: %e", *argv, errno); - returnval++; - continue; - } - /* automatically update the job table if sending a SIGCONT to a - job, and send the job a SIGCONT if sending it a non-stopping - signal. */ - if (jobtab[p].stat & STAT_STOPPED) { -#ifndef WIFCONTINUED - /* With WIFCONTINUED we find this out properly */ - if (sig == SIGCONT) - makerunning(jobtab + p); -#endif - if (sig != SIGKILL && sig != SIGCONT && sig != SIGTSTP - && sig != SIGTTOU && sig != SIGTTIN && sig != SIGSTOP) - killjb(jobtab + p, SIGCONT); - } - } else if (!isanum(*argv)) { - zwarnnam("kill", "illegal pid: %s", *argv); - returnval++; - } else { - int pid = atoi(*argv); - if (kill(pid, sig) == -1) { - zwarnnam("kill", "kill %s failed: %e", *argv, errno); - returnval++; - } -#ifndef WIFCONTINUED - else if (sig == SIGCONT) { - Job jn; - Process pn; - /* With WIFCONTINUED we find this out properly */ - if (findproc(pid, &jn, &pn, 0)) { - if (WIFSTOPPED(pn->status)) - pn->status = SP_RUNNING; - } - } -#endif - } - } - unqueue_signals(); - - return returnval < 126 ? returnval : 1; -} -/* Get a signal number from a string */ - -/**/ -mod_export int -getsignum(const char *s) -{ - int x, i; - - /* check for a signal specified by number */ - x = atoi(s); - if (idigit(*s) && x >= 0 && x < VSIGCOUNT) - return x; - - /* search for signal by name */ - if (!strncmp(s, "SIG", 3)) - s += 3; - - for (i = 0; i < VSIGCOUNT; i++) - if (!strcmp(s, sigs[i])) - return i; - - for (i = 0; alt_sigs[i].name; i++) - { - if (!strcmp(s, alt_sigs[i].name)) - return alt_sigs[i].num; - } - - /* no matching signal */ - return -1; -} - -/* Get the name for a signal. */ - -/**/ -mod_export const char * -getsigname(int sig) -{ - if (sigtrapped[sig] & ZSIG_ALIAS) - { - int i; - for (i = 0; alt_sigs[i].name; i++) - if (sig == alt_sigs[i].num) - return alt_sigs[i].name; - } - else - return sigs[sig]; - - /* shouldn't reach here */ -#ifdef DEBUG - dputs("Bad alias flag for signal"); -#endif - return ""; -} - - -/* Get the function node for a trap, taking care about alternative names */ -/**/ -HashNode -gettrapnode(int sig, int ignoredisable) -{ - char fname[20]; - HashNode hn; - HashNode (*getptr)(HashTable ht, const char *name); - int i; - if (ignoredisable) - getptr = shfunctab->getnode2; - else - getptr = shfunctab->getnode; - - sprintf(fname, "TRAP%s", sigs[sig]); - if ((hn = getptr(shfunctab, fname))) - return hn; - - for (i = 0; alt_sigs[i].name; i++) { - if (alt_sigs[i].num == sig) { - sprintf(fname, "TRAP%s", alt_sigs[i].name); - if ((hn = getptr(shfunctab, fname))) - return hn; - } - } - - return NULL; -} - -/* Remove a TRAP function under any name for the signal */ - -/**/ -void -removetrapnode(int sig) -{ - HashNode hn = gettrapnode(sig, 1); - if (hn) { - shfunctab->removenode(shfunctab, hn->nam); - shfunctab->freenode(hn); - } -} - -/* Suspend this shell */ - -/**/ -int -bin_suspend(char *name, UNUSED(char **argv), Options ops, UNUSED(int func)) -{ - /* won't suspend a login shell, unless forced */ - if (islogin && !OPT_ISSET(ops,'f')) { - zwarnnam(name, "can't suspend login shell"); - return 1; - } - if (jobbing) { - /* stop ignoring signals */ - signal_default(SIGTTIN); - signal_default(SIGTSTP); - signal_default(SIGTTOU); - - /* Move ourselves back to the process group we came from */ - release_pgrp(); - } - - /* suspend ourselves with a SIGTSTP */ - killpg(origpgrp, SIGTSTP); - - if (jobbing) { - acquire_pgrp(); - /* restore signal handling */ - signal_ignore(SIGTTOU); - signal_ignore(SIGTSTP); - signal_ignore(SIGTTIN); - } - return 0; -} - -/* find a job named s */ - -/**/ -int -findjobnam(const char *s) -{ - int jobnum; - - for (jobnum = maxjob; jobnum >= 0; jobnum--) - if (!(jobtab[jobnum].stat & (STAT_SUBJOB | STAT_NOPRINT)) && - jobtab[jobnum].stat && jobtab[jobnum].procs && jobnum != thisjob && - jobtab[jobnum].procs->text[0] && strpfx(s, jobtab[jobnum].procs->text)) - return jobnum; - return -1; -} - - -/* make sure we are a process group leader by creating a new process - group if necessary */ - -/**/ -void -acquire_pgrp(void) -{ - long ttpgrp; - sigset_t blockset, oldset; - - if ((mypgrp = GETPGRP()) >= 0) { - long lastpgrp = mypgrp; - sigemptyset(&blockset); - sigaddset(&blockset, SIGTTIN); - sigaddset(&blockset, SIGTTOU); - sigaddset(&blockset, SIGTSTP); - oldset = signal_block(blockset); - while ((ttpgrp = gettygrp()) != -1 && ttpgrp != mypgrp) { - mypgrp = GETPGRP(); - if (mypgrp == mypid) { - if (!interact) - break; /* attachtty() will be a no-op, give up */ - signal_setmask(oldset); - attachtty(mypgrp); /* Might generate SIGT* */ - signal_block(blockset); - } - if (mypgrp == gettygrp()) - break; - signal_setmask(oldset); - if (read(0, NULL, 0) != 0) {} /* Might generate SIGT* */ - signal_block(blockset); - mypgrp = GETPGRP(); - if (mypgrp == lastpgrp && !interact) - break; /* Unlikely that pgrp will ever change */ - lastpgrp = mypgrp; - } - if (mypgrp != mypid) { - if (setpgrp(0, 0) == 0) { - mypgrp = mypid; - attachtty(mypgrp); - } else - opts[MONITOR] = 0; - } - signal_setmask(oldset); - } else - opts[MONITOR] = 0; -} - -/* revert back to the process group we came from (before acquire_pgrp) */ - -/**/ -void -release_pgrp(void) -{ - if (origpgrp != mypgrp) { - /* in linux pid namespaces, origpgrp may never have been set */ - if (origpgrp) { - attachtty(origpgrp); - setpgrp(0, origpgrp); - } - mypgrp = origpgrp; - } -} |
