From 28f540f45bbacd939bfd07f213bcad2bf730b1bf Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Sat, 18 Feb 1995 01:27:10 +0000 Subject: initial import --- manual/process.texi | 775 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 775 insertions(+) create mode 100644 manual/process.texi (limited to 'manual/process.texi') diff --git a/manual/process.texi b/manual/process.texi new file mode 100644 index 0000000000..2f5ba65af5 --- /dev/null +++ b/manual/process.texi @@ -0,0 +1,775 @@ +@node Processes +@chapter Processes + +@cindex process +@dfn{Processes} are the primitive units for allocation of system +resources. Each process has its own address space and (usually) one +thread of control. A process executes a program; you can have multiple +processes executing the same program, but each process has its own copy +of the program within its own address space and executes it +independently of the other copies. + +@cindex child process +@cindex parent process +Processes are organized hierarchically. Each process has a @dfn{parent +process} which explicitly arranged to create it. The processes created +by a given parent are called its @dfn{child processes}. A child +inherits many of its attributes from the parent process. + +This chapter describes how a program can create, terminate, and control +child processes. Actually, there are three distinct operations +involved: creating a new child process, causing the new process to +execute a program, and coordinating the completion of the child process +with the original program. + +The @code{system} function provides a simple, portable mechanism for +running another program; it does all three steps automatically. If you +need more control over the details of how this is done, you can use the +primitive functions to do each step individually instead. + +@menu +* Running a Command:: The easy way to run another program. +* Process Creation Concepts:: An overview of the hard way to do it. +* Process Identification:: How to get the process ID of a process. +* Creating a Process:: How to fork a child process. +* Executing a File:: How to make a process execute another program. +* Process Completion:: How to tell when a child process has completed. +* Process Completion Status:: How to interpret the status value + returned from a child process. +* BSD Wait Functions:: More functions, for backward compatibility. +* Process Creation Example:: A complete example program. +@end menu + + +@node Running a Command +@section Running a Command +@cindex running a command + +The easy way to run another program is to use the @code{system} +function. This function does all the work of running a subprogram, but +it doesn't give you much control over the details: you have to wait +until the subprogram terminates before you can do anything else. + +@comment stdlib.h +@comment ANSI +@deftypefun int system (const char *@var{command}) +@pindex sh +This function executes @var{command} as a shell command. In the GNU C +library, it always uses the default shell @code{sh} to run the command. +In particular, it searches the directories in @code{PATH} to find +programs to execute. The return value is @code{-1} if it wasn't +possible to create the shell process, and otherwise is the status of the +shell process. @xref{Process Completion}, for details on how this +status code can be interpreted. + +@pindex stdlib.h +The @code{system} function is declared in the header file +@file{stdlib.h}. +@end deftypefun + +@strong{Portability Note:} Some C implementations may not have any +notion of a command processor that can execute other programs. You can +determine whether a command processor exists by executing +@w{@code{system (NULL)}}; if the return value is nonzero, a command +processor is available. + +The @code{popen} and @code{pclose} functions (@pxref{Pipe to a +Subprocess}) are closely related to the @code{system} function. They +allow the parent process to communicate with the standard input and +output channels of the command being executed. + +@node Process Creation Concepts +@section Process Creation Concepts + +This section gives an overview of processes and of the steps involved in +creating a process and making it run another program. + +@cindex process ID +@cindex process lifetime +Each process is named by a @dfn{process ID} number. A unique process ID +is allocated to each process when it is created. The @dfn{lifetime} of +a process ends when its termination is reported to its parent process; +at that time, all of the process resources, including its process ID, +are freed. + +@cindex creating a process +@cindex forking a process +@cindex child process +@cindex parent process +Processes are created with the @code{fork} system call (so the operation +of creating a new process is sometimes called @dfn{forking} a process). +The @dfn{child process} created by @code{fork} is a copy of the original +@dfn{parent process}, except that it has its own process ID. + +After forking a child process, both the parent and child processes +continue to execute normally. If you want your program to wait for a +child process to finish executing before continuing, you must do this +explicitly after the fork operation, by calling @code{wait} or +@code{waitpid} (@pxref{Process Completion}). These functions give you +limited information about why the child terminated---for example, its +exit status code. + +A newly forked child process continues to execute the same program as +its parent process, at the point where the @code{fork} call returns. +You can use the return value from @code{fork} to tell whether the program +is running in the parent process or the child. + +@cindex process image +Having several processes run the same program is only occasionally +useful. But the child can execute another program using one of the +@code{exec} functions; see @ref{Executing a File}. The program that the +process is executing is called its @dfn{process image}. Starting +execution of a new program causes the process to forget all about its +previous process image; when the new program exits, the process exits +too, instead of returning to the previous process image. + +@node Process Identification +@section Process Identification + +The @code{pid_t} data type represents process IDs. You can get the +process ID of a process by calling @code{getpid}. The function +@code{getppid} returns the process ID of the parent of the current +process (this is also known as the @dfn{parent process ID}). Your +program should include the header files @file{unistd.h} and +@file{sys/types.h} to use these functions. +@pindex sys/types.h +@pindex unistd.h + +@comment sys/types.h +@comment POSIX.1 +@deftp {Data Type} pid_t +The @code{pid_t} data type is a signed integer type which is capable +of representing a process ID. In the GNU library, this is an @code{int}. +@end deftp + +@comment unistd.h +@comment POSIX.1 +@deftypefun pid_t getpid (void) +The @code{getpid} function returns the process ID of the current process. +@end deftypefun + +@comment unistd.h +@comment POSIX.1 +@deftypefun pid_t getppid (void) +The @code{getppid} function returns the process ID of the parent of the +current process. +@end deftypefun + +@node Creating a Process +@section Creating a Process + +The @code{fork} function is the primitive for creating a process. +It is declared in the header file @file{unistd.h}. +@pindex unistd.h + +@comment unistd.h +@comment POSIX.1 +@deftypefun pid_t fork (void) +The @code{fork} function creates a new process. + +If the operation is successful, there are then both parent and child +processes and both see @code{fork} return, but with different values: it +returns a value of @code{0} in the child process and returns the child's +process ID in the parent process. + +If process creation failed, @code{fork} returns a value of @code{-1} in +the parent process. The following @code{errno} error conditions are +defined for @code{fork}: + +@table @code +@item EAGAIN +There aren't enough system resources to create another process, or the +user already has too many processes running. This means exceeding the +@code{RLIMIT_NPROC} resource limit, which can usually be increased; +@pxref{Limits on Resources}. + +@item ENOMEM +The process requires more space than the system can supply. +@end table +@end deftypefun + +The specific attributes of the child process that differ from the +parent process are: + +@itemize @bullet +@item +The child process has its own unique process ID. + +@item +The parent process ID of the child process is the process ID of its +parent process. + +@item +The child process gets its own copies of the parent process's open file +descriptors. Subsequently changing attributes of the file descriptors +in the parent process won't affect the file descriptors in the child, +and vice versa. @xref{Control Operations}. However, the file position +associated with each descriptor is shared by both processes; +@pxref{File Position}. + +@item +The elapsed processor times for the child process are set to zero; +see @ref{Processor Time}. + +@item +The child doesn't inherit file locks set by the parent process. +@c !!! flock locks shared +@xref{Control Operations}. + +@item +The child doesn't inherit alarms set by the parent process. +@xref{Setting an Alarm}. + +@item +The set of pending signals (@pxref{Delivery of Signal}) for the child +process is cleared. (The child process inherits its mask of blocked +signals and signal actions from the parent process.) +@end itemize + + +@comment unistd.h +@comment BSD +@deftypefun pid_t vfork (void) +The @code{vfork} function is similar to @code{fork} but on systems it +is more efficient; however, there are restrictions you must follow to +use it safely. + +While @code{fork} makes a complete copy of the calling process's +address space and allows both the parent and child to execute +independently, @code{vfork} does not make this copy. Instead, the +child process created with @code{vfork} shares its parent's address +space until it calls exits or one of the @code{exec} functions. In the +meantime, the parent process suspends execution. + +You must be very careful not to allow the child process created with +@code{vfork} to modify any global data or even local variables shared +with the parent. Furthermore, the child process cannot return from (or +do a long jump out of) the function that called @code{vfork}! This +would leave the parent process's control information very confused. If +in doubt, use @code{fork} instead. + +Some operating systems don't really implement @code{vfork}. The GNU C +library permits you to use @code{vfork} on all systems, but actually +executes @code{fork} if @code{vfork} isn't available. If you follow +the proper precautions for using @code{vfork}, your program will still +work even if the system uses @code{fork} instead. +@end deftypefun + +@node Executing a File +@section Executing a File +@cindex executing a file +@cindex @code{exec} functions + +This section describes the @code{exec} family of functions, for executing +a file as a process image. You can use these functions to make a child +process execute a new program after it has been forked. + +@pindex unistd.h +The functions in this family differ in how you specify the arguments, +but otherwise they all do the same thing. They are declared in the +header file @file{unistd.h}. + +@comment unistd.h +@comment POSIX.1 +@deftypefun int execv (const char *@var{filename}, char *const @var{argv}@t{[]}) +The @code{execv} function executes the file named by @var{filename} as a +new process image. + +The @var{argv} argument is an array of null-terminated strings that is +used to provide a value for the @code{argv} argument to the @code{main} +function of the program to be executed. The last element of this array +must be a null pointer. By convention, the first element of this array +is the file name of the program sans directory names. @xref{Program +Arguments}, for full details on how programs can access these arguments. + +The environment for the new process image is taken from the +@code{environ} variable of the current process image; see +@ref{Environment Variables}, for information about environments. +@end deftypefun + +@comment unistd.h +@comment POSIX.1 +@deftypefun int execl (const char *@var{filename}, const char *@var{arg0}, @dots{}) +This is similar to @code{execv}, but the @var{argv} strings are +specified individually instead of as an array. A null pointer must be +passed as the last such argument. +@end deftypefun + +@comment unistd.h +@comment POSIX.1 +@deftypefun int execve (const char *@var{filename}, char *const @var{argv}@t{[]}, char *const @var{env}@t{[]}) +This is similar to @code{execv}, but permits you to specify the environment +for the new program explicitly as the @var{env} argument. This should +be an array of strings in the same format as for the @code{environ} +variable; see @ref{Environment Access}. +@end deftypefun + +@comment unistd.h +@comment POSIX.1 +@deftypefun int execle (const char *@var{filename}, const char *@var{arg0}, char *const @var{env}@t{[]}, @dots{}) +This is similar to @code{execl}, but permits you to specify the +environment for the new program explicitly. The environment argument is +passed following the null pointer that marks the last @var{argv} +argument, and should be an array of strings in the same format as for +the @code{environ} variable. +@end deftypefun + +@comment unistd.h +@comment POSIX.1 +@deftypefun int execvp (const char *@var{filename}, char *const @var{argv}@t{[]}) +The @code{execvp} function is similar to @code{execv}, except that it +searches the directories listed in the @code{PATH} environment variable +(@pxref{Standard Environment}) to find the full file name of a +file from @var{filename} if @var{filename} does not contain a slash. + +This function is useful for executing system utility programs, because +it looks for them in the places that the user has chosen. Shells use it +to run the commands that users type. +@end deftypefun + +@comment unistd.h +@comment POSIX.1 +@deftypefun int execlp (const char *@var{filename}, const char *@var{arg0}, @dots{}) +This function is like @code{execl}, except that it performs the same +file name searching as the @code{execvp} function. +@end deftypefun + +The size of the argument list and environment list taken together must +not be greater than @code{ARG_MAX} bytes. @xref{General Limits}. In +the GNU system, the size (which compares against @code{ARG_MAX}) +includes, for each string, the number of characters in the string, plus +the size of a @code{char *}, plus one, rounded up to a multiple of the +size of a @code{char *}. Other systems may have somewhat different +rules for counting. + +These functions normally don't return, since execution of a new program +causes the currently executing program to go away completely. A value +of @code{-1} is returned in the event of a failure. In addition to the +usual file name errors (@pxref{File Name Errors}), the following +@code{errno} error conditions are defined for these functions: + +@table @code +@item E2BIG +The combined size of the new program's argument list and environment +list is larger than @code{ARG_MAX} bytes. The GNU system has no +specific limit on the argument list size, so this error code cannot +result, but you may get @code{ENOMEM} instead if the arguments are too +big for available memory. + +@item ENOEXEC +The specified file can't be executed because it isn't in the right format. + +@item ENOMEM +Executing the specified file requires more storage than is available. +@end table + +If execution of the new file succeeds, it updates the access time field +of the file as if the file had been read. @xref{File Times}, for more +details about access times of files. + +The point at which the file is closed again is not specified, but +is at some point before the process exits or before another process +image is executed. + +Executing a new process image completely changes the contents of memory, +copying only the argument and environment strings to new locations. But +many other attributes of the process are unchanged: + +@itemize @bullet +@item +The process ID and the parent process ID. @xref{Process Creation Concepts}. + +@item +Session and process group membership. @xref{Concepts of Job Control}. + +@item +Real user ID and group ID, and supplementary group IDs. @xref{Process +Persona}. + +@item +Pending alarms. @xref{Setting an Alarm}. + +@item +Current working directory and root directory. @xref{Working +Directory}. In the GNU system, the root directory is not copied when +executing a setuid program; instead the system default root directory +is used for the new program. + +@item +File mode creation mask. @xref{Setting Permissions}. + +@item +Process signal mask; see @ref{Process Signal Mask}. + +@item +Pending signals; see @ref{Blocking Signals}. + +@item +Elapsed processor time associated with the process; see @ref{Processor Time}. +@end itemize + +If the set-user-ID and set-group-ID mode bits of the process image file +are set, this affects the effective user ID and effective group ID +(respectively) of the process. These concepts are discussed in detail +in @ref{Process Persona}. + +Signals that are set to be ignored in the existing process image are +also set to be ignored in the new process image. All other signals are +set to the default action in the new process image. For more +information about signals, see @ref{Signal Handling}. + +File descriptors open in the existing process image remain open in the +new process image, unless they have the @code{FD_CLOEXEC} +(close-on-exec) flag set. The files that remain open inherit all +attributes of the open file description from the existing process image, +including file locks. File descriptors are discussed in @ref{Low-Level I/O}. + +Streams, by contrast, cannot survive through @code{exec} functions, +because they are located in the memory of the process itself. The new +process image has no streams except those it creates afresh. Each of +the streams in the pre-@code{exec} process image has a descriptor inside +it, and these descriptors do survive through @code{exec} (provided that +they do not have @code{FD_CLOEXEC} set). The new process image can +reconnect these to new streams using @code{fdopen} (@pxref{Descriptors +and Streams}). + +@node Process Completion +@section Process Completion +@cindex process completion +@cindex waiting for completion of child process +@cindex testing exit status of child process + +The functions described in this section are used to wait for a child +process to terminate or stop, and determine its status. These functions +are declared in the header file @file{sys/wait.h}. +@pindex sys/wait.h + +@comment sys/wait.h +@comment POSIX.1 +@deftypefun pid_t waitpid (pid_t @var{pid}, int *@var{status-ptr}, int @var{options}) +The @code{waitpid} function is used to request status information from a +child process whose process ID is @var{pid}. Normally, the calling +process is suspended until the child process makes status information +available by terminating. + +Other values for the @var{pid} argument have special interpretations. A +value of @code{-1} or @code{WAIT_ANY} requests status information for +any child process; a value of @code{0} or @code{WAIT_MYPGRP} requests +information for any child process in the same process group as the +calling process; and any other negative value @minus{} @var{pgid} +requests information for any child process whose process group ID is +@var{pgid}. + +If status information for a child process is available immediately, this +function returns immediately without waiting. If more than one eligible +child process has status information available, one of them is chosen +randomly, and its status is returned immediately. To get the status +from the other eligible child processes, you need to call @code{waitpid} +again. + +The @var{options} argument is a bit mask. Its value should be the +bitwise OR (that is, the @samp{|} operator) of zero or more of the +@code{WNOHANG} and @code{WUNTRACED} flags. You can use the +@code{WNOHANG} flag to indicate that the parent process shouldn't wait; +and the @code{WUNTRACED} flag to request status information from stopped +processes as well as processes that have terminated. + +The status information from the child process is stored in the object +that @var{status-ptr} points to, unless @var{status-ptr} is a null pointer. + +The return value is normally the process ID of the child process whose +status is reported. If the @code{WNOHANG} option was specified and no +child process is waiting to be noticed, the value is zero. A value of +@code{-1} is returned in case of error. The following @code{errno} +error conditions are defined for this function: + +@table @code +@item EINTR +The function was interrupted by delivery of a signal to the calling +process. @xref{Interrupted Primitives}. + +@item ECHILD +There are no child processes to wait for, or the specified @var{pid} +is not a child of the calling process. + +@item EINVAL +An invalid value was provided for the @var{options} argument. +@end table +@end deftypefun + +These symbolic constants are defined as values for the @var{pid} argument +to the @code{waitpid} function. + +@comment Extra blank lines make it look better. +@table @code +@item WAIT_ANY + +This constant macro (whose value is @code{-1}) specifies that +@code{waitpid} should return status information about any child process. + + +@item WAIT_MYPGRP +This constant (with value @code{0}) specifies that @code{waitpid} should +return status information about any child process in the same process +group as the calling process. +@end table + +These symbolic constants are defined as flags for the @var{options} +argument to the @code{waitpid} function. You can bitwise-OR the flags +together to obtain a value to use as the argument. + +@table @code +@item WNOHANG + +This flag specifies that @code{waitpid} should return immediately +instead of waiting, if there is no child process ready to be noticed. + +@item WUNTRACED + +This flag specifies that @code{waitpid} should report the status of any +child processes that have been stopped as well as those that have +terminated. +@end table + +@comment sys/wait.h +@comment POSIX.1 +@deftypefun pid_t wait (int *@var{status-ptr}) +This is a simplified version of @code{waitpid}, and is used to wait +until any one child process terminates. The call: + +@smallexample +wait (&status) +@end smallexample + +@noindent +is exactly equivalent to: + +@smallexample +waitpid (-1, &status, 0) +@end smallexample +@end deftypefun + +@comment sys/wait.h +@comment BSD +@deftypefun pid_t wait4 (pid_t @var{pid}, int *@var{status-ptr}, int @var{options}, struct rusage *@var{usage}) +If @var{usage} is a null pointer, @code{wait4} is equivalent to +@code{waitpid (@var{pid}, @var{status-ptr}, @var{options})}. + +If @var{usage} is not null, @code{wait4} stores usage figures for the +child process in @code{*@var{rusage}} (but only if the child has +terminated, not if it has stopped). @xref{Resource Usage}. + +This function is a BSD extension. +@end deftypefun + +Here's an example of how to use @code{waitpid} to get the status from +all child processes that have terminated, without ever waiting. This +function is designed to be a handler for @code{SIGCHLD}, the signal that +indicates that at least one child process has terminated. + +@smallexample +@group +void +sigchld_handler (int signum) +@{ + int pid; + int status; + while (1) + @{ + pid = waitpid (WAIT_ANY, &status, WNOHANG); + if (pid < 0) + @{ + perror ("waitpid"); + break; + @} + if (pid == 0) + break; + notice_termination (pid, status); + @} +@} +@end group +@end smallexample + +@node Process Completion Status +@section Process Completion Status + +If the exit status value (@pxref{Program Termination}) of the child +process is zero, then the status value reported by @code{waitpid} or +@code{wait} is also zero. You can test for other kinds of information +encoded in the returned status value using the following macros. +These macros are defined in the header file @file{sys/wait.h}. +@pindex sys/wait.h + +@comment sys/wait.h +@comment POSIX.1 +@deftypefn Macro int WIFEXITED (int @var{status}) +This macro returns a nonzero value if the child process terminated +normally with @code{exit} or @code{_exit}. +@end deftypefn + +@comment sys/wait.h +@comment POSIX.1 +@deftypefn Macro int WEXITSTATUS (int @var{status}) +If @code{WIFEXITED} is true of @var{status}, this macro returns the +low-order 8 bits of the exit status value from the child process. +@xref{Exit Status}. +@end deftypefn + +@comment sys/wait.h +@comment POSIX.1 +@deftypefn Macro int WIFSIGNALED (int @var{status}) +This macro returns a nonzero value if the child process terminated +because it received a signal that was not handled. +@xref{Signal Handling}. +@end deftypefn + +@comment sys/wait.h +@comment POSIX.1 +@deftypefn Macro int WTERMSIG (int @var{status}) +If @code{WIFSIGNALED} is true of @var{status}, this macro returns the +signal number of the signal that terminated the child process. +@end deftypefn + +@comment sys/wait.h +@comment BSD +@deftypefn Macro int WCOREDUMP (int @var{status}) +This macro returns a nonzero value if the child process terminated +and produced a core dump. +@end deftypefn + +@comment sys/wait.h +@comment POSIX.1 +@deftypefn Macro int WIFSTOPPED (int @var{status}) +This macro returns a nonzero value if the child process is stopped. +@end deftypefn + +@comment sys/wait.h +@comment POSIX.1 +@deftypefn Macro int WSTOPSIG (int @var{status}) +If @code{WIFSTOPPED} is true of @var{status}, this macro returns the +signal number of the signal that caused the child process to stop. +@end deftypefn + + +@node BSD Wait Functions +@section BSD Process Wait Functions + +The GNU library also provides these related facilities for compatibility +with BSD Unix. BSD uses the @code{union wait} data type to represent +status values rather than an @code{int}. The two representations are +actually interchangeable; they describe the same bit patterns. The GNU +C Library defines macros such as @code{WEXITSTATUS} so that they will +work on either kind of object, and the @code{wait} function is defined +to accept either type of pointer as its @var{status-ptr} argument. + +These functions are declared in @file{sys/wait.h}. +@pindex sys/wait.h + +@comment sys/wait.h +@comment BSD +@deftp {Data Type} {union wait} +This data type represents program termination status values. It has +the following members: + +@table @code +@item int w_termsig +The value of this member is the same as the result of the +@code{WTERMSIG} macro. + +@item int w_coredump +The value of this member is the same as the result of the +@code{WCOREDUMP} macro. + +@item int w_retcode +The value of this member is the same as the result of the +@code{WEXITSTATUS} macro. + +@item int w_stopsig +The value of this member is the same as the result of the +@code{WSTOPSIG} macro. +@end table + +Instead of accessing these members directly, you should use the +equivalent macros. +@end deftp + +The @code{wait3} function is the predecessor to @code{wait4}, which is +more flexible. @code{wait3} is now obsolete. + +@comment sys/wait.h +@comment BSD +@deftypefun pid_t wait3 (union wait *@var{status-ptr}, int @var{options}, struct rusage *@var{usage}) +If @var{usage} is a null pointer, @code{wait3} is equivalent to +@code{waitpid (-1, @var{status-ptr}, @var{options})}. + +If @var{usage} is not null, @code{wait3} stores usage figures for the +child process in @code{*@var{rusage}} (but only if the child has +terminated, not if it has stopped). @xref{Resource Usage}. +@end deftypefun + +@node Process Creation Example +@section Process Creation Example + +Here is an example program showing how you might write a function +similar to the built-in @code{system}. It executes its @var{command} +argument using the equivalent of @samp{sh -c @var{command}}. + +@smallexample +#include +#include +#include +#include +#include + +/* @r{Execute the command using this shell program.} */ +#define SHELL "/bin/sh" + +@group +int +my_system (const char *command) +@{ + int status; + pid_t pid; +@end group + + pid = fork (); + if (pid == 0) + @{ + /* @r{This is the child process. Execute the shell command.} */ + execl (SHELL, SHELL, "-c", command, NULL); + _exit (EXIT_FAILURE); + @} + else if (pid < 0) + /* @r{The fork failed. Report failure.} */ + status = -1; + else + /* @r{This is the parent process. Wait for the child to complete.} */ + if (waitpid (pid, &status, 0) != pid) + status = -1; + return status; +@} +@end smallexample + +@comment Yes, this example has been tested. + +There are a couple of things you should pay attention to in this +example. + +Remember that the first @code{argv} argument supplied to the program +represents the name of the program being executed. That is why, in the +call to @code{execl}, @code{SHELL} is supplied once to name the program +to execute and a second time to supply a value for @code{argv[0]}. + +The @code{execl} call in the child process doesn't return if it is +successful. If it fails, you must do something to make the child +process terminate. Just returning a bad status code with @code{return} +would leave two processes running the original program. Instead, the +right behavior is for the child process to report failure to its parent +process. + +Call @code{_exit} to accomplish this. The reason for using @code{_exit} +instead of @code{exit} is to avoid flushing fully buffered streams such +as @code{stdout}. The buffers of these streams probably contain data +that was copied from the parent process by the @code{fork}, data that +will be output eventually by the parent process. Calling @code{exit} in +the child would output the data twice. @xref{Termination Internals}. -- cgit v1.2.1