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