1
0
Files
irix-657m-src/eoe/cmd/csh/sh.proc.c
2022-09-29 17:59:04 +03:00

1356 lines
30 KiB
C

/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
/* All Rights Reserved */
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF */
/* UNIX System Laboratories, Inc. */
/* The copyright notice above does not evidence any */
/* actual or intended publication of such source code. */
#ident "$Revision: 2.13 $"
/*******************************************************************
PROPRIETARY NOTICE (Combined)
This source code is unpublished proprietary information
constituting, or derived under license from AT&T's UNIX(r) System V.
In addition, portions of such source code were derived from Berkeley
4.3 BSD under license from the Regents of the University of
California.
Copyright Notice
Notice of copyright on this source code product does not indicate
publication.
(c) 1986,1987,1988,1989 Sun Microsystems, Inc
(c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
All rights reserved.
********************************************************************/
#include <pfmt.h>
#include <unistd.h>
#include <sys/wait.h>
#include "sh.h"
#include "sh.dir.h"
#include "sh.proc.h"
#include "sh.wconst.h"
static void nosuchjob(void);
static void okpcntl(void);
static void padd(struct command *);
static void pads(wchar_t *);
static void palloc(int, struct command *);
static void pclean(void);
static void pclrcurr(struct process *);
static struct process *pfind(wchar_t *);
static void pflush(struct process *);
static void pflushall(void);
static struct process *pgetcurr(struct process *);
static void pjwait(struct process *);
static void pkill(wchar_t **, int);
static int pprint(struct process *, int);
static void pstart(struct process *, int);
static void ptprint(struct process *);
wchar_t command[PMAXLEN+4]; /* for '\0', 1 ea for << >& >> */
int cmdlen;
wchar_t *cmdp;
struct process proclist; /* list head of all processes */
bool pnoprocesses; /* pchild found nothing to wait for */
struct process *pholdjob; /* one level stack of current jobs */
struct process *pcurrjob; /* current job */
struct process *pcurrent; /* current job in table */
struct process *pprevious; /* previous job in table */
short pmaxindex; /* current maximum job index */
/*
* C Shell - functions that manage processes, handling hanging, termination
*/
#define BIGINDEX 9 /* largest desirable job index */
static void
nosuchjob(void)
{
bferr(gettxt(_SGI_DMMX_csh_nojob, "No such job"));
}
static void
pads(wchar_t *cp)
{
register int i = wslen(cp);
#ifdef TRACE
tprintf("TRACE- pads()\n");
#endif
if(cmdlen >= PMAXLEN)
return;
if((cmdlen + i) >= PMAXLEN) {
wscpy(cmdp, S_SPPPP);
cmdlen = PMAXLEN;
cmdp += 4;
return;
}
wscpy(cmdp, cp);
cmdp += i;
cmdlen += i;
}
/*
* pchild - called at interrupt level by the SIGCHLD signal
* indicating that at least one child has terminated or stopped
* thus at least one wait system call will definitely return a
* childs status. Top level routines (like pwait) must be sure
* to mask interrupts when playing with the proclist data structures,
* the S_child variable, or the S_time variable!
*/
void
pchild(void)
{
register struct process *pp;
register struct process *fp;
register int pid;
union wait w;
int jobflags;
struct rusage ru;
#ifdef TRACE
tprintf("TRACE- pchile()\n");
#endif
loop:
pid = wait3(&w, (setintr ? WNOHANG|WUNTRACED:WNOHANG), &ru);
/*
* SysV sends a SIGCHLD when the child process
* receives a SIGCONT, and result of that action is ignored here
*/
if ( w.w_status == WCONTFLG )
return;
if (pid <= 0) {
if (errno == EINTR) {
errno = 0;
goto loop;
}
pnoprocesses = pid == -1;
return;
}
for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next)
if (pid == pp->p_pid)
goto found;
goto loop;
found:
if (pid == atoi_(value(S_child)))
unsetv(S_child);
pp->p_flags &= ~(PRUNNING|PSTOPPED|PREPORTED);
if (WIFSTOPPED(w)) {
pp->p_flags |= PSTOPPED;
pp->p_reason = (long)w.w_stopsig;
} else {
if (pp->p_flags & (PTIME|PPTIME) || adrof(S_time /*"time"*/))
(void) gettimeofday(&pp->p_etime, (struct timezone *)0);
pp->p_rusage = ru;
if (WIFSIGNALED(w)) {
if (w.w_termsig == SIGINT)
pp->p_flags |= PINTERRUPTED;
else
pp->p_flags |= PSIGNALED;
if (w.w_coredump)
pp->p_flags |= PDUMPED;
pp->p_reason = (long)w.w_termsig;
} else {
pp->p_reason = (long)w.w_retcode;
if (pp->p_reason != 0)
pp->p_flags |= PAEXITED;
else
pp->p_flags |= PNEXITED;
}
}
jobflags = 0;
fp = pp;
do {
if ((fp->p_flags & (PPTIME|PRUNNING|PSTOPPED)) == 0 &&
!child && adrof(S_time /*"time"*/) &&
fp->p_rusage.ru_utime.tv_sec+fp->p_rusage.ru_stime.tv_sec >=
atoi_(value(S_time /*"time"*/)))
fp->p_flags |= PTIME;
jobflags |= fp->p_flags;
} while ((fp = fp->p_friends) != pp);
pp->p_flags &= ~PFOREGND;
if (pp == pp->p_friends && (pp->p_flags & PPTIME)) {
pp->p_flags &= ~PPTIME;
pp->p_flags |= PTIME;
}
if ((jobflags & (PRUNNING|PREPORTED)) == 0) {
fp = pp;
do {
if (fp->p_flags&PSTOPPED)
fp->p_flags |= PREPORTED;
} while((fp = fp->p_friends) != pp);
while(fp->p_pid != fp->p_jobid)
fp = fp->p_friends;
if (jobflags&PSTOPPED) {
if (pcurrent && pcurrent != fp)
pprevious = pcurrent;
pcurrent = fp;
} else
pclrcurr(fp);
if (jobflags&PFOREGND) {
if (jobflags & (PSIGNALED|PSTOPPED|PPTIME) ||
!eq(dcwd->di_name, fp->p_cwd->di_name)) {
; /* print in pjwait */
}
} else {
if (jobflags&PNOTIFY || adrof(S_notify)) {
write_string("\015\n");
flush();
(void) pprint(pp, NUMBER|NAME|REASON);
if ((jobflags&PSTOPPED) == 0)
pflush(pp);
} else {
fp->p_flags |= PNEEDNOTE;
neednote++;
}
}
}
goto loop;
}
void
pnote(void)
{
register struct process *pp;
int flags, omask;
#ifdef TRACE
tprintf("TRACE- pnote()\n");
#endif
neednote = 0;
for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) {
if (pp->p_flags & PNEEDNOTE) {
omask = sigblock(sigmask(SIGCHLD));
pp->p_flags &= ~PNEEDNOTE;
flags = pprint(pp, NUMBER|NAME|REASON);
if ((flags&(PRUNNING|PSTOPPED)) == 0)
pflush(pp);
(void) sigsetmask(omask);
}
}
}
static void
pclean(void)
{
register struct process *fp, *pp;
int omask;
/*
* Here's where dead procs get flushed.
*/
omask = sigblock(sigmask(SIGCHLD) | (loginsh ?
sigmask(SIGHUP)|sigmask(SIGXCPU)|sigmask(SIGXFSZ) :
0 ));
for (pp = (fp = &proclist)->p_next; pp != PNULL; pp = (fp = pp)->p_next)
if (pp->p_pid == 0) {
fp->p_next = pp->p_next;
xfree(pp->p_command);
if (pp->p_cwd && --pp->p_cwd->di_count == 0)
if (pp->p_cwd->di_next == 0)
dfree(pp->p_cwd);
xfree(pp);
pp = fp;
}
(void) sigsetmask(omask);
}
/*
* pwait - wait for current job to terminate, maintaining integrity
* of current and previous job indicators.
*/
void
pwait(void)
{
pclean();
pjwait(pcurrjob);
}
/*
* pjwait - wait for a job to finish or become stopped
* It is assumed to be in the foreground state (PFOREGND)
*/
static void
pjwait(struct process *pp)
{
register struct process *fp;
int jobflags, reason, omask;
#ifdef TRACE
tprintf("TRACE- pjwait()\n");
#endif
while (pp->p_pid != pp->p_jobid)
pp = pp->p_friends;
fp = pp;
do {
if ((fp->p_flags&(PFOREGND|PRUNNING)) == PRUNNING)
shprintf("BUG: waiting for background job!\n");
} while ((fp = fp->p_friends) != pp);
/*
* Now keep pausing as long as we are not interrupted (SIGINT),
* and the target process, or any of its friends, are running
*/
fp = pp;
omask = sigblock(sigmask(SIGCHLD));
for (;;) {
jobflags = 0;
do
jobflags |= fp->p_flags;
while ((fp = (fp->p_friends)) != pp);
if ((jobflags & PRUNNING) == 0)
break;
sigpause(sigblock(0) &~ sigmask(SIGCHLD));
}
(void) sigsetmask(omask);
if (tpgrp > 0) /* get tty back */
(void) tcsetpgrp(FSHTTY, tpgrp);
if ((jobflags&(PSIGNALED|PSTOPPED|PTIME)) ||
!eq(dcwd->di_name, fp->p_cwd->di_name)) {
if (jobflags&PSTOPPED)
shprintf("\n");
(void) pprint(pp, AREASON|SHELLDIR);
}
if ((jobflags&(PINTERRUPTED|PSTOPPED)) && setintr &&
(!gointr || !eq(gointr, S_MINUS /*"-"*/))) {
if ((jobflags & PSTOPPED) == 0)
pflush(pp);
pintr1(0);
/*NOTREACHED*/
}
reason = 0;
fp = pp;
do {
if (fp->p_reason)
reason = fp->p_flags & (PSIGNALED|PINTERRUPTED) ?
fp->p_reason | REASON_QUOTE : fp->p_reason;
} while ((fp = fp->p_friends) != pp);
set(S_status, putn(reason));
if (reason && exiterr)
exitstat();
pflush(pp);
}
/*
* dowait - wait for all processes to finish
*/
void
dowait(void)
{
register struct process *pp;
int omask;
#ifdef TRACE
tprintf("TRACE- dowait()\n");
#endif
pjobs++;
omask = sigblock(sigmask(SIGCHLD));
loop:
for (pp = proclist.p_next; pp; pp = pp->p_next)
if (pp->p_pid && /* pp->p_pid == pp->p_jobid && */
pp->p_flags&PRUNNING) {
sigpause(0);
goto loop;
}
(void) sigsetmask(omask);
/* clean up proc structures on wait built-in. */
pclean();
pjobs = 0;
}
/*
* pflushall - flush all jobs from list (e.g. at fork())
*/
static void
pflushall(void)
{
register struct process *pp;
#ifdef TRACE
tprintf("TRACE- pflush()\n");
#endif
for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next)
if (pp->p_pid)
pflush(pp);
}
/*
* pflush - flag all process structures in the same job as the
* the argument process for deletion. The actual free of the
* space is not done here since pflush is called at interrupt level.
*/
static void
pflush(struct process *pp)
{
register struct process *np;
register int index;
#ifdef TRACE
tprintf("TRACE- pflush()\n");
#endif
if (pp->p_pid == 0) {
shprintf("BUG: process flushed twice");
return;
}
while (pp->p_pid != pp->p_jobid)
pp = pp->p_friends;
pclrcurr(pp);
if (pp == pcurrjob)
pcurrjob = 0;
index = pp->p_index;
np = pp;
do {
np->p_index = np->p_pid = 0;
np->p_flags &= ~PNEEDNOTE;
} while ((np = np->p_friends) != pp);
if (index == pmaxindex) {
for (np = proclist.p_next, index = 0; np; np = np->p_next)
if (np->p_index > index)
index = np->p_index;
pmaxindex = index;
}
}
/*
* pclrcurr - make sure the given job is not the current or previous job;
* pp MUST be the job leader
*/
static void
pclrcurr(struct process *pp)
{
#ifdef TRACE
tprintf("TRACE- pclrcurr()\n");
#endif
if (pp == pcurrent)
if (pprevious != PNULL) {
pcurrent = pprevious;
pprevious = pgetcurr(pp);
} else {
pcurrent = pgetcurr(pp);
pprevious = pgetcurr(pp);
}
else if (pp == pprevious)
pprevious = pgetcurr(pp);
}
/*
* palloc - allocate a process structure and fill it up.
* an important assumption is made that the process is running.
*/
static void
palloc(int pid, struct command *t)
{
register struct process *pp;
int i;
#ifdef TRACE
tprintf("TRACE- palloc()\n");
#endif
pp = (struct process *)salloc(1, sizeof(struct process));
pp->p_pid = pid;
pp->p_flags = t->t_dflg & FAND ? PRUNNING : PRUNNING|PFOREGND;
if (t->t_dflg & FTIME)
pp->p_flags |= PPTIME;
cmdp = command;
cmdlen = 0;
padd(t);
*cmdp++ = 0;
if (t->t_dflg & FPOU) {
pp->p_flags |= PPOU;
if (t->t_dflg & FDIAG)
pp->p_flags |= PDIAG;
}
pp->p_command = savestr(command);
if (pcurrjob) {
struct process *fp;
/* careful here with interrupt level */
pp->p_cwd = 0;
pp->p_index = pcurrjob->p_index;
pp->p_friends = pcurrjob;
pp->p_jobid = pcurrjob->p_pid;
for (fp = pcurrjob; fp->p_friends != pcurrjob; fp = fp->p_friends)
;
fp->p_friends = pp;
} else {
pcurrjob = pp;
pp->p_jobid = pid;
pp->p_friends = pp;
pp->p_cwd = dcwd;
dcwd->di_count++;
if (pmaxindex < BIGINDEX)
pp->p_index = ++pmaxindex;
else {
struct process *np;
for (i = 1; ; i++) {
for (np = proclist.p_next; np; np = np->p_next)
if (np->p_index == i)
goto tryagain;
pp->p_index = i;
if (i > pmaxindex)
pmaxindex = i;
break;
tryagain:;
}
}
if (pcurrent == PNULL)
pcurrent = pp;
else if (pprevious == PNULL)
pprevious = pp;
}
pp->p_next = proclist.p_next;
proclist.p_next = pp;
(void) gettimeofday(&pp->p_btime, (struct timezone *)0);
}
static void
padd(struct command *t)
{
register wchar_t **argp;
#ifdef TRACE
tprintf("TRACE- padd()\n");
#endif
if( !t)
return;
switch (t->t_dtyp) {
case TPAR:
pads(S_LBRASP);
padd(t->t_dspr);
pads(S_SPRBRA);
break;
case TCOM:
for(argp = t->t_dcom; *argp; argp++) {
pads(*argp);
if(argp[1])
pads(S_SP);
}
break;
case TOR:
case TAND:
case TFIL:
case TLST:
padd(t->t_dcar);
switch(t->t_dtyp) {
case TOR:
pads(S_SPBARBARSP);
break;
case TAND:
pads(S_SPANDANDSP);
break;
case TFIL:
pads(S_SPBARSP);
break;
case TLST:
pads(S_SEMICOLONSP);
break;
}
padd(t->t_dcdr);
return;
}
if( !(t->t_dflg & FPIN) && t->t_dlef) {
pads((t->t_dflg & FHERE)? S_SPLESLESSP : S_SPLESSP);
pads(t->t_dlef);
}
if( !(t->t_dflg & FPOU) && t->t_drit) {
pads((t->t_dflg & FCAT)? S_SPGTRGTRSP : S_SPGTR);
if (t->t_dflg & FDIAG)
pads(S_AND);
pads(S_SP);
pads(t->t_drit);
}
}
/*
* psavejob - temporarily save the current job on a one level stack
* so another job can be created. Used for { } in exp6
* and `` in globbing.
*/
void
psavejob(void)
{
#ifdef TRACE
tprintf("TRACE- psavejob()\n");
#endif
pholdjob = pcurrjob;
pcurrjob = PNULL;
}
/*
* prestjob - opposite of psavejob.
* This may be missed if we are interrupted
* somewhere, but pendjob cleans up anyway.
*/
void
prestjob(void)
{
#ifdef TRACE
tprintf("TRACE- prestjob()\n");
#endif
pcurrjob = pholdjob;
pholdjob = PNULL;
}
/*
* pendjob
* indicate that a job (set of commands) has been
* completed or is about to begin.
*/
void
pendjob(void)
{
register struct process *pp, *tp;
int lastpid;
int omask;
#ifdef TRACE
tprintf("TRACE- pendjob()\n");
#endif
if(pcurrjob && !(pcurrjob->p_flags & (PFOREGND | PSTOPPED))) {
pp = pcurrjob;
while(pp->p_pid != pp->p_jobid)
pp = pp->p_friends;
shprintf("[%d]", pp->p_index);
tp = pp;
do {
lastpid = pp->p_pid;
shprintf(" %d", pp->p_pid);
pp = pp->p_friends;
} while (pp != tp);
shprintf("\n");
/* SGI - Since SIGCHLD's signal handler uses S_child, we
* need to block it until the set completes.
*/
omask = sigblock(sigmask(SIGCHLD));
set(S_child, putn(lastpid));
(void) sigsetmask(omask);
}
pholdjob = pcurrjob = 0;
}
/*
* pprint - print a job
*/
static int
pprint(struct process *pp, int flag)
{
register status, reason;
struct process *tp;
extern char *linp, linbuf[];
int jobflags, pstatus;
char *format;
#ifdef TRACE
tprintf("TRACE- pprint()\n");
#endif
while(pp->p_pid != pp->p_jobid)
pp = pp->p_friends;
if(pp == pp->p_friends && (pp->p_flags & PPTIME)) {
pp->p_flags &= ~PPTIME;
pp->p_flags |= PTIME;
}
tp = pp;
status = reason = -1;
jobflags = 0;
do {
jobflags |= pp->p_flags;
pstatus = pp->p_flags & PALLSTATES;
if(tp != pp
&& linp != linbuf
&& !(flag & FANCY)
&& (pstatus == status
&& pp->p_reason == reason
|| !(flag&REASON)))
shprintf(" ");
else {
if(tp != pp && linp != linbuf)
shprintf("\n");
if(flag & NUMBER)
if(pp == tp)
shprintf("[%d]%s %c ", pp->p_index,
(pp->p_index < 10)? " " : "",
(pp == pcurrent)? '+' :
(pp == pprevious ? '-' : ' '));
else
shprintf(" ");
if(flag & FANCY)
shprintf("%5d ", pp->p_pid);
if(flag & (REASON | AREASON)) {
if(flag&NAME)
format = "%-21s";
else
format = "%s";
if(pstatus == status) {
if(pp->p_reason == reason) {
shprintf(format, "");
goto prcomd;
} else
reason = pp->p_reason;
} else {
status = pstatus;
reason = pp->p_reason;
}
switch (status) {
case PRUNNING:
shprintf(format,
gettxt(_SGI_DMMX_csh_run, "Running "));
break;
case PINTERRUPTED:
case PSTOPPED:
case PSIGNALED:
if((flag&(REASON|AREASON))
&& reason != SIGINT
&& reason != SIGPIPE) {
if (pp->p_reason <= NSIG &&
mesg[pp->p_reason].cname &&
mesg[pp->p_reason].pname) {
shprintf(format,
gettxt(mesg[pp->p_reason].cname,
mesg[pp->p_reason].pname));
} else {
shprintf(
gettxt(_SGI_DMMX_csh_S_DEFAULTSTR,
"Signal %d"),
pp->p_reason);
}
}
break;
case PNEXITED:
case PAEXITED:
if(flag & REASON)
if (pp->p_reason)
shprintf(gettxt(_SGI_DMMX_csh_exit,
"Exit %-16d"), pp->p_reason);
else
shprintf(format,
gettxt(_SGI_DMMX_csh_done, "Done"));
break;
default:
shprintf("BUG: status=%-9o", status);
}
}
}
prcomd:
if(flag & NAME) {
shprintf("%t", pp->p_command);
if(pp->p_flags & PPOU)
shprintf(" |");
if(pp->p_flags & PDIAG)
shprintf("&");
}
if(flag & (REASON | AREASON) && pp->p_flags & PDUMPED)
shprintf(gettxt(_SGI_DMMX_csh_core, " (core dumped)"));
if(tp == pp->p_friends) {
if(flag & AMPERSAND)
shprintf(" &");
if(flag & JOBDIR && !eq(tp->p_cwd->di_name, dcwd->di_name)) {
shprintf(" (wd: ");
dtildepr(value(S_home), tp->p_cwd->di_name);
shprintf(")");
}
}
if(pp->p_flags & PPTIME && !(status & (PSTOPPED | PRUNNING))) {
if(linp != linbuf)
shprintf("\n\t");
{ static struct rusage zru;
prusage(&zru, &pp->p_rusage, &pp->p_etime,
&pp->p_btime);
}
}
if(tp == pp->p_friends) {
if(linp != linbuf)
shprintf("\n");
if(flag & SHELLDIR && !eq(tp->p_cwd->di_name, dcwd->di_name)) {
shprintf("(wd now: ");
dtildepr(value(S_home), dcwd->di_name);
shprintf(")\n");
}
}
} while ((pp = pp->p_friends) != tp);
if(jobflags & PTIME && !(jobflags & (PSTOPPED | PRUNNING))) {
if(jobflags & NUMBER)
shprintf(" ");
ptprint(tp);
}
return(jobflags);
}
static void
ptprint(struct process *tp)
{
struct timeval tetime, diff;
static struct timeval ztime;
struct rusage ru;
static struct rusage zru;
register struct process *pp = tp;
#ifdef TRACE
tprintf("TRACE- ptprint()\n");
#endif
ru = zru;
tetime = ztime;
do {
ruadd(&ru, &pp->p_rusage);
tvsub(&diff, &pp->p_etime, &pp->p_btime);
if(timercmp(&diff, &tetime, >))
tetime = diff;
} while ((pp = pp->p_friends) != tp);
prusage(&zru, &ru, &tetime, &ztime);
}
/*
* dojobs - print all jobs
*/
void
dojobs(wchar_t **v)
{
register struct process *pp;
register int flag = NUMBER|NAME|REASON;
int i;
#ifdef TRACE
tprintf("TRACE- dojobs()\n");
#endif
if(chkstop)
chkstop = 2;
if(*++v) {
if(v[1] || !eq(*v, S_DASHl))
err_usage("jobs [ -l ]");
flag |= FANCY | JOBDIR;
}
for(i = 1; i <= pmaxindex; i++) {
for(pp = proclist.p_next; pp; pp = pp->p_next) {
if(pp->p_index == i && pp->p_pid == pp->p_jobid) {
pp->p_flags &= ~PNEEDNOTE;
if( !(pprint(pp, flag) & (PRUNNING | PSTOPPED)))
pflush(pp);
break;
}
}
}
}
/*
* dofg - builtin - put the job into the foreground
*/
void
dofg(wchar_t **v)
{
register struct process *pp;
#ifdef TRACE
tprintf("TRACE- dofg()\n");
#endif
okpcntl();
++v;
do {
pp = pfind(*v);
pstart(pp, 1);
pjwait(pp);
} while (*v && *++v);
}
/*
* %... - builtin - put the job into the foreground
*/
void
dofg1(wchar_t **v)
{
register struct process *pp;
#ifdef TRACE
tprintf("TRACE- untty()\n");
#endif
okpcntl();
pp = pfind(v[0]);
pstart(pp, 1);
pjwait(pp);
}
/*
* dobg - builtin - put the job into the background
*/
void
dobg(wchar_t **v)
{
register struct process *pp;
#ifdef TRACE
tprintf("TRACE- dobg()\n");
#endif
okpcntl();
++v;
do {
pp = pfind(*v);
pstart(pp, 0);
} while (*v && *++v);
}
/*
* %... & - builtin - put the job into the background
*/
void
dobg1(wchar_t **v)
{
register struct process *pp;
#ifdef TRACE
tprintf("TRACE- dobg1()\n");
#endif
pp = pfind(v[0]);
pstart(pp, 0);
}
/*
* dostop - builtin - stop the job
*/
void
dostop(wchar_t **v)
{
#ifdef TRACE
tprintf("TRACE- dostop()\n");
#endif
pkill(++v, SIGSTOP);
}
/*
* dokill - builtin - superset of kill (1)
*/
void
dokill(wchar_t **v)
{
register int signum;
register wchar_t *name;
#ifdef TRACE
tprintf("TRACE- dokill()\n");
#endif
v++;
if (v[0] && v[0][0] == '-') {
if (v[0][1] == 'l') {
for (signum = 1; signum < NSIG; signum++) {
if (name = mesg[signum].iname)
shprintf("%t ", name);
if (signum == 17)
wputchar('\n');
}
wputchar('\n');
return;
}
if (digit(v[0][1])) {
signum = atoi_(v[0]+1);
if (signum < 0 || signum >= NSIG)
bferr(gettxt(_SGI_DMMX_csh_badsig, "Bad signal number"));
} else {
name = &v[0][1];
for (signum = 1; signum < NSIG; signum++) {
if (mesg[signum].iname &&
eq(name, mesg[signum].iname))
goto gotsig;
}
if (eq(name, S_IOT /*"IOT"*/)) {
signum = SIGABRT;
goto gotsig;
}
setname(name);
bferr(gettxt(_SGI_DMMX_csh_unknownsig,
"Unknown signal; kill -l lists signals"));
}
gotsig:
v++;
} else
signum = SIGTERM;
pkill(v, signum);
}
static void
pkill(wchar_t **v, int signum)
{
register struct process *pp, *np;
register int jobflags = 0;
int omask, pid, err = 0;
wchar_t *cp;
char chbuf[CSHBUFSIZ * MB_LEN_MAX];
#ifdef TRACE
tprintf("TRACE- pkill()\n");
#endif
omask = sigmask(SIGCHLD);
if (setintr)
omask |= sigmask(SIGINT);
omask = sigblock(omask) & ~omask;
while (*v) {
cp = globone(*v);
if (*cp == '%') {
np = pp = pfind(cp);
do
jobflags |= np->p_flags;
while ((np = np->p_friends) != pp);
switch (signum) {
case SIGSTOP:
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
if ((jobflags & PRUNNING) == 0) {
showstr(MM_INFO,
gettxt(_SGI_DMMX_csh_alrdstp,
"%s: Already stopped"),
tstostr(chbuf, cp, NOFLAG));
err++;
goto cont;
}
}
if (killpg(pp->p_jobid, signum) < 0) {
/* %s -> %t */
haderr = 1;
shprintf("%t: ", cp);
shprintf("%s\n", strerror(oserror()));
err++;
}
if (signum == SIGTERM || signum == SIGHUP)
(void) killpg(pp->p_jobid, SIGCONT);
} else if (!(digit(*cp) || *cp == '-'))
bferr(gettxt(_SGI_DMMX_csh_jobproc,
"Arguments should be jobs or process id's"));
else {
pid = atoi_(cp);
if (kill(pid, signum) < 0) {
haderr = 1;
shprintf("%d: ", pid);
shprintf("%s\n", strerror(oserror()));
err++;
goto cont;
}
if (signum == SIGTERM || signum == SIGHUP)
(void) kill(pid, SIGCONT);
}
cont:
xfree(cp);
v++;
}
(void) sigsetmask(omask);
if (err)
error(NULL);
}
/*
* pstart - start the job in foreground/background
*/
static void
pstart(struct process *pp, int foregnd)
{
register struct process *np;
int omask, jobflags = 0, jobs;
#ifdef TRACE
tprintf("TRACE- pstart()\n");
#endif
omask = sigblock(sigmask(SIGCHLD) | (loginsh ?
sigmask(SIGHUP)|sigmask(SIGXCPU)|sigmask(SIGXFSZ) :
0 ));
np = pp;
do {
jobflags |= np->p_flags;
if (np->p_flags&(PRUNNING|PSTOPPED)) {
np->p_flags |= PRUNNING;
np->p_flags &= ~PSTOPPED;
if (foregnd)
np->p_flags |= PFOREGND;
else
np->p_flags &= ~PFOREGND;
}
} while((np = np->p_friends) != pp);
if (!foregnd) {
/* count the running jobs */
for(np = proclist.p_next, jobs = 0; np; np = np->p_next)
if (np->p_index > 0)
jobs++;
if (jobs == 1)
pcurrent = pp;
else
pclrcurr(pp);
}
(void) pprint(pp, foregnd ? NAME|JOBDIR : NUMBER|NAME|AMPERSAND);
if (foregnd)
(void) tcsetpgrp(FSHTTY, pp->p_jobid);
if (jobflags&PSTOPPED)
(void) killpg(pp->p_jobid, SIGCONT);
(void) sigsetmask(omask);
}
void
panystop(int neednl)
{
register struct process *pp;
#ifdef TRACE
tprintf("TRACE- panystop()\n");
#endif
chkstop = 2;
for (pp = proclist.p_next; pp; pp = pp->p_next)
if (pp->p_flags & PSTOPPED)
error(gettxt(_SGI_DMMX_csh_stjobs,
"\nThere are stopped jobs" + 1 - neednl));
}
static struct process *
pfind(wchar_t *cp)
{
register struct process *pp, *np;
#ifdef TRACE
tprintf("TRACE- pfind()\n");
#endif
if (cp == 0 || (cp[0] == '%' && cp[1] == 0) ||
eq(cp, S_PARCENTPARCENT /*"%%"*/) ||
eq(cp, S_PARCENTPLUS /*"%+"*/)) {
if (pcurrent == PNULL)
bferr(gettxt(_SGI_DMMX_csh_nocjob, "No current job"));
return (pcurrent);
}
if (eq(cp, S_PARCENTMINUS /*"%-"*/) ||
eq(cp, S_PARCENTSHARP /*"%#"*/)) {
if (pprevious == PNULL)
bferr(gettxt(_SGI_DMMX_csh_nopjob, "No previous job"));
return (pprevious);
}
if (cp[0] == '%' && digit(cp[1])) {
int index = atoi_(cp+1);
for (pp = proclist.p_next; pp; pp = pp->p_next)
if (pp->p_index == index && pp->p_pid == pp->p_jobid)
return (pp);
nosuchjob();
}
np = PNULL;
for (pp = proclist.p_next; pp; pp = pp->p_next)
if (pp->p_pid == pp->p_jobid) {
if (cp[0] == '%' && cp[1] == '?') {
register wchar_t *dp;
for (dp = pp->p_command; *dp; dp++) {
if (*dp != cp[2])
continue;
if (prefix(cp+2, dp))
goto match;
}
} else if (prefix(cp+1, pp->p_command)) {
match:
if (np)
ambiguous();
np = pp;
}
}
if (np)
return (np);
if (cp[1] == '?')
bferr(gettxt(_SGI_DMMX_csh_jobmatch, "No job matches pattern"));
else
nosuchjob();
/*NOTREACHED*/
}
/*
* pgetcurr - find most recent job that is not pp, preferably stopped
*/
static struct process *
pgetcurr(struct process *pp)
{
register struct process *np;
register struct process *xp = PNULL;
#ifdef TRACE
tprintf("TRACE- pgetcurr()\n");
#endif
for (np = proclist.p_next; np; np = np->p_next)
if (np != pcurrent && np != pp && np->p_pid &&
np->p_pid == np->p_jobid) {
if (np->p_flags & PSTOPPED)
return (np);
if (xp == PNULL)
xp = np;
}
return (xp);
}
/*
* donotify - flag the job so as to report termination asynchronously
*/
void
donotify(wchar_t **v)
{
register struct process *pp;
#ifdef TRACE
tprintf("TRACE- donotify()\n");
#endif
pp = pfind(*++v);
pp->p_flags |= PNOTIFY;
}
/*
* Do the fork and whatever should be done in the child side that
* should not be done if we are not forking at all (like for simple builtin's)
* Also do everything that needs any signals fiddled with in the parent side
*
* Wanttty tells whether process and/or tty pgrps are to be manipulated:
* -1: leave tty alone; inherit pgrp from parent
* 0: already have tty; manipulate process pgrps only
* 1: want to claim tty; manipulate process and tty pgrps
* It is usually just the value of tpgrp.
*/
int
pfork(struct command *t, int wanttty)
{
register int pid;
bool ignint = 0;
int pgrp, omask;
#ifdef TRACE
tprintf("TRACE- pfork()\n");
#endif
/*
* A child will be uninterruptible only under very special
* conditions. Remember that the semantics of '&' is
* implemented by disconnecting the process from the tty so
* signals do not need to ignored just for '&'.
* Thus signals are set to default action for children unless:
* we have had an "onintr -" (then specifically ignored)
* we are not playing with signals (inherit action)
*/
if (setintr)
ignint = (tpgrp == -1 && (t->t_dflg&FINT))
|| (gointr && eq(gointr, S_MINUS /*"-"*/));
/*
* Hold SIGCHLD until we have the process installed in our table.
*/
omask = sigblock(sigmask(SIGCHLD));
while ((pid = fork()) < 0)
if (setintr == 0)
sleep(FORKSLEEP);
else {
(void) sigsetmask(omask);
error(gettxt(_SGI_DMMX_csh_fkfail, "Fork failed"));
}
if (pid == 0) {
settimes();
pgrp = pcurrjob ? pcurrjob->p_jobid : getpid();
pflushall();
pcurrjob = PNULL;
child++;
if (setintr) {
setintr = 0; /* until I think otherwise */
/*
* Children just get blown away on SIGINT, SIGQUIT
* unless "onintr -" seen.
*/
(void) signal(SIGINT, ignint ? SIG_IGN : SIG_DFL);
(void) signal(SIGQUIT, ignint ? SIG_IGN : SIG_DFL);
if (wanttty >= 0) {
/* make stoppable */
(void) sigset(SIGTSTP, SIG_DFL);
(void) sigset(SIGTTIN, SIG_DFL);
(void) sigset(SIGTTOU, SIG_DFL);
}
(void) signal(SIGTERM, parterm);
} else if (tpgrp == -1 && (t->t_dflg&FINT)) {
(void) signal(SIGINT, SIG_IGN);
(void) signal(SIGQUIT, SIG_IGN);
}
if (wanttty > 0)
omask = sigblock(sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU));
if (wanttty >= 0) {
if (setpgid(0, pgrp) == -1) {
shprintf("csh: setpgid error.\n");
done(0);
}
}
if (wanttty > 0) {
(void) tcsetpgrp(FSHTTY, pgrp);
(void) sigsetmask(omask);
}
if (tpgrp > 0)
tpgrp = 0; /* gave tty away */
/*
* Nohup and nice apply only to TCOM's but it would be
* nice (?!?) if you could say "nohup (foo;bar)"
* Then the parser would have to know about nice/nohup/time
*/
if (t->t_dflg & FNOHUP)
(void) signal(SIGHUP, SIG_IGN);
if (t->t_dflg & FNICE)
(void) setpriority(PRIO_PROCESS, 0, t->t_nice);
} else {
if (wanttty >= 0)
(void) setpgid(pid, pcurrjob ? pcurrjob->p_jobid : pid);
palloc(pid, t);
(void) sigsetmask(omask);
}
return (pid);
}
static void
okpcntl(void)
{
#ifdef TRACE
tprintf("TRACE- okpcntl()\n");
#endif
if (tpgrp == -1)
error(gettxt(_SGI_DMMX_csh_nojc1,
"No job control in this shell"));
if (tpgrp == 0)
error(gettxt(_SGI_DMMX_csh_nojc2,
"No job control in subshell"));
}
/*
* We kill the last foreground process group. It then becomes
* responsible to propagate the SIGHUP to its progeny.
*/
void
killfgpgroup(void)
{
struct process *pp, *np;
for (pp = proclist.p_next; pp; pp = pp->p_next) {
np = pp;
/*
* Find if this job is in the foreground. It could be that
* the process leader has exited and the foreground flag
* is cleared for it.
*/
do
/*
* If a process is in the foreground; we try to kill
* it's process group. If we succeed, then the
* whole job is gone. Otherwise we keep going...
* But avoid sending HUP to the shell again.
*/
if ((np->p_flags & PFOREGND) != 0 && np->p_jobid != shpgrp &&
killpg(np->p_jobid, SIGHUP) != -1) {
/* In case the job was suspended... */
(void) killpg(np->p_jobid, SIGCONT);
break;
}
while ((np = np->p_friends) != pp);
}
}