* Copyright (c) 1980, 1991 The Regents of the University of California.
* %sccs.include.redist.c%
static char sccsid
[] = "@(#)proc.c 5.18 (Berkeley) %G%";
#define BIGINDEX 9 /* largest desirable job index */
static struct rusage zru
;
static void pflushall
__P((void));
static void pflush
__P((struct process
*));
static void pclrcurr
__P((struct process
*));
static void padd
__P((struct command
*));
static int pprint
__P((struct process
*, int));
static void ptprint
__P((struct process
*));
static void pads
__P((Char
*));
static void pkill
__P((Char
**v
, int));
*pgetcurr
__P((struct process
*));
static void okpcntl
__P((void));
* 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!
register struct process
*pp
;
register struct process
*fp
;
errno
= 0; /* reset, just in case */
(setintr
&& (intty
|| insource
) ? WNOHANG
| WUNTRACED
: WNOHANG
), &ru
);
pnoprocesses
= pid
== -1;
for (pp
= proclist
.p_next
; pp
!= PNULL
; pp
= pp
->p_next
)
if (pid
== atoi(short2str(value(STRchild
))))
pp
->p_flags
&= ~(PRUNNING
| PSTOPPED
| PREPORTED
);
pp
->p_reason
= w
.w_stopsig
;
if (pp
->p_flags
& (PTIME
| PPTIME
) || adrof(STRtime
))
(void) gettimeofday(&pp
->p_etime
, (struct timezone
*) 0);
if (w
.w_termsig
== SIGINT
)
pp
->p_flags
|= PINTERRUPTED
;
pp
->p_flags
|= PSIGNALED
;
pp
->p_reason
= w
.w_termsig
;
pp
->p_reason
= w
.w_retcode
;
if ((fp
->p_flags
& (PPTIME
| PRUNNING
| PSTOPPED
)) == 0 &&
!child
&& adrof(STRtime
) &&
fp
->p_rusage
.ru_utime
.tv_sec
+ fp
->p_rusage
.ru_stime
.tv_sec
>= atoi(short2str(value(STRtime
))))
} while ((fp
= fp
->p_friends
) != pp
);
pp
->p_flags
&= ~PFOREGND
;
if (pp
== pp
->p_friends
&& (pp
->p_flags
& PPTIME
)) {
if ((jobflags
& (PRUNNING
| PREPORTED
)) == 0) {
if (fp
->p_flags
& PSTOPPED
)
fp
->p_flags
|= PREPORTED
;
} while ((fp
= fp
->p_friends
) != pp
);
while (fp
->p_pid
!= fp
->p_jobid
)
if (jobflags
& PSTOPPED
) {
if (pcurrent
&& pcurrent
!= fp
)
if (jobflags
& PFOREGND
) {
if (jobflags
& (PSIGNALED
| PSTOPPED
| PPTIME
) ||
!eq(dcwd
->di_name
, fp
->p_cwd
->di_name
)) {
/* PWP: print a newline after ^C */
else if (jobflags
& PINTERRUPTED
)
xputchar('\r' | QUOTE
), xputchar('\n');
if (jobflags
& PNOTIFY
|| adrof(STRnotify
)) {
xputchar('\r' | QUOTE
), xputchar('\n');
(void) pprint(pp
, NUMBER
| NAME
| REASON
);
if ((jobflags
& PSTOPPED
) == 0)
fp
->p_flags
|= PNEEDNOTE
;
register struct process
*pp
;
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)
(void) sigsetmask(omask
);
* pwait - wait for current job to terminate, maintaining integrity
* of current and previous job indicators.
register struct process
*fp
, *pp
;
* Here's where dead procs get flushed.
omask
= sigblock(sigmask(SIGCHLD
));
for (pp
= (fp
= &proclist
)->p_next
; pp
!= PNULL
; pp
= (fp
= pp
)->p_next
)
xfree((ptr_t
) pp
->p_command
);
if (pp
->p_cwd
&& --pp
->p_cwd
->di_count
== 0)
if (pp
->p_cwd
->di_next
== 0)
(void) sigsetmask(omask
);
* pjwait - wait for a job to finish or become stopped
* It is assumed to be in the foreground state (PFOREGND)
register struct process
*pp
;
register struct process
*fp
;
while (pp
->p_pid
!= pp
->p_jobid
)
if ((fp
->p_flags
& (PFOREGND
| PRUNNING
)) == PRUNNING
)
xprintf("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
omask
= sigblock(sigmask(SIGCHLD
));
(void) sigblock(sigmask(SIGCHLD
));
while ((fp
= (fp
->p_friends
)) != pp
);
if ((jobflags
& PRUNNING
) == 0)
xprintf("starting to sigpause for SIGCHLD on %d\n", fp
->p_pid
);
(void) sigpause(omask
& ~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
) {
if (adrof(STRlistjobs
)) {
if (eq(value(STRlistjobs
), STRlong
))
(void) pprint(pp
, SHELLDIR
);
(void) pprint(pp
, AREASON
| SHELLDIR
);
(void) pprint(pp
, AREASON
| SHELLDIR
);
if ((jobflags
& (PINTERRUPTED
| PSTOPPED
)) && setintr
&&
(!gointr
|| !eq(gointr
, STRminus
))) {
if ((jobflags
& PSTOPPED
) == 0)
reason
= fp
->p_flags
& (PSIGNALED
| PINTERRUPTED
) ?
fp
->p_reason
| META
: fp
->p_reason
;
} while ((fp
= fp
->p_friends
) != pp
);
if ((reason
!= 0) && (adrof(STRprintexitvalue
)))
xprintf("Exit %d\n", reason
);
set(STRstatus
, putn(reason
));
* dowait - wait for all processes to finish
register struct process
*pp
;
omask
= sigblock(sigmask(SIGCHLD
));
for (pp
= proclist
.p_next
; pp
; pp
= pp
->p_next
)
if (pp
->p_pid
&& /* pp->p_pid == pp->p_jobid && */
pp
->p_flags
& PRUNNING
) {
(void) sigpause((sigmask_t
) 0);
(void) sigsetmask(omask
);
* pflushall - flush all jobs from list (e.g. at fork())
register struct process
*pp
;
for (pp
= proclist
.p_next
; pp
!= PNULL
; pp
= pp
->p_next
)
* 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.
register struct process
*pp
;
register struct process
*np
;
xprintf("BUG: process flushed twice");
while (pp
->p_pid
!= pp
->p_jobid
)
np
->p_index
= np
->p_pid
= 0;
np
->p_flags
&= ~PNEEDNOTE
;
} while ((np
= np
->p_friends
) != pp
);
for (np
= proclist
.p_next
, idx
= 0; np
; np
= np
->p_next
)
* pclrcurr - make sure the given job is not the current or previous job;
* pp MUST be the job leader
register struct process
*pp
;
if (pprevious
!= PNULL
) {
pprevious
= pgetcurr(pp
);
pprevious
= pgetcurr(pp
);
else if (pp
== pprevious
)
pprevious
= pgetcurr(pp
);
/* +4 here is 1 for '\0', 1 ea for << >& >> */
static Char command
[PMAXLEN
+ 4];
* palloc - allocate a process structure and fill it up.
* an important assumption is made that the process is running.
register struct command
*t
;
register struct process
*pp
;
pp
= (struct process
*) xcalloc(1, (size_t) sizeof(struct process
));
pp
->p_flags
= t
->t_dflg
& F_AMPERSAND
? PRUNNING
: PRUNNING
| PFOREGND
;
if (t
->t_dflg
& F_PIPEOUT
) {
if (t
->t_dflg
& F_STDERR
)
pp
->p_command
= Strsave(command
);
/* careful here with interrupt level */
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
);
if (pmaxindex
< BIGINDEX
)
pp
->p_index
= ++pmaxindex
;
for (np
= proclist
.p_next
; np
; np
= np
->p_next
)
else if (pprevious
== PNULL
)
pp
->p_next
= proclist
.p_next
;
(void) gettimeofday(&pp
->p_btime
, (struct timezone
*) 0);
register struct command
*t
;
for (argp
= t
->t_dcom
; *argp
; argp
++) {
if ((t
->t_dflg
& F_PIPEIN
) == 0 && t
->t_dlef
) {
pads((t
->t_dflg
& F_READ
) ? STRspLarrow2sp
: STRspLarrowsp
);
if ((t
->t_dflg
& F_PIPEOUT
) == 0 && t
->t_drit
) {
pads((t
->t_dflg
& F_APPEND
) ? STRspRarrow2
: STRspRarrow
);
if (t
->t_dflg
& F_STDERR
)
* Avoid the Quoted Space alias hack! Reported by:
* sam@john-bigboote.ICS.UCI.EDU (Sam Horrocks)
if (cp
[0] == STRQNULL
[0])
if (cmdlen
+ i
>= PMAXLEN
) {
(void) Strcpy(cmdp
, STRsp3dots
);
* psavejob - temporarily save the current job on a one level stack
* so another job can be created. Used for { } in exp6
* prestjob - opposite of psavejob. This may be missed if we are interrupted
* somewhere, but pendjob cleans up anyway.
* pendjob - indicate that a job (set of commands) has been completed
register struct process
*pp
, *tp
;
if (pcurrjob
&& (pcurrjob
->p_flags
& (PFOREGND
| PSTOPPED
)) == 0) {
while (pp
->p_pid
!= pp
->p_jobid
)
xprintf("[%d]", pp
->p_index
);
xprintf(" %d", pp
->p_pid
);
register struct process
*pp
;
extern char *linp
, linbuf
[];
while (pp
->p_pid
!= pp
->p_jobid
)
if (pp
== pp
->p_friends
&& (pp
->p_flags
& PPTIME
)) {
pstatus
= pp
->p_flags
& PALLSTATES
;
if (tp
!= pp
&& linp
!= linbuf
&& !(flag
& FANCY
) &&
(pstatus
== status
&& pp
->p_reason
== reason
||
if (tp
!= pp
&& linp
!= linbuf
)
xprintf("[%d]%s %c ", pp
->p_index
,
pp
->p_index
< 10 ? " " : "",
(pp
== pprevious
? '-' : ' '));
xprintf("%5d ", pp
->p_pid
);
if (flag
& (REASON
| AREASON
)) {
if (pp
->p_reason
== reason
) {
xprintf(format
, "Running ");
if ((flag
& (REASON
| AREASON
)) && reason
!= SIGINT
xprintf(format
, mesg
[pp
->p_reason
].pname
);
xprintf("Exit %-16d", pp
->p_reason
);
xprintf("BUG: status=%-9o", status
);
xprintf("%s", short2str(pp
->p_command
));
if (flag
& (REASON
| AREASON
) && pp
->p_flags
& PDUMPED
)
xprintf(" (core dumped)");
if (tp
== pp
->p_friends
) {
!eq(tp
->p_cwd
->di_name
, dcwd
->di_name
)) {
dtildepr(value(STRhome
), tp
->p_cwd
->di_name
);
if (pp
->p_flags
& PPTIME
&& !(status
& (PSTOPPED
| PRUNNING
))) {
prusage(&zru
, &pp
->p_rusage
, &pp
->p_etime
,
if (tp
== pp
->p_friends
) {
if (flag
& SHELLDIR
&& !eq(tp
->p_cwd
->di_name
, dcwd
->di_name
)) {
dtildepr(value(STRhome
), dcwd
->di_name
);
} while ((pp
= pp
->p_friends
) != tp
);
if (jobflags
& PTIME
&& (jobflags
& (PSTOPPED
| PRUNNING
)) == 0) {
register struct process
*tp
;
struct timeval tetime
, diff
;
static struct timeval ztime
;
static struct rusage zru
;
register struct process
*pp
= tp
;
ruadd(&ru
, &pp
->p_rusage
);
tvsub(&diff
, &pp
->p_etime
, &pp
->p_btime
);
if (timercmp(&diff
, &tetime
, >))
} while ((pp
= pp
->p_friends
) != tp
);
prusage(&zru
, &ru
, &tetime
, &ztime
);
* dojobs - print all jobs
register struct process
*pp
;
register int flag
= NUMBER
| NAME
| REASON
;
if (v
[1] || !eq(*v
, STRml
))
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
)))
* dofg - builtin - put the job into the foreground
register struct process
*pp
;
* %... - builtin - put the job into the foreground
register struct process
*pp
;
* dobg - builtin - put the job into the background
register struct process
*pp
;
* %... & - builtin - put the job into the background
register struct process
*pp
;
* dostop - builtin - stop the job
* dokill - builtin - superset of kill (1)
register int signum
, len
= 0;
if (v
[0] && v
[0][0] == '-') {
for (signum
= 1; signum
<= NSIG
; signum
++) {
if ((name
= mesg
[signum
].iname
) != NULL
) {
signum
= atoi(short2str(v
[0] + 1));
if (signum
< 0 || signum
> NSIG
)
stderror(ERR_NAME
| ERR_BADSIG
);
for (signum
= 1; signum
<= NSIG
; signum
++)
if (mesg
[signum
].iname
&&
eq(&v
[0][1], str2short(mesg
[signum
].iname
)))
setname(short2str(&v
[0][1]));
stderror(ERR_NAME
| ERR_UNKSIG
);
register struct process
*pp
, *np
;
register int jobflags
= 0;
omask
= sigmask(SIGCHLD
);
omask
|= sigmask(SIGINT
);
omask
= sigblock(omask
) & ~omask
;
stderror(ERR_NAME
| ERR_NOMATCH
);
while ((np
= np
->p_friends
) != pp
);
if ((jobflags
& PRUNNING
) == 0) {
xprintf("%s: Already suspended\n", short2str(cp
));
* suspend a process, kill -CONT %, then type jobs; the shell
* says it is suspended, but it is running; thanks jaap..
if (killpg((pid_t
) pp
->p_jobid
, signum
) < 0) {
xprintf("%s: %s\n", short2str(cp
), strerror(errno
));
if (signum
== SIGTERM
|| signum
== SIGHUP
)
(void) killpg((pid_t
) pp
->p_jobid
, SIGCONT
);
else if (!(Isdigit(*cp
) || *cp
== '-'))
stderror(ERR_NAME
| ERR_JOBARGS
);
pid
= atoi(short2str(cp
));
if (kill((pid_t
) pid
, signum
) < 0) {
xprintf("%d: %s\n", pid
, strerror(errno
));
if (signum
== SIGTERM
|| signum
== SIGHUP
)
(void) kill((pid_t
) pid
, SIGCONT
);
blkfree(gargv
), gargv
= 0;
(void) sigsetmask(omask
);
* pstart - start the job in foreground/background
register struct process
*pp
;
register struct process
*np
;
omask
= sigblock(sigmask(SIGCHLD
));
if (np
->p_flags
& (PRUNNING
| PSTOPPED
)) {
np
->p_flags
&= ~PSTOPPED
;
np
->p_flags
&= ~PFOREGND
;
} while ((np
= np
->p_friends
) != pp
);
(void) pprint(pp
, foregnd
? NAME
| JOBDIR
: NUMBER
| NAME
| AMPERSAND
);
(void) tcsetpgrp(FSHTTY
, pp
->p_jobid
);
(void) killpg((pid_t
) pp
->p_jobid
, SIGCONT
);
(void) sigsetmask(omask
);
register struct process
*pp
;
for (pp
= proclist
.p_next
; pp
; pp
= pp
->p_next
)
if (pp
->p_flags
& PSTOPPED
)
stderror(ERR_STOPPED
, neednl
? "\n" : "");
register struct process
*pp
, *np
;
if (cp
== 0 || cp
[1] == 0 || eq(cp
, STRcent2
) || eq(cp
, STRcentplus
)) {
stderror(ERR_NAME
| ERR_JOBCUR
);
if (eq(cp
, STRcentminus
) || eq(cp
, STRcenthash
)) {
stderror(ERR_NAME
| ERR_JOBPREV
);
int idx
= atoi(short2str(cp
+ 1));
for (pp
= proclist
.p_next
; pp
; pp
= pp
->p_next
)
if (pp
->p_index
== idx
&& pp
->p_pid
== pp
->p_jobid
)
stderror(ERR_NAME
| ERR_NOSUCHJOB
);
for (pp
= proclist
.p_next
; pp
; pp
= pp
->p_next
)
if (pp
->p_pid
== pp
->p_jobid
) {
for (dp
= pp
->p_command
; *dp
; dp
++) {
else if (prefix(cp
+ 1, pp
->p_command
)) {
stderror(ERR_NAME
| ERR_AMBIG
);
stderror(ERR_NAME
| cp
[1] == '?' ? ERR_JOBPAT
: ERR_NOSUCHJOB
);
* pgetcurr - find most recent job that is not pp, preferably stopped
register struct process
*pp
;
register struct process
*np
;
register struct process
*xp
= PNULL
;
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
)
* donotify - flag the job so as to report termination asynchronously
register struct process
*pp
;
* 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.
struct command
*t
; /* command we are forking for */
* 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)
ignint
= (tpgrp
== -1 && (t
->t_dflg
& F_NOINTERRUPT
))
|| (gointr
&& eq(gointr
, STRminus
));
* Check for maximum nesting of 16 processes to avoid Forking loops
stderror(ERR_NESTING
, 16);
* Hold SIGCHLD until we have the process installed in our table.
omask
= sigblock(sigmask(SIGCHLD
));
while ((pid
= fork()) < 0)
(void) sigsetmask(omask
);
pgrp
= pcurrjob
? pcurrjob
->p_jobid
: getpid();
setintr
= 0; /* until I think otherwise */
* Children just get blown away on SIGINT, SIGQUIT unless "onintr
(void) signal(SIGINT
, ignint
? SIG_IGN
: SIG_DFL
);
(void) signal(SIGQUIT
, ignint
? SIG_IGN
: SIG_DFL
);
(void) signal(SIGTSTP
, SIG_DFL
);
(void) signal(SIGTTIN
, SIG_DFL
);
(void) signal(SIGTTOU
, SIG_DFL
);
(void) signal(SIGTERM
, parterm
);
else if (tpgrp
== -1 && (t
->t_dflg
& F_NOINTERRUPT
)) {
(void) signal(SIGINT
, SIG_IGN
);
(void) signal(SIGQUIT
, SIG_IGN
);
* Nohup and nice apply only to NODE_COMMAND's but it would be nice
* (?!?) if you could say "nohup (foo;bar)" Then the parser would have
* to know about nice/nohup/time
(void) signal(SIGHUP
, SIG_IGN
);
(void) setpriority(PRIO_PROCESS
, 0, t
->t_nice
);
(void) setpgid(pid
, pcurrjob
? pcurrjob
->p_jobid
: pid
);
(void) sigsetmask(omask
);
stderror(ERR_JOBCONTROL
);
stderror(ERR_JOBCTRLSUB
);
* if we don't have vfork(), things can still go in the wrong order
* resulting in the famous 'Stopped (tty output)'. But some systems
* don't permit the setpgid() call, (these are more recent secure
* systems such as ibm's aix). Then we'd rather print an error message
* I am open to suggestions how to fix that.
* christos: I am blocking the tty signals till I've set things
omask
= sigblock(sigmask(SIGTSTP
)|sigmask(SIGTTIN
)|sigmask(SIGTTOU
));
* From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de>
* Don't check for tpgrp >= 0 so even non-interactive shells give
* background jobs process groups Same for the comparison in the other part
if (setpgid(0, pgrp
) == -1) {
xprintf("csh: setpgid error.\n");
(void) tcsetpgrp(FSHTTY
, pgrp
);
(void) sigsetmask(omask
);
tpgrp
= 0; /* gave tty away */