Blame manual/job.texi

Packit 6c4009
@node Job Control, Name Service Switch, Inter-Process Communication, Top
Packit 6c4009
@c %MENU% All about process groups and sessions
Packit 6c4009
@chapter Job Control
Packit 6c4009
Packit 6c4009
@cindex process groups
Packit 6c4009
@cindex job control
Packit 6c4009
@cindex job
Packit 6c4009
@cindex session
Packit 6c4009
@dfn{Job control} refers to the protocol for allowing a user to move
Packit 6c4009
between multiple @dfn{process groups} (or @dfn{jobs}) within a single
Packit 6c4009
@dfn{login session}.  The job control facilities are set up so that
Packit 6c4009
appropriate behavior for most programs happens automatically and they
Packit 6c4009
need not do anything special about job control.  So you can probably
Packit 6c4009
ignore the material in this chapter unless you are writing a shell or
Packit 6c4009
login program.
Packit 6c4009
Packit 6c4009
You need to be familiar with concepts relating to process creation
Packit 6c4009
(@pxref{Process Creation Concepts}) and signal handling (@pxref{Signal
Packit 6c4009
Handling}) in order to understand this material presented in this
Packit 6c4009
chapter.
Packit 6c4009
Packit 6c4009
@menu
Packit 6c4009
* Concepts of Job Control::     Jobs can be controlled by a shell.
Packit 6c4009
* Job Control is Optional::     Not all POSIX systems support job control.
Packit 6c4009
* Controlling Terminal::        How a process gets its controlling terminal.
Packit 6c4009
* Access to the Terminal::      How processes share the controlling terminal.
Packit 6c4009
* Orphaned Process Groups::     Jobs left after the user logs out.
Packit 6c4009
* Implementing a Shell::        What a shell must do to implement job control.
Packit 6c4009
* Functions for Job Control::   Functions to control process groups.
Packit 6c4009
@end menu
Packit 6c4009
Packit 6c4009
@node Concepts of Job Control, Job Control is Optional,  , Job Control
Packit 6c4009
@section Concepts of Job Control
Packit 6c4009
Packit 6c4009
@cindex shell
Packit 6c4009
The fundamental purpose of an interactive shell is to read
Packit 6c4009
commands from the user's terminal and create processes to execute the
Packit 6c4009
programs specified by those commands.  It can do this using the
Packit 6c4009
@code{fork} (@pxref{Creating a Process}) and @code{exec}
Packit 6c4009
(@pxref{Executing a File}) functions.
Packit 6c4009
Packit 6c4009
A single command may run just one process---but often one command uses
Packit 6c4009
several processes.  If you use the @samp{|} operator in a shell command,
Packit 6c4009
you explicitly request several programs in their own processes.  But
Packit 6c4009
even if you run just one program, it can use multiple processes
Packit 6c4009
internally.  For example, a single compilation command such as @samp{cc
Packit 6c4009
-c foo.c} typically uses four processes (though normally only two at any
Packit 6c4009
given time).  If you run @code{make}, its job is to run other programs
Packit 6c4009
in separate processes.
Packit 6c4009
Packit 6c4009
The processes belonging to a single command are called a @dfn{process
Packit 6c4009
group} or @dfn{job}.  This is so that you can operate on all of them at
Packit 6c4009
once.  For example, typing @kbd{C-c} sends the signal @code{SIGINT} to
Packit 6c4009
terminate all the processes in the foreground process group.
Packit 6c4009
Packit 6c4009
@cindex session
Packit 6c4009
A @dfn{session} is a larger group of processes.  Normally all the
Packit 6c4009
processes that stem from a single login belong to the same session.
Packit 6c4009
Packit 6c4009
Every process belongs to a process group.  When a process is created, it
Packit 6c4009
becomes a member of the same process group and session as its parent
Packit 6c4009
process.  You can put it in another process group using the
Packit 6c4009
@code{setpgid} function, provided the process group belongs to the same
Packit 6c4009
session.
Packit 6c4009
Packit 6c4009
@cindex session leader
Packit 6c4009
The only way to put a process in a different session is to make it the
Packit 6c4009
initial process of a new session, or a @dfn{session leader}, using the
Packit 6c4009
@code{setsid} function.  This also puts the session leader into a new
Packit 6c4009
process group, and you can't move it out of that process group again.
Packit 6c4009
Packit 6c4009
Usually, new sessions are created by the system login program, and the
Packit 6c4009
session leader is the process running the user's login shell.
Packit 6c4009
Packit 6c4009
@cindex controlling terminal
Packit 6c4009
A shell that supports job control must arrange to control which job can
Packit 6c4009
use the terminal at any time.  Otherwise there might be multiple jobs
Packit 6c4009
trying to read from the terminal at once, and confusion about which
Packit 6c4009
process should receive the input typed by the user.  To prevent this,
Packit 6c4009
the shell must cooperate with the terminal driver using the protocol
Packit 6c4009
described in this chapter.
Packit 6c4009
Packit 6c4009
@cindex foreground job
Packit 6c4009
@cindex background job
Packit 6c4009
The shell can give unlimited access to the controlling terminal to only
Packit 6c4009
one process group at a time.  This is called the @dfn{foreground job} on
Packit 6c4009
that controlling terminal.  Other process groups managed by the shell
Packit 6c4009
that are executing without such access to the terminal are called
Packit 6c4009
@dfn{background jobs}.
Packit 6c4009
Packit 6c4009
@cindex stopped job
Packit 6c4009
If a background job needs to read from its controlling
Packit 6c4009
terminal, it is @dfn{stopped} by the terminal driver; if the
Packit 6c4009
@code{TOSTOP} mode is set, likewise for writing.  The user can stop
Packit 6c4009
a foreground job by typing the SUSP character (@pxref{Special
Packit 6c4009
Characters}) and a program can stop any job by sending it a
Packit 6c4009
@code{SIGSTOP} signal.  It's the responsibility of the shell to notice
Packit 6c4009
when jobs stop, to notify the user about them, and to provide mechanisms
Packit 6c4009
for allowing the user to interactively continue stopped jobs and switch
Packit 6c4009
jobs between foreground and background.
Packit 6c4009
Packit 6c4009
@xref{Access to the Terminal}, for more information about I/O to the
Packit 6c4009
controlling terminal.
Packit 6c4009
Packit 6c4009
@node Job Control is Optional, Controlling Terminal, Concepts of Job Control , Job Control
Packit 6c4009
@section Job Control is Optional
Packit 6c4009
@cindex job control is optional
Packit 6c4009
Packit 6c4009
Not all operating systems support job control.  @gnusystems{} do
Packit 6c4009
support job control, but if you are using @theglibc{} on some other
Packit 6c4009
system, that system may not support job control itself.
Packit 6c4009
Packit 6c4009
You can use the @code{_POSIX_JOB_CONTROL} macro to test at compile-time
Packit 6c4009
whether the system supports job control.  @xref{System Options}.
Packit 6c4009
Packit 6c4009
If job control is not supported, then there can be only one process
Packit 6c4009
group per session, which behaves as if it were always in the foreground.
Packit 6c4009
The functions for creating additional process groups simply fail with
Packit 6c4009
the error code @code{ENOSYS}.
Packit 6c4009
Packit 6c4009
The macros naming the various job control signals (@pxref{Job Control
Packit 6c4009
Signals}) are defined even if job control is not supported.  However,
Packit 6c4009
the system never generates these signals, and attempts to send a job
Packit 6c4009
control signal or examine or specify their actions report errors or do
Packit 6c4009
nothing.
Packit 6c4009
Packit 6c4009
Packit 6c4009
@node Controlling Terminal, Access to the Terminal, Job Control is Optional, Job Control
Packit 6c4009
@section Controlling Terminal of a Process
Packit 6c4009
Packit 6c4009
One of the attributes of a process is its controlling terminal.  Child
Packit 6c4009
processes created with @code{fork} inherit the controlling terminal from
Packit 6c4009
their parent process.  In this way, all the processes in a session
Packit 6c4009
inherit the controlling terminal from the session leader.  A session
Packit 6c4009
leader that has control of a terminal is called the @dfn{controlling
Packit 6c4009
process} of that terminal.
Packit 6c4009
Packit 6c4009
@cindex controlling process
Packit 6c4009
You generally do not need to worry about the exact mechanism used to
Packit 6c4009
allocate a controlling terminal to a session, since it is done for you
Packit 6c4009
by the system when you log in.
Packit 6c4009
@c ??? How does GNU system let a process get a ctl terminal.
Packit 6c4009
Packit 6c4009
An individual process disconnects from its controlling terminal when it
Packit 6c4009
calls @code{setsid} to become the leader of a new session.
Packit 6c4009
@xref{Process Group Functions}.
Packit 6c4009
Packit 6c4009
@c !!! explain how it gets a new one (by opening any terminal)
Packit 6c4009
@c ??? How you get a controlling terminal is system-dependent.
Packit 6c4009
@c We should document how this will work in the GNU system when it is decided.
Packit 6c4009
@c What Unix does is not clean and I don't think GNU should use that.
Packit 6c4009
Packit 6c4009
@node Access to the Terminal, Orphaned Process Groups, Controlling Terminal, Job Control
Packit 6c4009
@section Access to the Controlling Terminal
Packit 6c4009
@cindex controlling terminal, access to
Packit 6c4009
Packit 6c4009
Processes in the foreground job of a controlling terminal have
Packit 6c4009
unrestricted access to that terminal; background processes do not.  This
Packit 6c4009
section describes in more detail what happens when a process in a
Packit 6c4009
background job tries to access its controlling terminal.
Packit 6c4009
Packit 6c4009
@cindex @code{SIGTTIN}, from background job
Packit 6c4009
When a process in a background job tries to read from its controlling
Packit 6c4009
terminal, the process group is usually sent a @code{SIGTTIN} signal.
Packit 6c4009
This normally causes all of the processes in that group to stop (unless
Packit 6c4009
they handle the signal and don't stop themselves).  However, if the
Packit 6c4009
reading process is ignoring or blocking this signal, then @code{read}
Packit 6c4009
fails with an @code{EIO} error instead.
Packit 6c4009
Packit 6c4009
@cindex @code{SIGTTOU}, from background job
Packit 6c4009
Similarly, when a process in a background job tries to write to its
Packit 6c4009
controlling terminal, the default behavior is to send a @code{SIGTTOU}
Packit 6c4009
signal to the process group.  However, the behavior is modified by the
Packit 6c4009
@code{TOSTOP} bit of the local modes flags (@pxref{Local Modes}).  If
Packit 6c4009
this bit is not set (which is the default), then writing to the
Packit 6c4009
controlling terminal is always permitted without sending a signal.
Packit 6c4009
Writing is also permitted if the @code{SIGTTOU} signal is being ignored
Packit 6c4009
or blocked by the writing process.
Packit 6c4009
Packit 6c4009
Most other terminal operations that a program can do are treated as
Packit 6c4009
reading or as writing.  (The description of each operation should say
Packit 6c4009
which.)
Packit 6c4009
Packit 6c4009
For more information about the primitive @code{read} and @code{write}
Packit 6c4009
functions, see @ref{I/O Primitives}.
Packit 6c4009
Packit 6c4009
Packit 6c4009
@node Orphaned Process Groups, Implementing a Shell, Access to the Terminal, Job Control
Packit 6c4009
@section Orphaned Process Groups
Packit 6c4009
@cindex orphaned process group
Packit 6c4009
Packit 6c4009
When a controlling process terminates, its terminal becomes free and a
Packit 6c4009
new session can be established on it.  (In fact, another user could log
Packit 6c4009
in on the terminal.)  This could cause a problem if any processes from
Packit 6c4009
the old session are still trying to use that terminal.
Packit 6c4009
Packit 6c4009
To prevent problems, process groups that continue running even after the
Packit 6c4009
session leader has terminated are marked as @dfn{orphaned process
Packit 6c4009
groups}.
Packit 6c4009
Packit 6c4009
When a process group becomes an orphan, its processes are sent a
Packit 6c4009
@code{SIGHUP} signal.  Ordinarily, this causes the processes to
Packit 6c4009
terminate.  However, if a program ignores this signal or establishes a
Packit 6c4009
handler for it (@pxref{Signal Handling}), it can continue running as in
Packit 6c4009
the orphan process group even after its controlling process terminates;
Packit 6c4009
but it still cannot access the terminal any more.
Packit 6c4009
Packit 6c4009
@node Implementing a Shell, Functions for Job Control, Orphaned Process Groups, Job Control
Packit 6c4009
@section Implementing a Job Control Shell
Packit 6c4009
Packit 6c4009
This section describes what a shell must do to implement job control, by
Packit 6c4009
presenting an extensive sample program to illustrate the concepts
Packit 6c4009
involved.
Packit 6c4009
Packit 6c4009
@iftex
Packit 6c4009
@itemize @bullet
Packit 6c4009
@item
Packit 6c4009
@ref{Data Structures}, introduces the example and presents
Packit 6c4009
its primary data structures.
Packit 6c4009
Packit 6c4009
@item
Packit 6c4009
@ref{Initializing the Shell}, discusses actions which the shell must
Packit 6c4009
perform to prepare for job control.
Packit 6c4009
Packit 6c4009
@item
Packit 6c4009
@ref{Launching Jobs}, includes information about how to create jobs
Packit 6c4009
to execute commands.
Packit 6c4009
Packit 6c4009
@item
Packit 6c4009
@ref{Foreground and Background}, discusses what the shell should
Packit 6c4009
do differently when launching a job in the foreground as opposed to
Packit 6c4009
a background job.
Packit 6c4009
Packit 6c4009
@item
Packit 6c4009
@ref{Stopped and Terminated Jobs}, discusses reporting of job status
Packit 6c4009
back to the shell.
Packit 6c4009
Packit 6c4009
@item
Packit 6c4009
@ref{Continuing Stopped Jobs}, tells you how to continue jobs that
Packit 6c4009
have been stopped.
Packit 6c4009
Packit 6c4009
@item
Packit 6c4009
@ref{Missing Pieces}, discusses other parts of the shell.
Packit 6c4009
@end itemize
Packit 6c4009
@end iftex
Packit 6c4009
Packit 6c4009
@menu
Packit 6c4009
* Data Structures::             Introduction to the sample shell.
Packit 6c4009
* Initializing the Shell::      What the shell must do to take
Packit 6c4009
				 responsibility for job control.
Packit 6c4009
* Launching Jobs::              Creating jobs to execute commands.
Packit 6c4009
* Foreground and Background::   Putting a job in foreground of background.
Packit 6c4009
* Stopped and Terminated Jobs::  Reporting job status.
Packit 6c4009
* Continuing Stopped Jobs::     How to continue a stopped job in
Packit 6c4009
				 the foreground or background.
Packit 6c4009
* Missing Pieces::              Other parts of the shell.
Packit 6c4009
@end menu
Packit 6c4009
Packit 6c4009
@node Data Structures, Initializing the Shell,  , Implementing a Shell
Packit 6c4009
@subsection Data Structures for the Shell
Packit 6c4009
Packit 6c4009
All of the program examples included in this chapter are part of
Packit 6c4009
a simple shell program.  This section presents data structures
Packit 6c4009
and utility functions which are used throughout the example.
Packit 6c4009
Packit 6c4009
The sample shell deals mainly with two data structures.  The
Packit 6c4009
@code{job} type contains information about a job, which is a
Packit 6c4009
set of subprocesses linked together with pipes.  The @code{process} type
Packit 6c4009
holds information about a single subprocess.  Here are the relevant
Packit 6c4009
data structure declarations:
Packit 6c4009
Packit 6c4009
@smallexample
Packit 6c4009
@group
Packit 6c4009
/* @r{A process is a single process.}  */
Packit 6c4009
typedef struct process
Packit 6c4009
@{
Packit 6c4009
  struct process *next;       /* @r{next process in pipeline} */
Packit 6c4009
  char **argv;                /* @r{for exec} */
Packit 6c4009
  pid_t pid;                  /* @r{process ID} */
Packit 6c4009
  char completed;             /* @r{true if process has completed} */
Packit 6c4009
  char stopped;               /* @r{true if process has stopped} */
Packit 6c4009
  int status;                 /* @r{reported status value} */
Packit 6c4009
@} process;
Packit 6c4009
@end group
Packit 6c4009
Packit 6c4009
@group
Packit 6c4009
/* @r{A job is a pipeline of processes.}  */
Packit 6c4009
typedef struct job
Packit 6c4009
@{
Packit 6c4009
  struct job *next;           /* @r{next active job} */
Packit 6c4009
  char *command;              /* @r{command line, used for messages} */
Packit 6c4009
  process *first_process;     /* @r{list of processes in this job} */
Packit 6c4009
  pid_t pgid;                 /* @r{process group ID} */
Packit 6c4009
  char notified;              /* @r{true if user told about stopped job} */
Packit 6c4009
  struct termios tmodes;      /* @r{saved terminal modes} */
Packit 6c4009
  int stdin, stdout, stderr;  /* @r{standard i/o channels} */
Packit 6c4009
@} job;
Packit 6c4009
Packit 6c4009
/* @r{The active jobs are linked into a list.  This is its head.}   */
Packit 6c4009
job *first_job = NULL;
Packit 6c4009
@end group
Packit 6c4009
@end smallexample
Packit 6c4009
Packit 6c4009
Here are some utility functions that are used for operating on @code{job}
Packit 6c4009
objects.
Packit 6c4009
Packit 6c4009
@smallexample
Packit 6c4009
@group
Packit 6c4009
/* @r{Find the active job with the indicated @var{pgid}.}  */
Packit 6c4009
job *
Packit 6c4009
find_job (pid_t pgid)
Packit 6c4009
@{
Packit 6c4009
  job *j;
Packit 6c4009
Packit 6c4009
  for (j = first_job; j; j = j->next)
Packit 6c4009
    if (j->pgid == pgid)
Packit 6c4009
      return j;
Packit 6c4009
  return NULL;
Packit 6c4009
@}
Packit 6c4009
@end group
Packit 6c4009
Packit 6c4009
@group
Packit 6c4009
/* @r{Return true if all processes in the job have stopped or completed.}  */
Packit 6c4009
int
Packit 6c4009
job_is_stopped (job *j)
Packit 6c4009
@{
Packit 6c4009
  process *p;
Packit 6c4009
Packit 6c4009
  for (p = j->first_process; p; p = p->next)
Packit 6c4009
    if (!p->completed && !p->stopped)
Packit 6c4009
      return 0;
Packit 6c4009
  return 1;
Packit 6c4009
@}
Packit 6c4009
@end group
Packit 6c4009
Packit 6c4009
@group
Packit 6c4009
/* @r{Return true if all processes in the job have completed.}  */
Packit 6c4009
int
Packit 6c4009
job_is_completed (job *j)
Packit 6c4009
@{
Packit 6c4009
  process *p;
Packit 6c4009
Packit 6c4009
  for (p = j->first_process; p; p = p->next)
Packit 6c4009
    if (!p->completed)
Packit 6c4009
      return 0;
Packit 6c4009
  return 1;
Packit 6c4009
@}
Packit 6c4009
@end group
Packit 6c4009
@end smallexample
Packit 6c4009
Packit 6c4009
Packit 6c4009
@node Initializing the Shell, Launching Jobs, Data Structures, Implementing a Shell
Packit 6c4009
@subsection Initializing the Shell
Packit 6c4009
@cindex job control, enabling
Packit 6c4009
@cindex subshell
Packit 6c4009
Packit 6c4009
When a shell program that normally performs job control is started, it
Packit 6c4009
has to be careful in case it has been invoked from another shell that is
Packit 6c4009
already doing its own job control.
Packit 6c4009
Packit 6c4009
A subshell that runs interactively has to ensure that it has been placed
Packit 6c4009
in the foreground by its parent shell before it can enable job control
Packit 6c4009
itself.  It does this by getting its initial process group ID with the
Packit 6c4009
@code{getpgrp} function, and comparing it to the process group ID of the
Packit 6c4009
current foreground job associated with its controlling terminal (which
Packit 6c4009
can be retrieved using the @code{tcgetpgrp} function).
Packit 6c4009
Packit 6c4009
If the subshell is not running as a foreground job, it must stop itself
Packit 6c4009
by sending a @code{SIGTTIN} signal to its own process group.  It may not
Packit 6c4009
arbitrarily put itself into the foreground; it must wait for the user to
Packit 6c4009
tell the parent shell to do this.  If the subshell is continued again,
Packit 6c4009
it should repeat the check and stop itself again if it is still not in
Packit 6c4009
the foreground.
Packit 6c4009
Packit 6c4009
@cindex job control, enabling
Packit 6c4009
Once the subshell has been placed into the foreground by its parent
Packit 6c4009
shell, it can enable its own job control.  It does this by calling
Packit 6c4009
@code{setpgid} to put itself into its own process group, and then
Packit 6c4009
calling @code{tcsetpgrp} to place this process group into the
Packit 6c4009
foreground.
Packit 6c4009
Packit 6c4009
When a shell enables job control, it should set itself to ignore all the
Packit 6c4009
job control stop signals so that it doesn't accidentally stop itself.
Packit 6c4009
You can do this by setting the action for all the stop signals to
Packit 6c4009
@code{SIG_IGN}.
Packit 6c4009
Packit 6c4009
A subshell that runs non-interactively cannot and should not support job
Packit 6c4009
control.  It must leave all processes it creates in the same process
Packit 6c4009
group as the shell itself; this allows the non-interactive shell and its
Packit 6c4009
child processes to be treated as a single job by the parent shell.  This
Packit 6c4009
is easy to do---just don't use any of the job control primitives---but
Packit 6c4009
you must remember to make the shell do it.
Packit 6c4009
Packit 6c4009
Packit 6c4009
Here is the initialization code for the sample shell that shows how to
Packit 6c4009
do all of this.
Packit 6c4009
Packit 6c4009
@smallexample
Packit 6c4009
/* @r{Keep track of attributes of the shell.}  */
Packit 6c4009
Packit 6c4009
#include <sys/types.h>
Packit 6c4009
#include <termios.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
Packit 6c4009
pid_t shell_pgid;
Packit 6c4009
struct termios shell_tmodes;
Packit 6c4009
int shell_terminal;
Packit 6c4009
int shell_is_interactive;
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* @r{Make sure the shell is running interactively as the foreground job}
Packit 6c4009
   @r{before proceeding.} */
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
init_shell ()
Packit 6c4009
@{
Packit 6c4009
Packit 6c4009
  /* @r{See if we are running interactively.}  */
Packit 6c4009
  shell_terminal = STDIN_FILENO;
Packit 6c4009
  shell_is_interactive = isatty (shell_terminal);
Packit 6c4009
Packit 6c4009
  if (shell_is_interactive)
Packit 6c4009
    @{
Packit 6c4009
      /* @r{Loop until we are in the foreground.}  */
Packit 6c4009
      while (tcgetpgrp (shell_terminal) != (shell_pgid = getpgrp ()))
Packit 6c4009
        kill (- shell_pgid, SIGTTIN);
Packit 6c4009
Packit 6c4009
      /* @r{Ignore interactive and job-control signals.}  */
Packit 6c4009
      signal (SIGINT, SIG_IGN);
Packit 6c4009
      signal (SIGQUIT, SIG_IGN);
Packit 6c4009
      signal (SIGTSTP, SIG_IGN);
Packit 6c4009
      signal (SIGTTIN, SIG_IGN);
Packit 6c4009
      signal (SIGTTOU, SIG_IGN);
Packit 6c4009
      signal (SIGCHLD, SIG_IGN);
Packit 6c4009
Packit 6c4009
      /* @r{Put ourselves in our own process group.}  */
Packit 6c4009
      shell_pgid = getpid ();
Packit 6c4009
      if (setpgid (shell_pgid, shell_pgid) < 0)
Packit 6c4009
        @{
Packit 6c4009
          perror ("Couldn't put the shell in its own process group");
Packit 6c4009
          exit (1);
Packit 6c4009
        @}
Packit 6c4009
Packit 6c4009
      /* @r{Grab control of the terminal.}  */
Packit 6c4009
      tcsetpgrp (shell_terminal, shell_pgid);
Packit 6c4009
Packit 6c4009
      /* @r{Save default terminal attributes for shell.}  */
Packit 6c4009
      tcgetattr (shell_terminal, &shell_tmodes);
Packit 6c4009
    @}
Packit 6c4009
@}
Packit 6c4009
@end smallexample
Packit 6c4009
Packit 6c4009
Packit 6c4009
@node Launching Jobs, Foreground and Background, Initializing the Shell, Implementing a Shell
Packit 6c4009
@subsection Launching Jobs
Packit 6c4009
@cindex launching jobs
Packit 6c4009
Packit 6c4009
Once the shell has taken responsibility for performing job control on
Packit 6c4009
its controlling terminal, it can launch jobs in response to commands
Packit 6c4009
typed by the user.
Packit 6c4009
Packit 6c4009
To create the processes in a process group, you use the same @code{fork}
Packit 6c4009
and @code{exec} functions described in @ref{Process Creation Concepts}.
Packit 6c4009
Since there are multiple child processes involved, though, things are a
Packit 6c4009
little more complicated and you must be careful to do things in the
Packit 6c4009
right order.  Otherwise, nasty race conditions can result.
Packit 6c4009
Packit 6c4009
You have two choices for how to structure the tree of parent-child
Packit 6c4009
relationships among the processes.  You can either make all the
Packit 6c4009
processes in the process group be children of the shell process, or you
Packit 6c4009
can make one process in group be the ancestor of all the other processes
Packit 6c4009
in that group.  The sample shell program presented in this chapter uses
Packit 6c4009
the first approach because it makes bookkeeping somewhat simpler.
Packit 6c4009
Packit 6c4009
@cindex process group leader
Packit 6c4009
@cindex process group ID
Packit 6c4009
As each process is forked, it should put itself in the new process group
Packit 6c4009
by calling @code{setpgid}; see @ref{Process Group Functions}.  The first
Packit 6c4009
process in the new group becomes its @dfn{process group leader}, and its
Packit 6c4009
process ID becomes the @dfn{process group ID} for the group.
Packit 6c4009
Packit 6c4009
@cindex race conditions, relating to job control
Packit 6c4009
The shell should also call @code{setpgid} to put each of its child
Packit 6c4009
processes into the new process group.  This is because there is a
Packit 6c4009
potential timing problem: each child process must be put in the process
Packit 6c4009
group before it begins executing a new program, and the shell depends on
Packit 6c4009
having all the child processes in the group before it continues
Packit 6c4009
executing.  If both the child processes and the shell call
Packit 6c4009
@code{setpgid}, this ensures that the right things happen no matter which
Packit 6c4009
process gets to it first.
Packit 6c4009
Packit 6c4009
If the job is being launched as a foreground job, the new process group
Packit 6c4009
also needs to be put into the foreground on the controlling terminal
Packit 6c4009
using @code{tcsetpgrp}.  Again, this should be done by the shell as well
Packit 6c4009
as by each of its child processes, to avoid race conditions.
Packit 6c4009
Packit 6c4009
The next thing each child process should do is to reset its signal
Packit 6c4009
actions.
Packit 6c4009
Packit 6c4009
During initialization, the shell process set itself to ignore job
Packit 6c4009
control signals; see @ref{Initializing the Shell}.  As a result, any child
Packit 6c4009
processes it creates also ignore these signals by inheritance.  This is
Packit 6c4009
definitely undesirable, so each child process should explicitly set the
Packit 6c4009
actions for these signals back to @code{SIG_DFL} just after it is forked.
Packit 6c4009
Packit 6c4009
Since shells follow this convention, applications can assume that they
Packit 6c4009
inherit the correct handling of these signals from the parent process.
Packit 6c4009
But every application has a responsibility not to mess up the handling
Packit 6c4009
of stop signals.  Applications that disable the normal interpretation of
Packit 6c4009
the SUSP character should provide some other mechanism for the user to
Packit 6c4009
stop the job.  When the user invokes this mechanism, the program should
Packit 6c4009
send a @code{SIGTSTP} signal to the process group of the process, not
Packit 6c4009
just to the process itself.  @xref{Signaling Another Process}.
Packit 6c4009
Packit 6c4009
Finally, each child process should call @code{exec} in the normal way.
Packit 6c4009
This is also the point at which redirection of the standard input and
Packit 6c4009
output channels should be handled.  @xref{Duplicating Descriptors},
Packit 6c4009
for an explanation of how to do this.
Packit 6c4009
Packit 6c4009
Here is the function from the sample shell program that is responsible
Packit 6c4009
for launching a program.  The function is executed by each child process
Packit 6c4009
immediately after it has been forked by the shell, and never returns.
Packit 6c4009
Packit 6c4009
@smallexample
Packit 6c4009
void
Packit 6c4009
launch_process (process *p, pid_t pgid,
Packit 6c4009
                int infile, int outfile, int errfile,
Packit 6c4009
                int foreground)
Packit 6c4009
@{
Packit 6c4009
  pid_t pid;
Packit 6c4009
Packit 6c4009
  if (shell_is_interactive)
Packit 6c4009
    @{
Packit 6c4009
      /* @r{Put the process into the process group and give the process group}
Packit 6c4009
         @r{the terminal, if appropriate.}
Packit 6c4009
         @r{This has to be done both by the shell and in the individual}
Packit 6c4009
         @r{child processes because of potential race conditions.}  */
Packit 6c4009
      pid = getpid ();
Packit 6c4009
      if (pgid == 0) pgid = pid;
Packit 6c4009
      setpgid (pid, pgid);
Packit 6c4009
      if (foreground)
Packit 6c4009
        tcsetpgrp (shell_terminal, pgid);
Packit 6c4009
Packit 6c4009
      /* @r{Set the handling for job control signals back to the default.}  */
Packit 6c4009
      signal (SIGINT, SIG_DFL);
Packit 6c4009
      signal (SIGQUIT, SIG_DFL);
Packit 6c4009
      signal (SIGTSTP, SIG_DFL);
Packit 6c4009
      signal (SIGTTIN, SIG_DFL);
Packit 6c4009
      signal (SIGTTOU, SIG_DFL);
Packit 6c4009
      signal (SIGCHLD, SIG_DFL);
Packit 6c4009
    @}
Packit 6c4009
Packit 6c4009
  /* @r{Set the standard input/output channels of the new process.}  */
Packit 6c4009
  if (infile != STDIN_FILENO)
Packit 6c4009
    @{
Packit 6c4009
      dup2 (infile, STDIN_FILENO);
Packit 6c4009
      close (infile);
Packit 6c4009
    @}
Packit 6c4009
  if (outfile != STDOUT_FILENO)
Packit 6c4009
    @{
Packit 6c4009
      dup2 (outfile, STDOUT_FILENO);
Packit 6c4009
      close (outfile);
Packit 6c4009
    @}
Packit 6c4009
  if (errfile != STDERR_FILENO)
Packit 6c4009
    @{
Packit 6c4009
      dup2 (errfile, STDERR_FILENO);
Packit 6c4009
      close (errfile);
Packit 6c4009
    @}
Packit 6c4009
Packit 6c4009
  /* @r{Exec the new process.  Make sure we exit.}  */
Packit 6c4009
  execvp (p->argv[0], p->argv);
Packit 6c4009
  perror ("execvp");
Packit 6c4009
  exit (1);
Packit 6c4009
@}
Packit 6c4009
@end smallexample
Packit 6c4009
Packit 6c4009
If the shell is not running interactively, this function does not do
Packit 6c4009
anything with process groups or signals.  Remember that a shell not
Packit 6c4009
performing job control must keep all of its subprocesses in the same
Packit 6c4009
process group as the shell itself.
Packit 6c4009
Packit 6c4009
Next, here is the function that actually launches a complete job.
Packit 6c4009
After creating the child processes, this function calls some other
Packit 6c4009
functions to put the newly created job into the foreground or background;
Packit 6c4009
these are discussed in @ref{Foreground and Background}.
Packit 6c4009
Packit 6c4009
@smallexample
Packit 6c4009
void
Packit 6c4009
launch_job (job *j, int foreground)
Packit 6c4009
@{
Packit 6c4009
  process *p;
Packit 6c4009
  pid_t pid;
Packit 6c4009
  int mypipe[2], infile, outfile;
Packit 6c4009
Packit 6c4009
  infile = j->stdin;
Packit 6c4009
  for (p = j->first_process; p; p = p->next)
Packit 6c4009
    @{
Packit 6c4009
      /* @r{Set up pipes, if necessary.}  */
Packit 6c4009
      if (p->next)
Packit 6c4009
        @{
Packit 6c4009
          if (pipe (mypipe) < 0)
Packit 6c4009
            @{
Packit 6c4009
              perror ("pipe");
Packit 6c4009
              exit (1);
Packit 6c4009
            @}
Packit 6c4009
          outfile = mypipe[1];
Packit 6c4009
        @}
Packit 6c4009
      else
Packit 6c4009
        outfile = j->stdout;
Packit 6c4009
Packit 6c4009
      /* @r{Fork the child processes.}  */
Packit 6c4009
      pid = fork ();
Packit 6c4009
      if (pid == 0)
Packit 6c4009
        /* @r{This is the child process.}  */
Packit 6c4009
        launch_process (p, j->pgid, infile,
Packit 6c4009
                        outfile, j->stderr, foreground);
Packit 6c4009
      else if (pid < 0)
Packit 6c4009
        @{
Packit 6c4009
          /* @r{The fork failed.}  */
Packit 6c4009
          perror ("fork");
Packit 6c4009
          exit (1);
Packit 6c4009
        @}
Packit 6c4009
      else
Packit 6c4009
        @{
Packit 6c4009
          /* @r{This is the parent process.}  */
Packit 6c4009
          p->pid = pid;
Packit 6c4009
          if (shell_is_interactive)
Packit 6c4009
            @{
Packit 6c4009
              if (!j->pgid)
Packit 6c4009
                j->pgid = pid;
Packit 6c4009
              setpgid (pid, j->pgid);
Packit 6c4009
            @}
Packit 6c4009
        @}
Packit 6c4009
Packit 6c4009
      /* @r{Clean up after pipes.}  */
Packit 6c4009
      if (infile != j->stdin)
Packit 6c4009
        close (infile);
Packit 6c4009
      if (outfile != j->stdout)
Packit 6c4009
        close (outfile);
Packit 6c4009
      infile = mypipe[0];
Packit 6c4009
    @}
Packit 6c4009
Packit 6c4009
  format_job_info (j, "launched");
Packit 6c4009
Packit 6c4009
  if (!shell_is_interactive)
Packit 6c4009
    wait_for_job (j);
Packit 6c4009
  else if (foreground)
Packit 6c4009
    put_job_in_foreground (j, 0);
Packit 6c4009
  else
Packit 6c4009
    put_job_in_background (j, 0);
Packit 6c4009
@}
Packit 6c4009
@end smallexample
Packit 6c4009
Packit 6c4009
Packit 6c4009
@node Foreground and Background, Stopped and Terminated Jobs, Launching Jobs, Implementing a Shell
Packit 6c4009
@subsection Foreground and Background
Packit 6c4009
Packit 6c4009
Now let's consider what actions must be taken by the shell when it
Packit 6c4009
launches a job into the foreground, and how this differs from what
Packit 6c4009
must be done when a background job is launched.
Packit 6c4009
Packit 6c4009
@cindex foreground job, launching
Packit 6c4009
When a foreground job is launched, the shell must first give it access
Packit 6c4009
to the controlling terminal by calling @code{tcsetpgrp}.  Then, the
Packit 6c4009
shell should wait for processes in that process group to terminate or
Packit 6c4009
stop.  This is discussed in more detail in @ref{Stopped and Terminated
Packit 6c4009
Jobs}.
Packit 6c4009
Packit 6c4009
When all of the processes in the group have either completed or stopped,
Packit 6c4009
the shell should regain control of the terminal for its own process
Packit 6c4009
group by calling @code{tcsetpgrp} again.  Since stop signals caused by
Packit 6c4009
I/O from a background process or a SUSP character typed by the user
Packit 6c4009
are sent to the process group, normally all the processes in the job
Packit 6c4009
stop together.
Packit 6c4009
Packit 6c4009
The foreground job may have left the terminal in a strange state, so the
Packit 6c4009
shell should restore its own saved terminal modes before continuing.  In
Packit 6c4009
case the job is merely stopped, the shell should first save the current
Packit 6c4009
terminal modes so that it can restore them later if the job is
Packit 6c4009
continued.  The functions for dealing with terminal modes are
Packit 6c4009
@code{tcgetattr} and @code{tcsetattr}; these are described in
Packit 6c4009
@ref{Terminal Modes}.
Packit 6c4009
Packit 6c4009
Here is the sample shell's function for doing all of this.
Packit 6c4009
Packit 6c4009
@smallexample
Packit 6c4009
@group
Packit 6c4009
/* @r{Put job @var{j} in the foreground.  If @var{cont} is nonzero,}
Packit 6c4009
   @r{restore the saved terminal modes and send the process group a}
Packit 6c4009
   @r{@code{SIGCONT} signal to wake it up before we block.}  */
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
put_job_in_foreground (job *j, int cont)
Packit 6c4009
@{
Packit 6c4009
  /* @r{Put the job into the foreground.}  */
Packit 6c4009
  tcsetpgrp (shell_terminal, j->pgid);
Packit 6c4009
@end group
Packit 6c4009
Packit 6c4009
@group
Packit 6c4009
  /* @r{Send the job a continue signal, if necessary.}  */
Packit 6c4009
  if (cont)
Packit 6c4009
    @{
Packit 6c4009
      tcsetattr (shell_terminal, TCSADRAIN, &j->tmodes);
Packit 6c4009
      if (kill (- j->pgid, SIGCONT) < 0)
Packit 6c4009
        perror ("kill (SIGCONT)");
Packit 6c4009
    @}
Packit 6c4009
@end group
Packit 6c4009
Packit 6c4009
  /* @r{Wait for it to report.}  */
Packit 6c4009
  wait_for_job (j);
Packit 6c4009
Packit 6c4009
  /* @r{Put the shell back in the foreground.}  */
Packit 6c4009
  tcsetpgrp (shell_terminal, shell_pgid);
Packit 6c4009
Packit 6c4009
@group
Packit 6c4009
  /* @r{Restore the shell's terminal modes.}  */
Packit 6c4009
  tcgetattr (shell_terminal, &j->tmodes);
Packit 6c4009
  tcsetattr (shell_terminal, TCSADRAIN, &shell_tmodes);
Packit 6c4009
@}
Packit 6c4009
@end group
Packit 6c4009
@end smallexample
Packit 6c4009
Packit 6c4009
@cindex background job, launching
Packit 6c4009
If the process group is launched as a background job, the shell should
Packit 6c4009
remain in the foreground itself and continue to read commands from
Packit 6c4009
the terminal.
Packit 6c4009
Packit 6c4009
In the sample shell, there is not much that needs to be done to put
Packit 6c4009
a job into the background.  Here is the function it uses:
Packit 6c4009
Packit 6c4009
@smallexample
Packit 6c4009
/* @r{Put a job in the background.  If the cont argument is true, send}
Packit 6c4009
   @r{the process group a @code{SIGCONT} signal to wake it up.}  */
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
put_job_in_background (job *j, int cont)
Packit 6c4009
@{
Packit 6c4009
  /* @r{Send the job a continue signal, if necessary.}  */
Packit 6c4009
  if (cont)
Packit 6c4009
    if (kill (-j->pgid, SIGCONT) < 0)
Packit 6c4009
      perror ("kill (SIGCONT)");
Packit 6c4009
@}
Packit 6c4009
@end smallexample
Packit 6c4009
Packit 6c4009
Packit 6c4009
@node Stopped and Terminated Jobs, Continuing Stopped Jobs, Foreground and Background, Implementing a Shell
Packit 6c4009
@subsection Stopped and Terminated Jobs
Packit 6c4009
Packit 6c4009
@cindex stopped jobs, detecting
Packit 6c4009
@cindex terminated jobs, detecting
Packit 6c4009
When a foreground process is launched, the shell must block until all of
Packit 6c4009
the processes in that job have either terminated or stopped.  It can do
Packit 6c4009
this by calling the @code{waitpid} function; see @ref{Process
Packit 6c4009
Completion}.  Use the @code{WUNTRACED} option so that status is reported
Packit 6c4009
for processes that stop as well as processes that terminate.
Packit 6c4009
Packit 6c4009
The shell must also check on the status of background jobs so that it
Packit 6c4009
can report terminated and stopped jobs to the user; this can be done by
Packit 6c4009
calling @code{waitpid} with the @code{WNOHANG} option.  A good place to
Packit 6c4009
put a such a check for terminated and stopped jobs is just before
Packit 6c4009
prompting for a new command.
Packit 6c4009
Packit 6c4009
@cindex @code{SIGCHLD}, handling of
Packit 6c4009
The shell can also receive asynchronous notification that there is
Packit 6c4009
status information available for a child process by establishing a
Packit 6c4009
handler for @code{SIGCHLD} signals.  @xref{Signal Handling}.
Packit 6c4009
Packit 6c4009
In the sample shell program, the @code{SIGCHLD} signal is normally
Packit 6c4009
ignored.  This is to avoid reentrancy problems involving the global data
Packit 6c4009
structures the shell manipulates.  But at specific times when the shell
Packit 6c4009
is not using these data structures---such as when it is waiting for
Packit 6c4009
input on the terminal---it makes sense to enable a handler for
Packit 6c4009
@code{SIGCHLD}.  The same function that is used to do the synchronous
Packit 6c4009
status checks (@code{do_job_notification}, in this case) can also be
Packit 6c4009
called from within this handler.
Packit 6c4009
Packit 6c4009
Here are the parts of the sample shell program that deal with checking
Packit 6c4009
the status of jobs and reporting the information to the user.
Packit 6c4009
Packit 6c4009
@smallexample
Packit 6c4009
@group
Packit 6c4009
/* @r{Store the status of the process @var{pid} that was returned by waitpid.}
Packit 6c4009
   @r{Return 0 if all went well, nonzero otherwise.}  */
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
mark_process_status (pid_t pid, int status)
Packit 6c4009
@{
Packit 6c4009
  job *j;
Packit 6c4009
  process *p;
Packit 6c4009
@end group
Packit 6c4009
Packit 6c4009
@group
Packit 6c4009
  if (pid > 0)
Packit 6c4009
    @{
Packit 6c4009
      /* @r{Update the record for the process.}  */
Packit 6c4009
      for (j = first_job; j; j = j->next)
Packit 6c4009
        for (p = j->first_process; p; p = p->next)
Packit 6c4009
          if (p->pid == pid)
Packit 6c4009
            @{
Packit 6c4009
              p->status = status;
Packit 6c4009
              if (WIFSTOPPED (status))
Packit 6c4009
                p->stopped = 1;
Packit 6c4009
              else
Packit 6c4009
                @{
Packit 6c4009
                  p->completed = 1;
Packit 6c4009
                  if (WIFSIGNALED (status))
Packit 6c4009
                    fprintf (stderr, "%d: Terminated by signal %d.\n",
Packit 6c4009
                             (int) pid, WTERMSIG (p->status));
Packit 6c4009
                @}
Packit 6c4009
              return 0;
Packit 6c4009
             @}
Packit 6c4009
      fprintf (stderr, "No child process %d.\n", pid);
Packit 6c4009
      return -1;
Packit 6c4009
    @}
Packit 6c4009
@end group
Packit 6c4009
@group
Packit 6c4009
  else if (pid == 0 || errno == ECHILD)
Packit 6c4009
    /* @r{No processes ready to report.}  */
Packit 6c4009
    return -1;
Packit 6c4009
  else @{
Packit 6c4009
    /* @r{Other weird errors.}  */
Packit 6c4009
    perror ("waitpid");
Packit 6c4009
    return -1;
Packit 6c4009
  @}
Packit 6c4009
@}
Packit 6c4009
@end group
Packit 6c4009
Packit 6c4009
@group
Packit 6c4009
/* @r{Check for processes that have status information available,}
Packit 6c4009
   @r{without blocking.}  */
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
update_status (void)
Packit 6c4009
@{
Packit 6c4009
  int status;
Packit 6c4009
  pid_t pid;
Packit 6c4009
Packit 6c4009
  do
Packit 6c4009
    pid = waitpid (WAIT_ANY, &status, WUNTRACED|WNOHANG);
Packit 6c4009
  while (!mark_process_status (pid, status));
Packit 6c4009
@}
Packit 6c4009
@end group
Packit 6c4009
Packit 6c4009
@group
Packit 6c4009
/* @r{Check for processes that have status information available,}
Packit 6c4009
   @r{blocking until all processes in the given job have reported.}  */
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
wait_for_job (job *j)
Packit 6c4009
@{
Packit 6c4009
  int status;
Packit 6c4009
  pid_t pid;
Packit 6c4009
Packit 6c4009
  do
Packit 6c4009
    pid = waitpid (WAIT_ANY, &status, WUNTRACED);
Packit 6c4009
  while (!mark_process_status (pid, status)
Packit 6c4009
         && !job_is_stopped (j)
Packit 6c4009
         && !job_is_completed (j));
Packit 6c4009
@}
Packit 6c4009
@end group
Packit 6c4009
Packit 6c4009
@group
Packit 6c4009
/* @r{Format information about job status for the user to look at.}  */
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
format_job_info (job *j, const char *status)
Packit 6c4009
@{
Packit 6c4009
  fprintf (stderr, "%ld (%s): %s\n", (long)j->pgid, status, j->command);
Packit 6c4009
@}
Packit 6c4009
@end group
Packit 6c4009
Packit 6c4009
@group
Packit 6c4009
/* @r{Notify the user about stopped or terminated jobs.}
Packit 6c4009
   @r{Delete terminated jobs from the active job list.}  */
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
do_job_notification (void)
Packit 6c4009
@{
Packit 6c4009
  job *j, *jlast, *jnext;
Packit 6c4009
  process *p;
Packit 6c4009
Packit 6c4009
  /* @r{Update status information for child processes.}  */
Packit 6c4009
  update_status ();
Packit 6c4009
Packit 6c4009
  jlast = NULL;
Packit 6c4009
  for (j = first_job; j; j = jnext)
Packit 6c4009
    @{
Packit 6c4009
      jnext = j->next;
Packit 6c4009
Packit 6c4009
      /* @r{If all processes have completed, tell the user the job has}
Packit 6c4009
         @r{completed and delete it from the list of active jobs.}  */
Packit 6c4009
      if (job_is_completed (j)) @{
Packit 6c4009
        format_job_info (j, "completed");
Packit 6c4009
        if (jlast)
Packit 6c4009
          jlast->next = jnext;
Packit 6c4009
        else
Packit 6c4009
          first_job = jnext;
Packit 6c4009
        free_job (j);
Packit 6c4009
      @}
Packit 6c4009
Packit 6c4009
      /* @r{Notify the user about stopped jobs,}
Packit 6c4009
         @r{marking them so that we won't do this more than once.}  */
Packit 6c4009
      else if (job_is_stopped (j) && !j->notified) @{
Packit 6c4009
        format_job_info (j, "stopped");
Packit 6c4009
        j->notified = 1;
Packit 6c4009
        jlast = j;
Packit 6c4009
      @}
Packit 6c4009
Packit 6c4009
      /* @r{Don't say anything about jobs that are still running.}  */
Packit 6c4009
      else
Packit 6c4009
        jlast = j;
Packit 6c4009
    @}
Packit 6c4009
@}
Packit 6c4009
@end group
Packit 6c4009
@end smallexample
Packit 6c4009
Packit 6c4009
@node Continuing Stopped Jobs, Missing Pieces, Stopped and Terminated Jobs, Implementing a Shell
Packit 6c4009
@subsection Continuing Stopped Jobs
Packit 6c4009
Packit 6c4009
@cindex stopped jobs, continuing
Packit 6c4009
The shell can continue a stopped job by sending a @code{SIGCONT} signal
Packit 6c4009
to its process group.  If the job is being continued in the foreground,
Packit 6c4009
the shell should first invoke @code{tcsetpgrp} to give the job access to
Packit 6c4009
the terminal, and restore the saved terminal settings.  After continuing
Packit 6c4009
a job in the foreground, the shell should wait for the job to stop or
Packit 6c4009
complete, as if the job had just been launched in the foreground.
Packit 6c4009
Packit 6c4009
The sample shell program handles both newly created and continued jobs
Packit 6c4009
with the same pair of functions, @w{@code{put_job_in_foreground}} and
Packit 6c4009
@w{@code{put_job_in_background}}.  The definitions of these functions
Packit 6c4009
were given in @ref{Foreground and Background}.  When continuing a
Packit 6c4009
stopped job, a nonzero value is passed as the @var{cont} argument to
Packit 6c4009
ensure that the @code{SIGCONT} signal is sent and the terminal modes
Packit 6c4009
reset, as appropriate.
Packit 6c4009
Packit 6c4009
This leaves only a function for updating the shell's internal bookkeeping
Packit 6c4009
about the job being continued:
Packit 6c4009
Packit 6c4009
@smallexample
Packit 6c4009
@group
Packit 6c4009
/* @r{Mark a stopped job J as being running again.}  */
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
mark_job_as_running (job *j)
Packit 6c4009
@{
Packit 6c4009
  Process *p;
Packit 6c4009
Packit 6c4009
  for (p = j->first_process; p; p = p->next)
Packit 6c4009
    p->stopped = 0;
Packit 6c4009
  j->notified = 0;
Packit 6c4009
@}
Packit 6c4009
@end group
Packit 6c4009
Packit 6c4009
@group
Packit 6c4009
/* @r{Continue the job J.}  */
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
continue_job (job *j, int foreground)
Packit 6c4009
@{
Packit 6c4009
  mark_job_as_running (j);
Packit 6c4009
  if (foreground)
Packit 6c4009
    put_job_in_foreground (j, 1);
Packit 6c4009
  else
Packit 6c4009
    put_job_in_background (j, 1);
Packit 6c4009
@}
Packit 6c4009
@end group
Packit 6c4009
@end smallexample
Packit 6c4009
Packit 6c4009
@node Missing Pieces,  , Continuing Stopped Jobs, Implementing a Shell
Packit 6c4009
@subsection The Missing Pieces
Packit 6c4009
Packit 6c4009
The code extracts for the sample shell included in this chapter are only
Packit 6c4009
a part of the entire shell program.  In particular, nothing at all has
Packit 6c4009
been said about how @code{job} and @code{program} data structures are
Packit 6c4009
allocated and initialized.
Packit 6c4009
Packit 6c4009
Most real shells provide a complex user interface that has support for
Packit 6c4009
a command language; variables; abbreviations, substitutions, and pattern
Packit 6c4009
matching on file names; and the like.  All of this is far too complicated
Packit 6c4009
to explain here!  Instead, we have concentrated on showing how to
Packit 6c4009
implement the core process creation and job control functions that can
Packit 6c4009
be called from such a shell.
Packit 6c4009
Packit 6c4009
Here is a table summarizing the major entry points we have presented:
Packit 6c4009
Packit 6c4009
@table @code
Packit 6c4009
@item void init_shell (void)
Packit 6c4009
Initialize the shell's internal state.  @xref{Initializing the
Packit 6c4009
Shell}.
Packit 6c4009
Packit 6c4009
@item void launch_job (job *@var{j}, int @var{foreground})
Packit 6c4009
Launch the job @var{j} as either a foreground or background job.
Packit 6c4009
@xref{Launching Jobs}.
Packit 6c4009
Packit 6c4009
@item void do_job_notification (void)
Packit 6c4009
Check for and report any jobs that have terminated or stopped.  Can be
Packit 6c4009
called synchronously or within a handler for @code{SIGCHLD} signals.
Packit 6c4009
@xref{Stopped and Terminated Jobs}.
Packit 6c4009
Packit 6c4009
@item void continue_job (job *@var{j}, int @var{foreground})
Packit 6c4009
Continue the job @var{j}.  @xref{Continuing Stopped Jobs}.
Packit 6c4009
@end table
Packit 6c4009
Packit 6c4009
Of course, a real shell would also want to provide other functions for
Packit 6c4009
managing jobs.  For example, it would be useful to have commands to list
Packit 6c4009
all active jobs or to send a signal (such as @code{SIGKILL}) to a job.
Packit 6c4009
Packit 6c4009
Packit 6c4009
@node Functions for Job Control,  , Implementing a Shell, Job Control
Packit 6c4009
@section Functions for Job Control
Packit 6c4009
@cindex process group functions
Packit 6c4009
@cindex job control functions
Packit 6c4009
Packit 6c4009
This section contains detailed descriptions of the functions relating
Packit 6c4009
to job control.
Packit 6c4009
Packit 6c4009
@menu
Packit 6c4009
* Identifying the Terminal::    Determining the controlling terminal's name.
Packit 6c4009
* Process Group Functions::     Functions for manipulating process groups.
Packit 6c4009
* Terminal Access Functions::   Functions for controlling terminal access.
Packit 6c4009
@end menu
Packit 6c4009
Packit 6c4009
Packit 6c4009
@node Identifying the Terminal, Process Group Functions,  , Functions for Job Control
Packit 6c4009
@subsection Identifying the Controlling Terminal
Packit 6c4009
@cindex controlling terminal, determining
Packit 6c4009
Packit 6c4009
You can use the @code{ctermid} function to get a file name that you can
Packit 6c4009
use to open the controlling terminal.  In @theglibc{}, it returns
Packit 6c4009
the same string all the time: @code{"/dev/tty"}.  That is a special
Packit 6c4009
``magic'' file name that refers to the controlling terminal of the
Packit 6c4009
current process (if it has one).  To find the name of the specific
Packit 6c4009
terminal device, use @code{ttyname}; @pxref{Is It a Terminal}.
Packit 6c4009
Packit 6c4009
The function @code{ctermid} is declared in the header file
Packit 6c4009
@file{stdio.h}.
Packit 6c4009
@pindex stdio.h
Packit 6c4009
Packit 6c4009
@deftypefun {char *} ctermid (char *@var{string})
Packit 6c4009
@standards{POSIX.1, stdio.h}
Packit 6c4009
@safety{@prelim{}@mtsafe{@mtsposix{/!string}}@assafe{}@acsafe{}}
Packit 6c4009
@c This function is a stub by default; the actual implementation, for
Packit 6c4009
@c posix systems, returns a pointer to a string literal if passed a NULL
Packit 6c4009
@c string.  It's not clear we want to commit to being MT-Safe in the
Packit 6c4009
@c !string case, so maybe add mtasurace{:ctermid/!string} when we take
Packit 6c4009
@c prelim out, to make room for using a static buffer in the future.
Packit 6c4009
The @code{ctermid} function returns a string containing the file name of
Packit 6c4009
the controlling terminal for the current process.  If @var{string} is
Packit 6c4009
not a null pointer, it should be an array that can hold at least
Packit 6c4009
@code{L_ctermid} characters; the string is returned in this array.
Packit 6c4009
Otherwise, a pointer to a string in a static area is returned, which
Packit 6c4009
might get overwritten on subsequent calls to this function.
Packit 6c4009
Packit 6c4009
An empty string is returned if the file name cannot be determined for
Packit 6c4009
any reason.  Even if a file name is returned, access to the file it
Packit 6c4009
represents is not guaranteed.
Packit 6c4009
@end deftypefun
Packit 6c4009
Packit 6c4009
@deftypevr Macro int L_ctermid
Packit 6c4009
@standards{POSIX.1, stdio.h}
Packit 6c4009
The value of this macro is an integer constant expression that
Packit 6c4009
represents the size of a string large enough to hold the file name
Packit 6c4009
returned by @code{ctermid}.
Packit 6c4009
@end deftypevr
Packit 6c4009
Packit 6c4009
See also the @code{isatty} and @code{ttyname} functions, in
Packit 6c4009
@ref{Is It a Terminal}.
Packit 6c4009
Packit 6c4009
Packit 6c4009
@node Process Group Functions, Terminal Access Functions, Identifying the Terminal, Functions for Job Control
Packit 6c4009
@subsection Process Group Functions
Packit 6c4009
Packit 6c4009
Here are descriptions of the functions for manipulating process groups.
Packit 6c4009
Your program should include the header files @file{sys/types.h} and
Packit 6c4009
@file{unistd.h} to use these functions.
Packit 6c4009
@pindex unistd.h
Packit 6c4009
@pindex sys/types.h
Packit 6c4009
Packit 6c4009
@deftypefun pid_t setsid (void)
Packit 6c4009
@standards{POSIX.1, unistd.h}
Packit 6c4009
@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
Packit 6c4009
@c This is usually a direct syscall, but if a syscall is not available,
Packit 6c4009
@c we use a stub, or Hurd- and BSD-specific implementations.  The former
Packit 6c4009
@c uses a mutex and a hurd critical section, and the latter issues a few
Packit 6c4009
@c syscalls, so both seem safe, the locking on Hurd is safe because of
Packit 6c4009
@c the critical section.
Packit 6c4009
The @code{setsid} function creates a new session.  The calling process
Packit 6c4009
becomes the session leader, and is put in a new process group whose
Packit 6c4009
process group ID is the same as the process ID of that process.  There
Packit 6c4009
are initially no other processes in the new process group, and no other
Packit 6c4009
process groups in the new session.
Packit 6c4009
Packit 6c4009
This function also makes the calling process have no controlling terminal.
Packit 6c4009
Packit 6c4009
The @code{setsid} function returns the new process group ID of the
Packit 6c4009
calling process if successful.  A return value of @code{-1} indicates an
Packit 6c4009
error.  The following @code{errno} error conditions are defined for this
Packit 6c4009
function:
Packit 6c4009
Packit 6c4009
@table @code
Packit 6c4009
@item EPERM
Packit 6c4009
The calling process is already a process group leader, or there is
Packit 6c4009
already another process group around that has the same process group ID.
Packit 6c4009
@end table
Packit 6c4009
@end deftypefun
Packit 6c4009
Packit 6c4009
@deftypefun pid_t getsid (pid_t @var{pid})
Packit 6c4009
@standards{SVID, unistd.h}
Packit 6c4009
@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
Packit 6c4009
@c Stub or direct syscall, except on hurd, where it is equally safe.
Packit 6c4009
Packit 6c4009
The @code{getsid} function returns the process group ID of the session
Packit 6c4009
leader of the specified process.  If a @var{pid} is @code{0}, the
Packit 6c4009
process group ID of the session leader of the current process is
Packit 6c4009
returned.
Packit 6c4009
Packit 6c4009
In case of error @code{-1} is returned and @code{errno} is set.  The
Packit 6c4009
following @code{errno} error conditions are defined for this function:
Packit 6c4009
Packit 6c4009
@table @code
Packit 6c4009
@item ESRCH
Packit 6c4009
There is no process with the given process ID @var{pid}.
Packit 6c4009
@item EPERM
Packit 6c4009
The calling process and the process specified by @var{pid} are in
Packit 6c4009
different sessions, and the implementation doesn't allow to access the
Packit 6c4009
process group ID of the session leader of the process with ID @var{pid}
Packit 6c4009
from the calling process.
Packit 6c4009
@end table
Packit 6c4009
@end deftypefun
Packit 6c4009
Packit 6c4009
@deftypefun pid_t getpgrp (void)
Packit 6c4009
@standards{POSIX.1, unistd.h}
Packit 6c4009
@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
Packit 6c4009
The @code{getpgrp} function returns the process group ID of
Packit 6c4009
the calling process.
Packit 6c4009
@end deftypefun
Packit 6c4009
Packit 6c4009
@deftypefun int getpgid (pid_t @var{pid})
Packit 6c4009
@standards{POSIX.1, unistd.h}
Packit 6c4009
@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
Packit 6c4009
@c Stub or direct syscall, except on hurd, where it is equally safe.
Packit 6c4009
Packit 6c4009
The @code{getpgid} function
Packit 6c4009
returns the process group ID of the process @var{pid}.  You can supply a
Packit 6c4009
value of @code{0} for the @var{pid} argument to get information about
Packit 6c4009
the calling process.
Packit 6c4009
Packit 6c4009
In case of error @code{-1} is returned and @code{errno} is set.  The
Packit 6c4009
following @code{errno} error conditions are defined for this function:
Packit 6c4009
Packit 6c4009
@table @code
Packit 6c4009
@item ESRCH
Packit 6c4009
There is no process with the given process ID @var{pid}.
Packit 6c4009
The calling process and the process specified by @var{pid} are in
Packit 6c4009
different sessions, and the implementation doesn't allow to access the
Packit 6c4009
process group ID of the process with ID @var{pid} from the calling
Packit 6c4009
process.
Packit 6c4009
@end table
Packit 6c4009
@end deftypefun
Packit 6c4009
Packit 6c4009
@deftypefun int setpgid (pid_t @var{pid}, pid_t @var{pgid})
Packit 6c4009
@standards{POSIX.1, unistd.h}
Packit 6c4009
@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
Packit 6c4009
@c Stub or direct syscall, except on hurd, where it is equally safe.
Packit 6c4009
The @code{setpgid} function puts the process @var{pid} into the process
Packit 6c4009
group @var{pgid}.  As a special case, either @var{pid} or @var{pgid} can
Packit 6c4009
be zero to indicate the process ID of the calling process.
Packit 6c4009
Packit 6c4009
This function fails on a system that does not support job control.
Packit 6c4009
@xref{Job Control is Optional}, for more information.
Packit 6c4009
Packit 6c4009
If the operation is successful, @code{setpgid} returns zero.  Otherwise
Packit 6c4009
it returns @code{-1}.  The following @code{errno} error conditions are
Packit 6c4009
defined for this function:
Packit 6c4009
Packit 6c4009
@table @code
Packit 6c4009
@item EACCES
Packit 6c4009
The child process named by @var{pid} has executed an @code{exec}
Packit 6c4009
function since it was forked.
Packit 6c4009
Packit 6c4009
@item EINVAL
Packit 6c4009
The value of the @var{pgid} is not valid.
Packit 6c4009
Packit 6c4009
@item ENOSYS
Packit 6c4009
The system doesn't support job control.
Packit 6c4009
Packit 6c4009
@item EPERM
Packit 6c4009
The process indicated by the @var{pid} argument is a session leader,
Packit 6c4009
or is not in the same session as the calling process, or the value of
Packit 6c4009
the @var{pgid} argument doesn't match a process group ID in the same
Packit 6c4009
session as the calling process.
Packit 6c4009
Packit 6c4009
@item ESRCH
Packit 6c4009
The process indicated by the @var{pid} argument is not the calling
Packit 6c4009
process or a child of the calling process.
Packit 6c4009
@end table
Packit 6c4009
@end deftypefun
Packit 6c4009
Packit 6c4009
@deftypefun int setpgrp (pid_t @var{pid}, pid_t @var{pgid})
Packit 6c4009
@standards{BSD, unistd.h}
Packit 6c4009
@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
Packit 6c4009
@c Direct syscall or setpgid wrapper.
Packit 6c4009
This is the BSD Unix name for @code{setpgid}.  Both functions do exactly
Packit 6c4009
the same thing.
Packit 6c4009
@end deftypefun
Packit 6c4009
Packit 6c4009
Packit 6c4009
@node Terminal Access Functions,  , Process Group Functions, Functions for Job Control
Packit 6c4009
@subsection Functions for Controlling Terminal Access
Packit 6c4009
Packit 6c4009
These are the functions for reading or setting the foreground
Packit 6c4009
process group of a terminal.  You should include the header files
Packit 6c4009
@file{sys/types.h} and @file{unistd.h} in your application to use
Packit 6c4009
these functions.
Packit 6c4009
@pindex unistd.h
Packit 6c4009
@pindex sys/types.h
Packit 6c4009
Packit 6c4009
Although these functions take a file descriptor argument to specify
Packit 6c4009
the terminal device, the foreground job is associated with the terminal
Packit 6c4009
file itself and not a particular open file descriptor.
Packit 6c4009
Packit 6c4009
@deftypefun pid_t tcgetpgrp (int @var{filedes})
Packit 6c4009
@standards{POSIX.1, unistd.h}
Packit 6c4009
@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
Packit 6c4009
@c Stub, or ioctl on BSD and GNU/Linux.
Packit 6c4009
This function returns the process group ID of the foreground process
Packit 6c4009
group associated with the terminal open on descriptor @var{filedes}.
Packit 6c4009
Packit 6c4009
If there is no foreground process group, the return value is a number
Packit 6c4009
greater than @code{1} that does not match the process group ID of any
Packit 6c4009
existing process group.  This can happen if all of the processes in the
Packit 6c4009
job that was formerly the foreground job have terminated, and no other
Packit 6c4009
job has yet been moved into the foreground.
Packit 6c4009
Packit 6c4009
In case of an error, a value of @code{-1} is returned.  The
Packit 6c4009
following @code{errno} error conditions are defined for this function:
Packit 6c4009
Packit 6c4009
@table @code
Packit 6c4009
@item EBADF
Packit 6c4009
The @var{filedes} argument is not a valid file descriptor.
Packit 6c4009
Packit 6c4009
@item ENOSYS
Packit 6c4009
The system doesn't support job control.
Packit 6c4009
Packit 6c4009
@item ENOTTY
Packit 6c4009
The terminal file associated with the @var{filedes} argument isn't the
Packit 6c4009
controlling terminal of the calling process.
Packit 6c4009
@end table
Packit 6c4009
@end deftypefun
Packit 6c4009
Packit 6c4009
@deftypefun int tcsetpgrp (int @var{filedes}, pid_t @var{pgid})
Packit 6c4009
@standards{POSIX.1, unistd.h}
Packit 6c4009
@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
Packit 6c4009
@c Stub, or ioctl on BSD and GNU/Linux.
Packit 6c4009
This function is used to set a terminal's foreground process group ID.
Packit 6c4009
The argument @var{filedes} is a descriptor which specifies the terminal;
Packit 6c4009
@var{pgid} specifies the process group.  The calling process must be a
Packit 6c4009
member of the same session as @var{pgid} and must have the same
Packit 6c4009
controlling terminal.
Packit 6c4009
Packit 6c4009
For terminal access purposes, this function is treated as output.  If it
Packit 6c4009
is called from a background process on its controlling terminal,
Packit 6c4009
normally all processes in the process group are sent a @code{SIGTTOU}
Packit 6c4009
signal.  The exception is if the calling process itself is ignoring or
Packit 6c4009
blocking @code{SIGTTOU} signals, in which case the operation is
Packit 6c4009
performed and no signal is sent.
Packit 6c4009
Packit 6c4009
If successful, @code{tcsetpgrp} returns @code{0}.  A return value of
Packit 6c4009
@code{-1} indicates an error.  The following @code{errno} error
Packit 6c4009
conditions are defined for this function:
Packit 6c4009
Packit 6c4009
@table @code
Packit 6c4009
@item EBADF
Packit 6c4009
The @var{filedes} argument is not a valid file descriptor.
Packit 6c4009
Packit 6c4009
@item EINVAL
Packit 6c4009
The @var{pgid} argument is not valid.
Packit 6c4009
Packit 6c4009
@item ENOSYS
Packit 6c4009
The system doesn't support job control.
Packit 6c4009
Packit 6c4009
@item ENOTTY
Packit 6c4009
The @var{filedes} isn't the controlling terminal of the calling process.
Packit 6c4009
Packit 6c4009
@item EPERM
Packit 6c4009
The @var{pgid} isn't a process group in the same session as the calling
Packit 6c4009
process.
Packit 6c4009
@end table
Packit 6c4009
@end deftypefun
Packit 6c4009
Packit 6c4009
@deftypefun pid_t tcgetsid (int @var{fildes})
Packit 6c4009
@standards{Unix98, termios.h}
Packit 6c4009
@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
Packit 6c4009
@c Ioctl call, if available, or tcgetpgrp followed by getsid.
Packit 6c4009
This function is used to obtain the process group ID of the session
Packit 6c4009
for which the terminal specified by @var{fildes} is the controlling terminal.
Packit 6c4009
If the call is successful the group ID is returned.  Otherwise the
Packit 6c4009
return value is @code{(pid_t) -1} and the global variable @var{errno}
Packit 6c4009
is set to the following value:
Packit 6c4009
@table @code
Packit 6c4009
@item EBADF
Packit 6c4009
The @var{filedes} argument is not a valid file descriptor.
Packit 6c4009
Packit 6c4009
@item ENOTTY
Packit 6c4009
The calling process does not have a controlling terminal, or the file
Packit 6c4009
is not the controlling terminal.
Packit 6c4009
@end table
Packit 6c4009
@end deftypefun