ash: cleanup part 5
diff --git a/shell/ash.c b/shell/ash.c
index 7ffecf4..54fc8ea 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -175,6 +175,18 @@
static volatile sig_atomic_t pendingsigs;
/*
+ * Sigmode records the current value of the signal handlers for the various
+ * modes. A value of zero means that the current handler is not known.
+ * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
+ */
+
+#define S_DFL 1 /* default signal handling (SIG_DFL) */
+#define S_CATCH 2 /* signal is caught */
+#define S_IGN 3 /* signal is ignored (SIG_IGN) */
+#define S_HARD_IGN 4 /* signal is ignored permenantly */
+#define S_RESET 5 /* temporary - to reset a hard ignored sig */
+
+/*
* These macros allow the user to suspend the handling of interrupt signals
* over a period of time. This is similar to SIGHOLD to or sigblock, but
* much more efficient and portable. (But hacking the kernel is so much
@@ -285,6 +297,34 @@
})
/* EXSIG is turned off by evalbltin(). */
+/*
+ * Ignore a signal. Only one usage site - in forkchild()
+ */
+static void
+ignoresig(int signo)
+{
+ if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
+ signal(signo, SIG_IGN);
+ }
+ sigmode[signo - 1] = S_HARD_IGN;
+}
+
+/*
+ * Signal handler. Only one usage site - in setsignal()
+ */
+static void
+onsig(int signo)
+{
+ gotsig[signo - 1] = 1;
+ pendingsigs = signo;
+
+ if (exsig || (signo == SIGINT && !trap[SIGINT])) {
+ if (!suppressint)
+ raise_interrupt();
+ intpending = 1;
+ }
+}
+
/* ============ stdout/stderr output */
@@ -3244,10 +3284,10 @@
#if ENABLE_ASH_MATH_SUPPORT_64
typedef int64_t arith_t;
-#define arith_t_type (long long)
+#define arith_t_type long long
#else
typedef long arith_t;
-#define arith_t_type (long)
+#define arith_t_type long
#endif
#if ENABLE_ASH_MATH_SUPPORT
@@ -3341,7 +3381,6 @@
static struct job *makejob(union node *, int);
static int forkshell(struct job *, union node *, int);
static int waitforjob(struct job *);
-static int stoppedjobs(void);
#if ! JOBS
#define setjobctl(on) /* do nothing */
@@ -3384,8 +3423,6 @@
static void clear_traps(void);
static void setsignal(int);
-static void ignoresig(int);
-static void onsig(int);
static int dotrap(void);
static void setinteractive(int);
static void exitshell(void) ATTRIBUTE_NORETURN;
@@ -7226,38 +7263,26 @@
#define DOWAIT_NORMAL 0
#define DOWAIT_BLOCK 1
-/* array of jobs */
-static struct job *jobtab;
-/* size of array */
-static unsigned njobs;
#if JOBS
/* pgrp of shell on invocation */
static int initialpgrp;
static int ttyfd = -1;
#endif
+/* array of jobs */
+static struct job *jobtab;
+/* size of array */
+static unsigned njobs;
/* current job */
static struct job *curjob;
/* number of presumed living untracked jobs */
static int jobless;
-static void set_curjob(struct job *, unsigned);
#if JOBS
-static int restartjob(struct job *, int);
-static void xtcsetpgrp(int, pid_t);
static char *commandtext(union node *);
static void cmdlist(union node *, int);
static void cmdtxt(union node *);
static void cmdputs(const char *);
-static void showpipe(struct job *, FILE *);
#endif
-static int sprint_status(char *, int, int);
-static void freejob(struct job *);
-static struct job *getjob(const char *, int);
-static struct job *growjobtab(void);
-static void forkchild(struct job *, union node *, int);
-static void forkparent(struct job *, union node *, int, pid_t);
-static int dowait(int, struct job *);
-static int getstatus(struct job *);
static void
set_curjob(struct job *jp, unsigned mode)
@@ -7307,7 +7332,126 @@
}
}
+#if JOBS || DEBUG
+static int
+jobno(const struct job *jp)
+{
+ return jp - jobtab + 1;
+}
+#endif
+
+/*
+ * Convert a job name to a job structure.
+ */
+static struct job *
+getjob(const char *name, int getctl)
+{
+ struct job *jp;
+ struct job *found;
+ const char *err_msg = "No such job: %s";
+ unsigned num;
+ int c;
+ const char *p;
+ char *(*match)(const char *, const char *);
+
+ jp = curjob;
+ p = name;
+ if (!p)
+ goto currentjob;
+
+ if (*p != '%')
+ goto err;
+
+ c = *++p;
+ if (!c)
+ goto currentjob;
+
+ if (!p[1]) {
+ if (c == '+' || c == '%') {
+ currentjob:
+ err_msg = "No current job";
+ goto check;
+ }
+ if (c == '-') {
+ if (jp)
+ jp = jp->prev_job;
+ err_msg = "No previous job";
+ check:
+ if (!jp)
+ goto err;
+ goto gotit;
+ }
+ }
+
+ if (is_number(p)) {
+ num = atoi(p);
+ if (num < njobs) {
+ jp = jobtab + num - 1;
+ if (jp->used)
+ goto gotit;
+ goto err;
+ }
+ }
+
+ match = prefix;
+ if (*p == '?') {
+ match = strstr;
+ p++;
+ }
+
+ found = 0;
+ while (1) {
+ if (!jp)
+ goto err;
+ if (match(jp->ps[0].cmd, p)) {
+ if (found)
+ goto err;
+ found = jp;
+ err_msg = "%s: ambiguous";
+ }
+ jp = jp->prev_job;
+ }
+
+ gotit:
#if JOBS
+ err_msg = "job %s not created under job control";
+ if (getctl && jp->jobctl == 0)
+ goto err;
+#endif
+ return jp;
+ err:
+ ash_msg_and_raise_error(err_msg, name);
+}
+
+/*
+ * Mark a job structure as unused.
+ */
+static void
+freejob(struct job *jp)
+{
+ struct procstat *ps;
+ int i;
+
+ INT_OFF;
+ for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
+ if (ps->cmd != nullstr)
+ free(ps->cmd);
+ }
+ if (jp->ps != &jp->ps0)
+ free(jp->ps);
+ jp->used = 0;
+ set_curjob(jp, CUR_DELETE);
+ INT_ON;
+}
+
+#if JOBS
+static void
+xtcsetpgrp(int fd, pid_t pgrp)
+{
+ if (tcsetpgrp(fd, pgrp))
+ ash_msg_and_raise_error("Cannot set tty process group (%m)");
+}
+
/*
* Turn job control on and off.
*
@@ -7401,7 +7545,7 @@
if (signo < 0) {
int c;
- while ((c = nextopt("ls:")) != '\0')
+ while ((c = nextopt("ls:")) != '\0') {
switch (c) {
default:
#if DEBUG
@@ -7420,6 +7564,7 @@
}
break;
}
+ }
argv = argptr;
} else
argv++;
@@ -7467,42 +7612,21 @@
return i;
}
-#endif /* JOBS */
-#if JOBS || DEBUG
-static int
-jobno(const struct job *jp)
+static void
+showpipe(struct job *jp, FILE *out)
{
- return jp - jobtab + 1;
-}
-#endif
+ struct procstat *sp;
+ struct procstat *spend;
-#if JOBS
-static int
-fg_bgcmd(int argc, char **argv)
-{
- struct job *jp;
- FILE *out;
- int mode;
- int retval;
-
- mode = (**argv == 'f') ? FORK_FG : FORK_BG;
- nextopt(nullstr);
- argv = argptr;
- out = stdout;
- do {
- jp = getjob(*argv, 1);
- if (mode == FORK_BG) {
- set_curjob(jp, CUR_RUNNING);
- fprintf(out, "[%d] ", jobno(jp));
- }
- outstr(jp->ps->cmd, out);
- showpipe(jp, out);
- retval = restartjob(jp, mode);
- } while (*argv && *++argv);
- return retval;
+ spend = jp->ps + jp->nprocs;
+ for (sp = jp->ps + 1; sp < spend; sp++)
+ fprintf(out, " | %s", sp->cmd);
+ outcslow('\n', out);
+ flush_stdout_stderr();
}
+
static int
restartjob(struct job *jp, int mode)
{
@@ -7531,6 +7655,31 @@
INT_ON;
return status;
}
+
+static int
+fg_bgcmd(int argc, char **argv)
+{
+ struct job *jp;
+ FILE *out;
+ int mode;
+ int retval;
+
+ mode = (**argv == 'f') ? FORK_FG : FORK_BG;
+ nextopt(nullstr);
+ argv = argptr;
+ out = stdout;
+ do {
+ jp = getjob(*argv, 1);
+ if (mode == FORK_BG) {
+ set_curjob(jp, CUR_RUNNING);
+ fprintf(out, "[%d] ", jobno(jp));
+ }
+ outstr(jp->ps->cmd, out);
+ showpipe(jp, out);
+ retval = restartjob(jp, mode);
+ } while (*argv && *++argv);
+ return retval;
+}
#endif
static int
@@ -7571,552 +7720,6 @@
return col;
}
-#if JOBS
-static void
-showjob(FILE *out, struct job *jp, int mode)
-{
- struct procstat *ps;
- struct procstat *psend;
- int col;
- int indent;
- char s[80];
-
- ps = jp->ps;
-
- if (mode & SHOW_PGID) {
- /* just output process (group) id of pipeline */
- fprintf(out, "%d\n", ps->pid);
- return;
- }
-
- col = fmtstr(s, 16, "[%d] ", jobno(jp));
- indent = col;
-
- if (jp == curjob)
- s[col - 2] = '+';
- else if (curjob && jp == curjob->prev_job)
- s[col - 2] = '-';
-
- if (mode & SHOW_PID)
- col += fmtstr(s + col, 16, "%d ", ps->pid);
-
- psend = ps + jp->nprocs;
-
- if (jp->state == JOBRUNNING) {
- strcpy(s + col, "Running");
- col += sizeof("Running") - 1;
- } else {
- int status = psend[-1].status;
-#if JOBS
- if (jp->state == JOBSTOPPED)
- status = jp->stopstatus;
-#endif
- col += sprint_status(s + col, status, 0);
- }
-
- goto start;
-
- do {
- /* for each process */
- col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
- start:
- fprintf(out, "%s%*c%s",
- s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
- );
- if (!(mode & SHOW_PID)) {
- showpipe(jp, out);
- break;
- }
- if (++ps == psend) {
- outcslow('\n', out);
- break;
- }
- } while (1);
-
- jp->changed = 0;
-
- if (jp->state == JOBDONE) {
- TRACE(("showjob: freeing job %d\n", jobno(jp)));
- freejob(jp);
- }
-}
-
-
-static int
-jobscmd(int argc, char **argv)
-{
- int mode, m;
- FILE *out;
-
- mode = 0;
- while ((m = nextopt("lp"))) {
- if (m == 'l')
- mode = SHOW_PID;
- else
- mode = SHOW_PGID;
- }
-
- out = stdout;
- argv = argptr;
- if (*argv) {
- do
- showjob(out, getjob(*argv,0), mode);
- while (*++argv);
- } else
- showjobs(out, mode);
-
- return 0;
-}
-
-
-/*
- * Print a list of jobs. If "change" is nonzero, only print jobs whose
- * statuses have changed since the last call to showjobs.
- */
-static void
-showjobs(FILE *out, int mode)
-{
- struct job *jp;
-
- TRACE(("showjobs(%x) called\n", mode));
-
- /* If not even one one job changed, there is nothing to do */
- while (dowait(DOWAIT_NORMAL, NULL) > 0)
- continue;
-
- for (jp = curjob; jp; jp = jp->prev_job) {
- if (!(mode & SHOW_CHANGED) || jp->changed)
- showjob(out, jp, mode);
- }
-}
-#endif /* JOBS */
-
-
-/*
- * Mark a job structure as unused.
- */
-static void
-freejob(struct job *jp)
-{
- struct procstat *ps;
- int i;
-
- INT_OFF;
- for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
- if (ps->cmd != nullstr)
- free(ps->cmd);
- }
- if (jp->ps != &jp->ps0)
- free(jp->ps);
- jp->used = 0;
- set_curjob(jp, CUR_DELETE);
- INT_ON;
-}
-
-
-static int
-waitcmd(int argc, char **argv)
-{
- struct job *job;
- int retval;
- struct job *jp;
-
- EXSIGON;
-
- nextopt(nullstr);
- retval = 0;
-
- argv = argptr;
- if (!*argv) {
- /* wait for all jobs */
- for (;;) {
- jp = curjob;
- while (1) {
- if (!jp) {
- /* no running procs */
- goto out;
- }
- if (jp->state == JOBRUNNING)
- break;
- jp->waited = 1;
- jp = jp->prev_job;
- }
- dowait(DOWAIT_BLOCK, 0);
- }
- }
-
- retval = 127;
- do {
- if (**argv != '%') {
- pid_t pid = number(*argv);
- job = curjob;
- goto start;
- do {
- if (job->ps[job->nprocs - 1].pid == pid)
- break;
- job = job->prev_job;
- start:
- if (!job)
- goto repeat;
- } while (1);
- } else
- job = getjob(*argv, 0);
- /* loop until process terminated or stopped */
- while (job->state == JOBRUNNING)
- dowait(DOWAIT_BLOCK, 0);
- job->waited = 1;
- retval = getstatus(job);
- repeat:
- ;
- } while (*++argv);
-
- out:
- return retval;
-}
-
-
-/*
- * Convert a job name to a job structure.
- */
-static struct job *
-getjob(const char *name, int getctl)
-{
- struct job *jp;
- struct job *found;
- const char *err_msg = "No such job: %s";
- unsigned num;
- int c;
- const char *p;
- char *(*match)(const char *, const char *);
-
- jp = curjob;
- p = name;
- if (!p)
- goto currentjob;
-
- if (*p != '%')
- goto err;
-
- c = *++p;
- if (!c)
- goto currentjob;
-
- if (!p[1]) {
- if (c == '+' || c == '%') {
- currentjob:
- err_msg = "No current job";
- goto check;
- }
- if (c == '-') {
- if (jp)
- jp = jp->prev_job;
- err_msg = "No previous job";
- check:
- if (!jp)
- goto err;
- goto gotit;
- }
- }
-
- if (is_number(p)) {
- num = atoi(p);
- if (num < njobs) {
- jp = jobtab + num - 1;
- if (jp->used)
- goto gotit;
- goto err;
- }
- }
-
- match = prefix;
- if (*p == '?') {
- match = strstr;
- p++;
- }
-
- found = 0;
- while (1) {
- if (!jp)
- goto err;
- if (match(jp->ps[0].cmd, p)) {
- if (found)
- goto err;
- found = jp;
- err_msg = "%s: ambiguous";
- }
- jp = jp->prev_job;
- }
-
- gotit:
-#if JOBS
- err_msg = "job %s not created under job control";
- if (getctl && jp->jobctl == 0)
- goto err;
-#endif
- return jp;
- err:
- ash_msg_and_raise_error(err_msg, name);
-}
-
-
-/*
- * Return a new job structure.
- * Called with interrupts off.
- */
-static struct job *
-makejob(union node *node, int nprocs)
-{
- int i;
- struct job *jp;
-
- for (i = njobs, jp = jobtab; ; jp++) {
- if (--i < 0) {
- jp = growjobtab();
- break;
- }
- if (jp->used == 0)
- break;
- if (jp->state != JOBDONE || !jp->waited)
- continue;
-#if JOBS
- if (jobctl)
- continue;
-#endif
- freejob(jp);
- break;
- }
- memset(jp, 0, sizeof(*jp));
-#if JOBS
- if (jobctl)
- jp->jobctl = 1;
-#endif
- jp->prev_job = curjob;
- curjob = jp;
- jp->used = 1;
- jp->ps = &jp->ps0;
- if (nprocs > 1) {
- jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
- }
- TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
- jobno(jp)));
- return jp;
-}
-
-static struct job *
-growjobtab(void)
-{
- size_t len;
- ptrdiff_t offset;
- struct job *jp, *jq;
-
- len = njobs * sizeof(*jp);
- jq = jobtab;
- jp = ckrealloc(jq, len + 4 * sizeof(*jp));
-
- offset = (char *)jp - (char *)jq;
- if (offset) {
- /* Relocate pointers */
- size_t l = len;
-
- jq = (struct job *)((char *)jq + l);
- while (l) {
- l -= sizeof(*jp);
- jq--;
-#define joff(p) ((struct job *)((char *)(p) + l))
-#define jmove(p) (p) = (void *)((char *)(p) + offset)
- if (xlikely(joff(jp)->ps == &jq->ps0))
- jmove(joff(jp)->ps);
- if (joff(jp)->prev_job)
- jmove(joff(jp)->prev_job);
- }
- if (curjob)
- jmove(curjob);
-#undef joff
-#undef jmove
- }
-
- njobs += 4;
- jobtab = jp;
- jp = (struct job *)((char *)jp + len);
- jq = jp + 3;
- do {
- jq->used = 0;
- } while (--jq >= jp);
- return jp;
-}
-
-
-/*
- * Fork off a subshell. If we are doing job control, give the subshell its
- * own process group. Jp is a job structure that the job is to be added to.
- * N is the command that will be evaluated by the child. Both jp and n may
- * be NULL. The mode parameter can be one of the following:
- * FORK_FG - Fork off a foreground process.
- * FORK_BG - Fork off a background process.
- * FORK_NOJOB - Like FORK_FG, but don't give the process its own
- * process group even if job control is on.
- *
- * When job control is turned off, background processes have their standard
- * input redirected to /dev/null (except for the second and later processes
- * in a pipeline).
- *
- * Called with interrupts off.
- */
-static void forkchild(struct job *jp, union node *n, int mode)
-{
- int oldlvl;
-
- TRACE(("Child shell %d\n", getpid()));
- oldlvl = shlvl;
- shlvl++;
-
- closescript();
- clear_traps();
-#if JOBS
- /* do job control only in root shell */
- jobctl = 0;
- if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
- pid_t pgrp;
-
- if (jp->nprocs == 0)
- pgrp = getpid();
- else
- pgrp = jp->ps[0].pid;
- /* This can fail because we are doing it in the parent also */
- (void)setpgid(0, pgrp);
- if (mode == FORK_FG)
- xtcsetpgrp(ttyfd, pgrp);
- setsignal(SIGTSTP);
- setsignal(SIGTTOU);
- } else
-#endif
- if (mode == FORK_BG) {
- ignoresig(SIGINT);
- ignoresig(SIGQUIT);
- if (jp->nprocs == 0) {
- close(0);
- if (open(bb_dev_null, O_RDONLY) != 0)
- ash_msg_and_raise_error("Can't open %s", bb_dev_null);
- }
- }
- if (!oldlvl && iflag) {
- setsignal(SIGINT);
- setsignal(SIGQUIT);
- setsignal(SIGTERM);
- }
- for (jp = curjob; jp; jp = jp->prev_job)
- freejob(jp);
- jobless = 0;
-}
-
-static void forkparent(struct job *jp, union node *n, int mode, pid_t pid)
-{
- TRACE(("In parent shell: child = %d\n", pid));
- if (!jp) {
- while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
- jobless++;
- return;
- }
-#if JOBS
- if (mode != FORK_NOJOB && jp->jobctl) {
- int pgrp;
-
- if (jp->nprocs == 0)
- pgrp = pid;
- else
- pgrp = jp->ps[0].pid;
- /* This can fail because we are doing it in the child also */
- setpgid(pid, pgrp);
- }
-#endif
- if (mode == FORK_BG) {
- backgndpid = pid; /* set $! */
- set_curjob(jp, CUR_RUNNING);
- }
- if (jp) {
- struct procstat *ps = &jp->ps[jp->nprocs++];
- ps->pid = pid;
- ps->status = -1;
- ps->cmd = nullstr;
-#if JOBS
- if (jobctl && n)
- ps->cmd = commandtext(n);
-#endif
- }
-}
-
-static int
-forkshell(struct job *jp, union node *n, int mode)
-{
- int pid;
-
- TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
- pid = fork();
- if (pid < 0) {
- TRACE(("Fork failed, errno=%d", errno));
- if (jp)
- freejob(jp);
- ash_msg_and_raise_error("Cannot fork");
- }
- if (pid == 0)
- forkchild(jp, n, mode);
- else
- forkparent(jp, n, mode, pid);
- return pid;
-}
-
-
-/*
- * Wait for job to finish.
- *
- * Under job control we have the problem that while a child process is
- * running interrupts generated by the user are sent to the child but not
- * to the shell. This means that an infinite loop started by an inter-
- * active user may be hard to kill. With job control turned off, an
- * interactive user may place an interactive program inside a loop. If
- * the interactive program catches interrupts, the user doesn't want
- * these interrupts to also abort the loop. The approach we take here
- * is to have the shell ignore interrupt signals while waiting for a
- * foreground process to terminate, and then send itself an interrupt
- * signal if the child process was terminated by an interrupt signal.
- * Unfortunately, some programs want to do a bit of cleanup and then
- * exit on interrupt; unless these processes terminate themselves by
- * sending a signal to themselves (instead of calling exit) they will
- * confuse this approach.
- *
- * Called with interrupts off.
- */
-static int
-waitforjob(struct job *jp)
-{
- int st;
-
- TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
- while (jp->state == JOBRUNNING) {
- dowait(DOWAIT_BLOCK, jp);
- }
- st = getstatus(jp);
-#if JOBS
- if (jp->jobctl) {
- xtcsetpgrp(ttyfd, rootpid);
- /*
- * This is truly gross.
- * If we're doing job control, then we did a TIOCSPGRP which
- * caused us (the shell) to no longer be in the controlling
- * session -- so we wouldn't have seen any ^C/SIGINT. So, we
- * intuit from the subprocess exit status whether a SIGINT
- * occurred, and if so interrupt ourselves. Yuck. - mycroft
- */
- if (jp->sigint)
- raise(SIGINT);
- }
- if (jp->state == JOBDONE)
-#endif
- freejob(jp);
- return st;
-}
-
-
/*
* Do a wait system call. If job control is compiled in, we accept
* stopped processes. If block is zero, we return a value of zero
@@ -8159,7 +7762,6 @@
return wait3(status, flags, (struct rusage *)NULL);
}
-
/*
* Wait for a process to terminate.
*/
@@ -8249,11 +7851,473 @@
return pid;
}
+#if JOBS
+static void
+showjob(FILE *out, struct job *jp, int mode)
+{
+ struct procstat *ps;
+ struct procstat *psend;
+ int col;
+ int indent;
+ char s[80];
+
+ ps = jp->ps;
+
+ if (mode & SHOW_PGID) {
+ /* just output process (group) id of pipeline */
+ fprintf(out, "%d\n", ps->pid);
+ return;
+ }
+
+ col = fmtstr(s, 16, "[%d] ", jobno(jp));
+ indent = col;
+
+ if (jp == curjob)
+ s[col - 2] = '+';
+ else if (curjob && jp == curjob->prev_job)
+ s[col - 2] = '-';
+
+ if (mode & SHOW_PID)
+ col += fmtstr(s + col, 16, "%d ", ps->pid);
+
+ psend = ps + jp->nprocs;
+
+ if (jp->state == JOBRUNNING) {
+ strcpy(s + col, "Running");
+ col += sizeof("Running") - 1;
+ } else {
+ int status = psend[-1].status;
+#if JOBS
+ if (jp->state == JOBSTOPPED)
+ status = jp->stopstatus;
+#endif
+ col += sprint_status(s + col, status, 0);
+ }
+
+ goto start;
+
+ do {
+ /* for each process */
+ col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
+ start:
+ fprintf(out, "%s%*c%s",
+ s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
+ );
+ if (!(mode & SHOW_PID)) {
+ showpipe(jp, out);
+ break;
+ }
+ if (++ps == psend) {
+ outcslow('\n', out);
+ break;
+ }
+ } while (1);
+
+ jp->changed = 0;
+
+ if (jp->state == JOBDONE) {
+ TRACE(("showjob: freeing job %d\n", jobno(jp)));
+ freejob(jp);
+ }
+}
+
+static int
+jobscmd(int argc, char **argv)
+{
+ int mode, m;
+ FILE *out;
+
+ mode = 0;
+ while ((m = nextopt("lp"))) {
+ if (m == 'l')
+ mode = SHOW_PID;
+ else
+ mode = SHOW_PGID;
+ }
+
+ out = stdout;
+ argv = argptr;
+ if (*argv) {
+ do
+ showjob(out, getjob(*argv,0), mode);
+ while (*++argv);
+ } else
+ showjobs(out, mode);
+
+ return 0;
+}
+
+/*
+ * Print a list of jobs. If "change" is nonzero, only print jobs whose
+ * statuses have changed since the last call to showjobs.
+ */
+static void
+showjobs(FILE *out, int mode)
+{
+ struct job *jp;
+
+ TRACE(("showjobs(%x) called\n", mode));
+
+ /* If not even one one job changed, there is nothing to do */
+ while (dowait(DOWAIT_NORMAL, NULL) > 0)
+ continue;
+
+ for (jp = curjob; jp; jp = jp->prev_job) {
+ if (!(mode & SHOW_CHANGED) || jp->changed)
+ showjob(out, jp, mode);
+ }
+}
+#endif /* JOBS */
+
+static int
+getstatus(struct job *job)
+{
+ int status;
+ int retval;
+
+ status = job->ps[job->nprocs - 1].status;
+ retval = WEXITSTATUS(status);
+ if (!WIFEXITED(status)) {
+#if JOBS
+ retval = WSTOPSIG(status);
+ if (!WIFSTOPPED(status))
+#endif
+ {
+ /* XXX: limits number of signals */
+ retval = WTERMSIG(status);
+#if JOBS
+ if (retval == SIGINT)
+ job->sigint = 1;
+#endif
+ }
+ retval += 128;
+ }
+ TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
+ jobno(job), job->nprocs, status, retval));
+ return retval;
+}
+
+static int
+waitcmd(int argc, char **argv)
+{
+ struct job *job;
+ int retval;
+ struct job *jp;
+
+ EXSIGON;
+
+ nextopt(nullstr);
+ retval = 0;
+
+ argv = argptr;
+ if (!*argv) {
+ /* wait for all jobs */
+ for (;;) {
+ jp = curjob;
+ while (1) {
+ if (!jp) {
+ /* no running procs */
+ goto out;
+ }
+ if (jp->state == JOBRUNNING)
+ break;
+ jp->waited = 1;
+ jp = jp->prev_job;
+ }
+ dowait(DOWAIT_BLOCK, 0);
+ }
+ }
+
+ retval = 127;
+ do {
+ if (**argv != '%') {
+ pid_t pid = number(*argv);
+ job = curjob;
+ goto start;
+ do {
+ if (job->ps[job->nprocs - 1].pid == pid)
+ break;
+ job = job->prev_job;
+ start:
+ if (!job)
+ goto repeat;
+ } while (1);
+ } else
+ job = getjob(*argv, 0);
+ /* loop until process terminated or stopped */
+ while (job->state == JOBRUNNING)
+ dowait(DOWAIT_BLOCK, 0);
+ job->waited = 1;
+ retval = getstatus(job);
+ repeat:
+ ;
+ } while (*++argv);
+
+ out:
+ return retval;
+}
+
+static struct job *
+growjobtab(void)
+{
+ size_t len;
+ ptrdiff_t offset;
+ struct job *jp, *jq;
+
+ len = njobs * sizeof(*jp);
+ jq = jobtab;
+ jp = ckrealloc(jq, len + 4 * sizeof(*jp));
+
+ offset = (char *)jp - (char *)jq;
+ if (offset) {
+ /* Relocate pointers */
+ size_t l = len;
+
+ jq = (struct job *)((char *)jq + l);
+ while (l) {
+ l -= sizeof(*jp);
+ jq--;
+#define joff(p) ((struct job *)((char *)(p) + l))
+#define jmove(p) (p) = (void *)((char *)(p) + offset)
+ if (xlikely(joff(jp)->ps == &jq->ps0))
+ jmove(joff(jp)->ps);
+ if (joff(jp)->prev_job)
+ jmove(joff(jp)->prev_job);
+ }
+ if (curjob)
+ jmove(curjob);
+#undef joff
+#undef jmove
+ }
+
+ njobs += 4;
+ jobtab = jp;
+ jp = (struct job *)((char *)jp + len);
+ jq = jp + 3;
+ do {
+ jq->used = 0;
+ } while (--jq >= jp);
+ return jp;
+}
+
+/*
+ * Return a new job structure.
+ * Called with interrupts off.
+ */
+static struct job *
+makejob(union node *node, int nprocs)
+{
+ int i;
+ struct job *jp;
+
+ for (i = njobs, jp = jobtab; ; jp++) {
+ if (--i < 0) {
+ jp = growjobtab();
+ break;
+ }
+ if (jp->used == 0)
+ break;
+ if (jp->state != JOBDONE || !jp->waited)
+ continue;
+#if JOBS
+ if (jobctl)
+ continue;
+#endif
+ freejob(jp);
+ break;
+ }
+ memset(jp, 0, sizeof(*jp));
+#if JOBS
+ if (jobctl)
+ jp->jobctl = 1;
+#endif
+ jp->prev_job = curjob;
+ curjob = jp;
+ jp->used = 1;
+ jp->ps = &jp->ps0;
+ if (nprocs > 1) {
+ jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
+ }
+ TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
+ jobno(jp)));
+ return jp;
+}
+
+/*
+ * Fork off a subshell. If we are doing job control, give the subshell its
+ * own process group. Jp is a job structure that the job is to be added to.
+ * N is the command that will be evaluated by the child. Both jp and n may
+ * be NULL. The mode parameter can be one of the following:
+ * FORK_FG - Fork off a foreground process.
+ * FORK_BG - Fork off a background process.
+ * FORK_NOJOB - Like FORK_FG, but don't give the process its own
+ * process group even if job control is on.
+ *
+ * When job control is turned off, background processes have their standard
+ * input redirected to /dev/null (except for the second and later processes
+ * in a pipeline).
+ *
+ * Called with interrupts off.
+ */
+static void
+forkchild(struct job *jp, union node *n, int mode)
+{
+ int oldlvl;
+
+ TRACE(("Child shell %d\n", getpid()));
+ oldlvl = shlvl;
+ shlvl++;
+
+ closescript();
+ clear_traps();
+#if JOBS
+ /* do job control only in root shell */
+ jobctl = 0;
+ if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
+ pid_t pgrp;
+
+ if (jp->nprocs == 0)
+ pgrp = getpid();
+ else
+ pgrp = jp->ps[0].pid;
+ /* This can fail because we are doing it in the parent also */
+ (void)setpgid(0, pgrp);
+ if (mode == FORK_FG)
+ xtcsetpgrp(ttyfd, pgrp);
+ setsignal(SIGTSTP);
+ setsignal(SIGTTOU);
+ } else
+#endif
+ if (mode == FORK_BG) {
+ ignoresig(SIGINT);
+ ignoresig(SIGQUIT);
+ if (jp->nprocs == 0) {
+ close(0);
+ if (open(bb_dev_null, O_RDONLY) != 0)
+ ash_msg_and_raise_error("Can't open %s", bb_dev_null);
+ }
+ }
+ if (!oldlvl && iflag) {
+ setsignal(SIGINT);
+ setsignal(SIGQUIT);
+ setsignal(SIGTERM);
+ }
+ for (jp = curjob; jp; jp = jp->prev_job)
+ freejob(jp);
+ jobless = 0;
+}
+
+static void
+forkparent(struct job *jp, union node *n, int mode, pid_t pid)
+{
+ TRACE(("In parent shell: child = %d\n", pid));
+ if (!jp) {
+ while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
+ jobless++;
+ return;
+ }
+#if JOBS
+ if (mode != FORK_NOJOB && jp->jobctl) {
+ int pgrp;
+
+ if (jp->nprocs == 0)
+ pgrp = pid;
+ else
+ pgrp = jp->ps[0].pid;
+ /* This can fail because we are doing it in the child also */
+ setpgid(pid, pgrp);
+ }
+#endif
+ if (mode == FORK_BG) {
+ backgndpid = pid; /* set $! */
+ set_curjob(jp, CUR_RUNNING);
+ }
+ if (jp) {
+ struct procstat *ps = &jp->ps[jp->nprocs++];
+ ps->pid = pid;
+ ps->status = -1;
+ ps->cmd = nullstr;
+#if JOBS
+ if (jobctl && n)
+ ps->cmd = commandtext(n);
+#endif
+ }
+}
+
+static int
+forkshell(struct job *jp, union node *n, int mode)
+{
+ int pid;
+
+ TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
+ pid = fork();
+ if (pid < 0) {
+ TRACE(("Fork failed, errno=%d", errno));
+ if (jp)
+ freejob(jp);
+ ash_msg_and_raise_error("Cannot fork");
+ }
+ if (pid == 0)
+ forkchild(jp, n, mode);
+ else
+ forkparent(jp, n, mode, pid);
+ return pid;
+}
+
+/*
+ * Wait for job to finish.
+ *
+ * Under job control we have the problem that while a child process is
+ * running interrupts generated by the user are sent to the child but not
+ * to the shell. This means that an infinite loop started by an inter-
+ * active user may be hard to kill. With job control turned off, an
+ * interactive user may place an interactive program inside a loop. If
+ * the interactive program catches interrupts, the user doesn't want
+ * these interrupts to also abort the loop. The approach we take here
+ * is to have the shell ignore interrupt signals while waiting for a
+ * foreground process to terminate, and then send itself an interrupt
+ * signal if the child process was terminated by an interrupt signal.
+ * Unfortunately, some programs want to do a bit of cleanup and then
+ * exit on interrupt; unless these processes terminate themselves by
+ * sending a signal to themselves (instead of calling exit) they will
+ * confuse this approach.
+ *
+ * Called with interrupts off.
+ */
+static int
+waitforjob(struct job *jp)
+{
+ int st;
+
+ TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
+ while (jp->state == JOBRUNNING) {
+ dowait(DOWAIT_BLOCK, jp);
+ }
+ st = getstatus(jp);
+#if JOBS
+ if (jp->jobctl) {
+ xtcsetpgrp(ttyfd, rootpid);
+ /*
+ * This is truly gross.
+ * If we're doing job control, then we did a TIOCSPGRP which
+ * caused us (the shell) to no longer be in the controlling
+ * session -- so we wouldn't have seen any ^C/SIGINT. So, we
+ * intuit from the subprocess exit status whether a SIGINT
+ * occurred, and if so interrupt ourselves. Yuck. - mycroft
+ */
+ if (jp->sigint)
+ raise(SIGINT);
+ }
+ if (jp->state == JOBDONE)
+#endif
+ freejob(jp);
+ return st;
+}
/*
* return 1 if there are stopped jobs, otherwise 0
*/
-int
+static int
stoppedjobs(void)
{
struct job *jp;
@@ -8289,7 +8353,7 @@
cmdtxt(n);
name = stackblock();
TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
- name, cmdnextc, cmdnextc));
+ name, cmdnextc, cmdnextc));
return ckstrdup(name);
}
@@ -8550,57 +8614,8 @@
*nextc = 0;
cmdnextc = nextc;
}
-
-
-static void
-showpipe(struct job *jp, FILE *out)
-{
- struct procstat *sp;
- struct procstat *spend;
-
- spend = jp->ps + jp->nprocs;
- for (sp = jp->ps + 1; sp < spend; sp++)
- fprintf(out, " | %s", sp->cmd);
- outcslow('\n', out);
- flush_stdout_stderr();
-}
-
-static void
-xtcsetpgrp(int fd, pid_t pgrp)
-{
- if (tcsetpgrp(fd, pgrp))
- ash_msg_and_raise_error("Cannot set tty process group (%m)");
-}
#endif /* JOBS */
-static int
-getstatus(struct job *job)
-{
- int status;
- int retval;
-
- status = job->ps[job->nprocs - 1].status;
- retval = WEXITSTATUS(status);
- if (!WIFEXITED(status)) {
-#if JOBS
- retval = WSTOPSIG(status);
- if (!WIFSTOPPED(status))
-#endif
- {
- /* XXX: limits number of signals */
- retval = WTERMSIG(status);
-#if JOBS
- if (retval == SIGINT)
- job->sigint = 1;
-#endif
- }
- retval += 128;
- }
- TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
- jobno(job), job->nprocs, status, retval));
- return retval;
-}
-
#if ENABLE_ASH_MAIL
/* mail.c */
@@ -11333,17 +11348,6 @@
/* trap.c */
-/*
- * Sigmode records the current value of the signal handlers for the various
- * modes. A value of zero means that the current handler is not known.
- * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
- */
-
-#define S_DFL 1 /* default signal handling (SIG_DFL) */
-#define S_CATCH 2 /* signal is caught */
-#define S_IGN 3 /* signal is ignored (SIG_IGN) */
-#define S_HARD_IGN 4 /* signal is ignored permenantly */
-#define S_RESET 5 /* temporary - to reset a hard ignored sig */
/*
@@ -11507,36 +11511,6 @@
/*
- * Ignore a signal.
- */
-static void
-ignoresig(int signo)
-{
- if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
- signal(signo, SIG_IGN);
- }
- sigmode[signo - 1] = S_HARD_IGN;
-}
-
-
-/*
- * Signal handler.
- */
-static void
-onsig(int signo)
-{
- gotsig[signo - 1] = 1;
- pendingsigs = signo;
-
- if (exsig || (signo == SIGINT && !trap[SIGINT])) {
- if (!suppressint)
- raise_interrupt();
- intpending = 1;
- }
-}
-
-
-/*
* Called to execute a trap. Perhaps we should avoid entering new trap
* handlers while we are executing a trap handler.
*/
@@ -11587,7 +11561,7 @@
#if !ENABLE_FEATURE_SH_EXTRA_QUIET
if (is_interactive > 1) {
/* Looks like they want an interactive shell */
- static int do_banner;
+ static smallint do_banner;
if (!do_banner) {
out1fmt(
@@ -11596,15 +11570,16 @@
"Enter 'help' for a list of built-in commands."
"\n\n",
BB_BANNER);
- do_banner++;
+ do_banner = 1;
}
}
#endif
}
#if !ENABLE_FEATURE_SH_EXTRA_QUIET
-/*** List the available builtins ***/
-
+/*
+ * Lists available builtins
+ */
static int
helpcmd(int argc, char **argv)
{
@@ -11711,7 +11686,8 @@
* will be restored when the shell function returns. We handle the name
* "-" as a special case.
*/
-static void mklocal(char *name)
+static void
+mklocal(char *name)
{
struct localvar *lvp;
struct var **vpp;
@@ -11811,9 +11787,10 @@
0
};
-static int timescmd(int ac, char **av)
+static int
+timescmd(int ac, char **av)
{
- long int clk_tck, s, t;
+ long clk_tck, s, t;
const unsigned char *p;
struct tms buf;
@@ -11845,12 +11822,11 @@
if (errcode < 0) {
if (errcode == -3)
ash_msg_and_raise_error("exponent less than 0");
- else if (errcode == -2)
+ if (errcode == -2)
ash_msg_and_raise_error("divide by zero");
- else if (errcode == -5)
+ if (errcode == -5)
ash_msg_and_raise_error("expression recursion loop detected");
- else
- raise_error_syntax(s);
+ raise_error_syntax(s);
}
INT_ON;
@@ -12083,7 +12059,8 @@
}
-static int umaskcmd(int argc, char **argv)
+static int
+umaskcmd(int argc, char **argv)
{
static const char permuser[3] = "ugo";
static const char permmode[3] = "rwx";
@@ -12201,12 +12178,13 @@
#ifdef RLIMIT_LOCKS
{ "locks", RLIMIT_LOCKS, 1, 'w' },
#endif
- { (char *) 0, 0, 0, '\0' }
+ { NULL, 0, 0, '\0' }
};
enum limtype { SOFT = 0x1, HARD = 0x2 };
-static void printlim(enum limtype how, const struct rlimit *limit,
+static void
+printlim(enum limtype how, const struct rlimit *limit,
const struct limits *l)
{
rlim_t val;
@@ -12223,16 +12201,16 @@
}
}
-int
+static int
ulimitcmd(int argc, char **argv)
{
- int c;
+ int c;
rlim_t val = 0;
enum limtype how = SOFT | HARD;
- const struct limits *l;
- int set, all = 0;
- int optc, what;
- struct rlimit limit;
+ const struct limits *l;
+ int set, all = 0;
+ int optc, what;
+ struct rlimit limit;
what = 'f';
while ((optc = nextopt("HSa"
@@ -12428,11 +12406,9 @@
* - always use special isspace(), see comment from bash ;-)
*/
-
#define arith_isspace(arithval) \
(arithval == ' ' || arithval == '\n' || arithval == '\t')
-
typedef unsigned char operator;
/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
@@ -12524,7 +12500,8 @@
#define NUMPTR (*numstackptr)
-static int tok_have_assign(operator op)
+static int
+tok_have_assign(operator op)
{
operator prec = PREC(op);
@@ -12533,13 +12510,13 @@
prec == PREC_PRE || prec == PREC_POST);
}
-static int is_right_associativity(operator prec)
+static int
+is_right_associativity(operator prec)
{
return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
|| prec == PREC(TOK_CONDITIONAL));
}
-
typedef struct ARITCH_VAR_NUM {
arith_t val;
arith_t contidional_second_val;
@@ -12548,7 +12525,6 @@
else is variable name */
} v_n_t;
-
typedef struct CHK_VAR_RECURSIVE_LOOPED {
const char *var;
struct CHK_VAR_RECURSIVE_LOOPED *next;
@@ -12556,8 +12532,8 @@
static chk_var_recursive_looped_t *prev_chk_var_recursive;
-
-static int arith_lookup_val(v_n_t *t)
+static int
+arith_lookup_val(v_n_t *t)
{
if (t->var) {
const char * p = lookupvar(t->var);
@@ -12595,7 +12571,8 @@
/* "applying" a token means performing it on the top elements on the integer
* stack. For a unary operator it will only change the top element, but a
* binary operator will pop two arguments and push a result */
-static int arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
+static int
+arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
{
v_n_t *numptr_m1;
arith_t numptr_val, rez;
@@ -12692,12 +12669,10 @@
}
numptr_m1->contidional_second_val_initialized = op;
numptr_m1->contidional_second_val = numptr_val;
- }
- else if (op == TOK_CONDITIONAL) {
+ } else if (op == TOK_CONDITIONAL) {
rez = rez ?
numptr_val : numptr_m1->contidional_second_val;
- }
- else if (op == TOK_EXPONENT) {
+ } else if (op == TOK_EXPONENT) {
if (numptr_val < 0)
return -3; /* exponent less than 0 */
else {
@@ -12708,8 +12683,7 @@
c *= rez;
rez = c;
}
- }
- else if (numptr_val==0) /* zero divisor check */
+ } else if (numptr_val==0) /* zero divisor check */
return -2;
else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
rez /= numptr_val;
@@ -12717,7 +12691,7 @@
rez %= numptr_val;
}
if (tok_have_assign(op)) {
- char buf[32];
+ char buf[sizeof(arith_t_type)*3 + 2];
if (numptr_m1->var == NULL) {
/* Hmm, 1=2 ? */
@@ -12725,9 +12699,9 @@
}
/* save to shell variable */
#if ENABLE_ASH_MATH_SUPPORT_64
- snprintf(buf, sizeof(buf), "%lld", arith_t_type rez);
+ snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
#else
- snprintf(buf, sizeof(buf), "%ld", arith_t_type rez);
+ snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
#endif
setvar(numptr_m1->var, buf, 0);
/* after saving, make previous value for v++ or v-- */
@@ -12744,7 +12718,7 @@
return -1;
}
-/* longest must first */
+/* longest must be first */
static const char op_tokens[] = {
'<','<','=',0, TOK_LSHIFT_ASSIGN,
'>','>','=',0, TOK_RSHIFT_ASSIGN,
@@ -12792,7 +12766,8 @@
#define endexpression &op_tokens[sizeof(op_tokens)-7]
-static arith_t arith(const char *expr, int *perrcode)
+static arith_t
+arith(const char *expr, int *perrcode)
{
char arithval; /* Current character under analysis */
operator lasttok, op;
@@ -12990,8 +12965,8 @@
* exception occurs. When an exception occurs the variable "state"
* is used to figure out how far we had gotten.
*/
-
-static void init(void)
+static void
+init(void)
{
/* from input.c: */
basepf.nextc = basepf.buf = basebuf;
@@ -13002,7 +12977,7 @@
/* from var.c: */
{
char **envp;
- char ppid[32];
+ char ppid[sizeof(int)*3 + 1];
const char *p;
struct stat st1, st2;
@@ -13013,7 +12988,7 @@
}
}
- snprintf(ppid, sizeof(ppid), "%d", (int) getppid());
+ snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
setvar("PPID", ppid, 0);
p = lookupvar("PWD");