static char *sccsid
= "@(#)proc.c 4.12 (Berkeley) 83/07/01";
* C Shell - functions that manage processes, handling hanging, termination
#define BIGINDEX 9 /* largest desirable job index */
* 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
;
timesdone
++, times(&shtimes
);
pid
= wait3(&w
.w_status
, (setintr
? WNOHANG
|WUNTRACED
:WNOHANG
),
pnoprocesses
= pid
== -1;
for (pp
= proclist
.p_next
; pp
!= PNULL
; pp
= pp
->p_next
)
if (pid
== atoi(value("child")))
pp
->p_flags
&= ~(PRUNNING
|PSTOPPED
|PREPORTED
);
pp
->p_reason
= w
.w_stopsig
;
if (pp
->p_flags
& (PTIME
|PPTIME
) || adrof("time")) {
time_t oldcutimes
, oldcstimes
;
oldcutimes
= shtimes
.tms_cutime
;
oldcstimes
= shtimes
.tms_cstime
;
gettimeofday(&pp
->p_etime
, (struct timezone
*)0);
pp
->p_utime
= shtimes
.tms_cutime
- oldcutimes
;
pp
->p_stime
= shtimes
.tms_cstime
- oldcstimes
;
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("time") &&
(fp
->p_utime
+ fp
->p_stime
) / HZ
>=
} 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 (pcurrent
&& pcurrent
!= fp
)
if (jobflags
& (PSIGNALED
|PSTOPPED
|PPTIME
) ||
!eq(dcwd
->di_name
, fp
->p_cwd
->di_name
)) {
else if ((jobflags & (PTIME|PSTOPPED)) == PTIME)
if (jobflags
&PNOTIFY
|| adrof("notify")) {
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
) {
pp
->p_flags
&= ~PNEEDNOTE
;
flags
= pprint(pp
, NUMBER
|NAME
|REASON
);
if ((flags
&(PRUNNING
|PSTOPPED
)) == 0)
* 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.
for (pp
= (fp
= &proclist
)->p_next
; pp
!= PNULL
; pp
= (fp
= pp
)->p_next
)
if (pp
->p_cwd
&& --pp
->p_cwd
->di_count
== 0)
if (pp
->p_cwd
->di_next
== 0)
* 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
)
printf("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
while((fp
= (fp
->p_friends
)) != pp
);
if ((jobflags
& PRUNNING
) == 0)
sigpause(sigblock(0) &~ mask(SIGCHLD
));
ioctl(FSHTTY
, TIOCSPGRP
, &tpgrp
); /* get tty back */
if ((jobflags
&(PSIGNALED
|PSTOPPED
|PTIME
)) ||
!eq(dcwd
->di_name
, fp
->p_cwd
->di_name
)) {
pprint(pp
, AREASON
|SHELLDIR
);
if ((jobflags
&(PINTERRUPTED
|PSTOPPED
)) && setintr
&&
(!gointr
|| !eq(gointr
, "-"))) {
if ((jobflags
& PSTOPPED
) == 0)
reason
= fp
->p_flags
& (PSIGNALED
|PINTERRUPTED
) ?
fp
->p_reason
| QUOTE
: fp
->p_reason
;
} while ((fp
= fp
->p_friends
) != pp
);
set("status", putn(reason
));
* dowait - wait for all processes to finish
register struct process
*pp
;
for (pp
= proclist
.p_next
; pp
; pp
= pp
->p_next
)
if (pp
->p_pid
&& /* pp->p_pid == pp->p_jobid && */
sigpause(sigblock(0) &~ mask(SIGCHLD
));
* 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
;
printf("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
);
if (index
== pmaxindex
) {
for (np
= proclist
.p_next
, index
= 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 << >& >> */
* 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
*)calloc(1, sizeof(struct process
));
pp
->p_flags
= t
->t_dflg
& FAND
? PRUNNING
: PRUNNING
|PFOREGND
;
pp
->p_command
= savestr(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
;
gettimeofday(&pp
->p_btime
, (struct timezone
*)0);
register struct command
*t
;
for (argp
= t
->t_dcom
; *argp
; argp
++) {
if ((t
->t_dflg
& FPIN
) == 0 && t
->t_dlef
) {
pads((t
->t_dflg
& FHERE
) ? " << " : " < ");
if ((t
->t_dflg
& FPOU
) == 0 && t
->t_drit
) {
pads((t
->t_dflg
& FCAT
) ? " >>" : " >");
register int i
= strlen(cp
);
if (cmdlen
+ i
>= PMAXLEN
) {
* 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
)
printf("[%d]", pp
->p_index
);
printf(" %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
)
printf("[%d]%s %c ", pp
->p_index
,
pp
->p_index
< 10 ? " " : "",
(pp
== pprevious
? '-' : ' '));
printf("%5d ", pp
->p_pid
);
if (flag
&(REASON
|AREASON
)) {
if (pp
->p_reason
== reason
) {
printf(format
, "Running ");
if (flag
&REASON
|| reason
!= SIGINT
||
printf(format
, mesg
[pp
->p_reason
].pname
);
printf("Exit %-16d", pp
->p_reason
);
printf("BUG: status=%-9o", status
);
printf("%s", pp
->p_command
);
if (flag
&(REASON
|AREASON
) && pp
->p_flags
&PDUMPED
)
printf(" (core dumped)");
if (tp
== pp
->p_friends
) {
!eq(tp
->p_cwd
->di_name
, dcwd
->di_name
)) {
dtildepr(value("home"), tp
->p_cwd
->di_name
);
if (pp
->p_flags
&PPTIME
&& !(status
&(PSTOPPED
|PRUNNING
))) {
{ static struct rusage zru
;
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("home"), 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
, "-l"))
error("Usage: jobs [ -l ]");
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)
if (v
[0] && v
[0][0] == '-') {
for (signum
= 1; signum
<= NSIG
; signum
++) {
if (name
= mesg
[signum
].iname
)
if (signum
< 1 || signum
> NSIG
)
bferr("Bad signal number");
for (signum
= 1; signum
<= NSIG
; signum
++)
if (mesg
[signum
].iname
&&
eq(name
, mesg
[signum
].iname
))
bferr("Unknown signal; kill -l lists signals");
register struct process
*pp
, *np
;
register int jobflags
= 0;
extern char *sys_errlist
[];
while ((np
= np
->p_friends
) != pp
);
if ((jobflags
& PRUNNING
) == 0) {
printf("%s: Already stopped\n", cp
);
killpg(pp
->p_jobid
, signum
);
if (signum
== SIGTERM
|| signum
== SIGHUP
)
killpg(pp
->p_jobid
, SIGCONT
);
bferr("Arguments should be jobs or process id's");
if (kill(pid
, signum
) < 0) {
printf("%s\n", sys_errlist
[errno
]);
if (signum
== SIGTERM
|| signum
== SIGHUP
)
* pstart - start the job in foreground/background
register struct process
*pp
;
register struct process
*np
;
if (np
->p_flags
&(PRUNNING
|PSTOPPED
)) {
np
->p_flags
&= ~PSTOPPED
;
np
->p_flags
&= ~PFOREGND
;
} while((np
= np
->p_friends
) != pp
);
pprint(pp
, foregnd
? NAME
|JOBDIR
: NUMBER
|NAME
|AMPERSAND
);
ioctl(FSHTTY
, TIOCSPGRP
, &pp
->p_jobid
);
killpg(pp
->p_jobid
, SIGCONT
);
register struct process
*pp
;
for (pp
= proclist
.p_next
; pp
; pp
= pp
->p_next
)
if (pp
->p_flags
& PSTOPPED
)
error("\nThere are stopped jobs" + 1 - neednl
);
register struct process
*pp
, *np
;
if (cp
== 0 || cp
[1] == 0 || eq(cp
, "%%") || eq(cp
, "%+")) {
if (eq(cp
, "%-") || eq(cp
, "%#")) {
bferr("No previous job");
for (pp
= proclist
.p_next
; pp
; pp
= pp
->p_next
)
if (pp
->p_index
== index
&& pp
->p_pid
== pp
->p_jobid
)
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
)) {
bferr("No job matches pattern");
* 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
&FINT
))
|| (gointr
&& eq(gointr
, "-"));
* Hold SIGCHLD until we have the process installed in our table.
while ((pid
= fork()) < 0)
error("No more processes");
pgrp
= pcurrjob
? pcurrjob
->p_jobid
: getpid();
setintr
= 0; /* until I think otherwise */
* Children just get blown away on SIGINT, SIGQUIT
* unless "onintr -" seen.
signal(SIGINT
, ignint
? SIG_IGN
: SIG_DFL
);
signal(SIGQUIT
, ignint
? SIG_IGN
: SIG_DFL
);
signal(SIGTSTP
, SIG_DFL
);
signal(SIGTTIN
, SIG_DFL
);
signal(SIGTTOU
, SIG_DFL
);
signal(SIGTERM
, parterm
);
} else if (tpgrp
== -1 && (t
->t_dflg
&FINT
)) {
signal(SIGQUIT
, SIG_IGN
);
ioctl(FSHTTY
, TIOCSPGRP
, &pgrp
);
if (wanttty
>= 0 && 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
error("No job control in this shell");
error("No job control in subshells");