sh: Allow enabling job control without a tty in non-interactive mode.

If no tty is available, 'set -m' is still useful to put jobs in their own
process groups.
This commit is contained in:
Jilles Tjoelker 2014-09-04 21:48:33 +00:00
parent 50e7b30ffc
commit cd60e2c67d
2 changed files with 47 additions and 14 deletions

View File

@ -118,6 +118,24 @@ static void showjob(struct job *, int);
static int jobctl; static int jobctl;
#if JOBS #if JOBS
static void
jobctl_notty(void)
{
if (ttyfd >= 0) {
close(ttyfd);
ttyfd = -1;
}
if (!iflag) {
setsignal(SIGTSTP);
setsignal(SIGTTOU);
setsignal(SIGTTIN);
jobctl = 1;
return;
}
out2fmt_flush("sh: can't access tty; job control turned off\n");
mflag = 0;
}
void void
setjobctl(int on) setjobctl(int on)
{ {
@ -133,8 +151,10 @@ setjobctl(int on)
while (i <= 2 && !isatty(i)) while (i <= 2 && !isatty(i))
i++; i++;
if (i > 2 || if (i > 2 ||
(ttyfd = fcntl(i, F_DUPFD_CLOEXEC, 10)) < 0) (ttyfd = fcntl(i, F_DUPFD_CLOEXEC, 10)) < 0) {
goto out; jobctl_notty();
return;
}
} }
if (ttyfd < 10) { if (ttyfd < 10) {
/* /*
@ -142,9 +162,8 @@ setjobctl(int on)
* the user's redirections. * the user's redirections.
*/ */
if ((i = fcntl(ttyfd, F_DUPFD_CLOEXEC, 10)) < 0) { if ((i = fcntl(ttyfd, F_DUPFD_CLOEXEC, 10)) < 0) {
close(ttyfd); jobctl_notty();
ttyfd = -1; return;
goto out;
} }
close(ttyfd); close(ttyfd);
ttyfd = i; ttyfd = i;
@ -152,11 +171,15 @@ setjobctl(int on)
do { /* while we are in the background */ do { /* while we are in the background */
initialpgrp = tcgetpgrp(ttyfd); initialpgrp = tcgetpgrp(ttyfd);
if (initialpgrp < 0) { if (initialpgrp < 0) {
out: out2fmt_flush("sh: can't access tty; job control turned off\n"); jobctl_notty();
mflag = 0;
return; return;
} }
if (initialpgrp != getpgrp()) { if (initialpgrp != getpgrp()) {
if (!iflag) {
initialpgrp = -1;
jobctl_notty();
return;
}
kill(0, SIGTTIN); kill(0, SIGTTIN);
continue; continue;
} }
@ -168,9 +191,11 @@ out: out2fmt_flush("sh: can't access tty; job control turned off\n");
tcsetpgrp(ttyfd, rootpid); tcsetpgrp(ttyfd, rootpid);
} else { /* turning job control off */ } else { /* turning job control off */
setpgid(0, initialpgrp); setpgid(0, initialpgrp);
tcsetpgrp(ttyfd, initialpgrp); if (ttyfd >= 0) {
close(ttyfd); tcsetpgrp(ttyfd, initialpgrp);
ttyfd = -1; close(ttyfd);
ttyfd = -1;
}
setsignal(SIGTSTP); setsignal(SIGTSTP);
setsignal(SIGTTOU); setsignal(SIGTTOU);
setsignal(SIGTTIN); setsignal(SIGTTIN);
@ -195,7 +220,8 @@ fgcmd(int argc __unused, char **argv __unused)
printjobcmd(jp); printjobcmd(jp);
flushout(&output); flushout(&output);
pgrp = jp->ps[0].pid; pgrp = jp->ps[0].pid;
tcsetpgrp(ttyfd, pgrp); if (ttyfd >= 0)
tcsetpgrp(ttyfd, pgrp);
restartjob(jp); restartjob(jp);
jp->foreground = 1; jp->foreground = 1;
INTOFF; INTOFF;
@ -847,7 +873,8 @@ forkshell(struct job *jp, union node *n, int mode)
pgrp = getpid(); pgrp = getpid();
else else
pgrp = jp->ps[0].pid; pgrp = jp->ps[0].pid;
if (setpgid(0, pgrp) == 0 && mode == FORK_FG) { if (setpgid(0, pgrp) == 0 && mode == FORK_FG &&
ttyfd >= 0) {
/*** this causes superfluous TIOCSPGRPS ***/ /*** this causes superfluous TIOCSPGRPS ***/
if (tcsetpgrp(ttyfd, pgrp) < 0) if (tcsetpgrp(ttyfd, pgrp) < 0)
error("tcsetpgrp failed, errno=%d", errno); error("tcsetpgrp failed, errno=%d", errno);
@ -1007,7 +1034,7 @@ waitforjob(struct job *jp, int *origstatus)
dotrap(); dotrap();
#if JOBS #if JOBS
if (jp->jobctl) { if (jp->jobctl) {
if (tcsetpgrp(ttyfd, rootpid) < 0) if (ttyfd >= 0 && tcsetpgrp(ttyfd, rootpid) < 0)
error("tcsetpgrp failed, errno=%d\n", errno); error("tcsetpgrp failed, errno=%d\n", errno);
} }
if (jp->state == JOBSTOPPED) if (jp->state == JOBSTOPPED)

View File

@ -32,7 +32,7 @@
.\" from: @(#)sh.1 8.6 (Berkeley) 5/4/95 .\" from: @(#)sh.1 8.6 (Berkeley) 5/4/95
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd January 26, 2014 .Dd September 4, 2014
.Dt SH 1 .Dt SH 1
.Os .Os
.Sh NAME .Sh NAME
@ -259,6 +259,12 @@ from input when in interactive mode.
Force the shell to behave interactively. Force the shell to behave interactively.
.It Fl m Li monitor .It Fl m Li monitor
Turn on job control (set automatically when interactive). Turn on job control (set automatically when interactive).
A new process group is created for each pipeline (called a job).
It is possible to suspend jobs or to have them run in the foreground or
in the background.
In a non-interactive shell,
this option can be set even if no terminal is available
and is useful to place processes in separate process groups.
.It Fl n Li noexec .It Fl n Li noexec
If not interactive, read commands but do not If not interactive, read commands but do not
execute them. execute them.