Job control under ksh
The UNIX operating system Korn shell utility,
ksh(C)
supports job control for applications that execute under this
shell.\*(F
Job control works for almost all programs;
however, screen-oriented
programs using
curses(S)
must make special provisions to utilize
job control.
The example code on the following pages
illustrates how to write a curses application that uses
job controls to switch contexts between curses mode
and a shell line mode.
For more background about using Korn Shell job control, see
David Korn's Korn Shell book.
To understand this sample code,
you should also understand the signal mechanism.
To compile this program, use the following command:
cc file.c -lcurses
This example assumes that you have the source code
for the program to be run under job control
and can rewrite it as shown here.
If that is not the case,
you can implement job control by writing a program
similar to the one shown here and inserting your program sections
as shown in the following table:
Program section
|
Insert code after line #
|
Initialization information
|
22
|
Main body of program
|
23 (replacing the while loop)
|
Code to set terminal to program's screen mode
|
37
|
Code to set terminal to the new context
|
41
|
1 #include <signal.h>
2 #include <curses.h>
3 extern int SigHandler( int );
4 struct sigaction signal_action, old_action;
5 main()
6 {
7 sigset_t set, old_set;
8 signal_action.sa_handler = SigHandler;
9 signal_action.sa_flags = 0;
10 sigemptyset( &set);
11 signal_action.sa_mask = set;
lines 1 and 2-
Header files are included for the program. signal.h
provides the signal values, such as SIGTSTP shown in
line 12.
curses.h provides support for the curses
routines and macros used in this program.
line 4-
Declare an instance of the sigaction structure. The
structure is as follows:
struct sigaction {
void (*sa_handler)(); /* signal handler */
sigset_t sa_mask; /* signal mask */
int sa_flags; /* signal flags */
};
line 7-
Declare the set and old_set variables to
sigset_t, which is defined as a long in the
/usr/include/signal.h header file. set is a
set of signals.
line 8-
Assign the name of the signal handler subroutine to the
sa_handler
member of the sigaction
structure.
line 9-
Turn off the flags field. This field is only used for the
SIGCHLD signal, which is not used in this example.
line 10-
Create an empty set of signals as the first step in blocking those
signals associated with a terminal device. It is necessary to
block these signals so that the signal handler routine can be
established and so that curses can be initialized
before signals are received.
line 11-
Set the signal_action.sa_mask to be empty.
12 sigaddset( &set, SIGTSTP );
13 sigaddset( &set, SIGTTIN );
14 sigaddset( &set, SIGCONT );
15 sigaddset( &set, SIGTTOU );
16 sigprocmask( SIG_SETMASK, &set, &old_set );
17 sigaction( SIGCONT, &signal_action, NULL );
18 signal_action.sa_mask = set;
lines 12 to 16-
Add the signals to be
blocked. When signals are blocked they are postponed, but not
ignored. The call to sigprocmask in line 16 actually
causes the signals in the set to be blocked. The signals are
blocked so that program initialization can occur prior to
actually receiving a
signal. sigprocmask also saves the old mask value into
old_set.
The sigaddset calls assign values to
the set mask. The signals are:
-
SIGTSTP occurs when an application sends <Ctrl>
z to push the current foreground task into the
background. The terminal is requesting that the current program stop.
-
SIGTTIN occurs when a
background process attempts to read from the terminal.
-
SIGCONT continues a stopped process.
-
SIGTTOU
occurs when a background process attempts to write to the terminal,
if the TOSTOP mode is set in the
c_lflags
member of the termios structure. SIGTTOU can
also occur if a background process attempts to change the terminal
termios members with an ioctl.
line 17-
Now that the signals from the terminal device are blocked, the signal
handler routine can be initialized.
The sigaction call sets a function pointer in the
kernel for the signal handler routine (SigHandler) and
permits the signal handler routine to be executed only when the
specified signal, in this case, SIGCONT is received.
The main difference between signal and
sigaction is that sigaction utilizes the
information in the sigaction structure. When the
signal handler is called, none of the signals specified in the
mask are received.
line 18-
Set the sigaction structure mask to the values held in
the signal set. This is a necessary initialization
step prior to calling sigaction to establish the
conditions under which the signal handler is called for the next
set of signals.
19 sigaction( SIGTTIN, &signal_action, &old_action );
20 sigaction( SIGTTOU, &signal_action, NULL );
21 sigaction( SIGTSTP, &signal_action, NULL );
22 initscr();
23 sigprocmask( SIG_SETMASK, &old_set, NULL );
24 while (pause())
25 {
26 printw( "Got a Signal\n" );
27 refresh();
28 }
29 }
lines 19 to 21-
Execute sigaction for any possible signals from the terminal
device. This ensures that the signal handler executes for each
signal value.
line 22-
Now that the signal handler is established, the curses
application can be started.
Initialize the curses data structures and get terminal
information for use by the curses routines that follow.
line 23-
With the curses application started, use
sigprocmask to let the signals be received.
lines 24 to 28-
Wait until a signal is received in line 24 using
pause. When a signal arrives, display
the
Got a Signal
message and rewrite the
curses window.
30 SigHandler( sig )
31 int sig;
32 {
33 if (sig == SIGCONT)
34 {
35 sigaction( SIGTTIN, &signal_action, NULL );
36 sigaction( SIGTTOU, &signal_action, NULL );
37 sigaction( SIGTSTP, &signal_action, NULL );
38 reset_prog_mode();
39 printw("Returned to curses line modes.\n");
40 } else {
41 sigaction( sig, &old_action, NULL );
42 printw( "Returning to shell line modes. \n");
43 refresh();
44 reset_shell_mode();
45 kill( getpid(), sig );
46 }
47 }
line 30-
The SigHandler signal handler routine operates in two conditions.
Either the SIGCONT signal occurs and process continues
in curses mode, or SIGTSTP, SIGTTIN, or
SIGTTOU occurs and the program performs a context shift
to a different mode, in this case, the shell line mode.
lines 33 to 39-
The SIGCONT signal occurred and the curses
process continues. The signal handler is now instructed to be
executed when the three other signals occur. The terminal device
is returned to curses mode and a message displayed in
the window.
lines 41 to 45-
One of the SIGTSTP, SIGTTIN, or
SIGTTOU signals occurred. In line 41,
sigaction sets the conditions for the signal handler
back to the previously stored value.
A message displays in the window
indicating that the mode is now changing. The mode is then
changed in line 44, and finally the signal is sent to the
process.
The following manual pages provide additional information about
the routines and structures described in the example:
Routine
|
Manual Page
|
Description
|
getpid
|
getpid(S)
|
get process group ID
|
initscr
|
curses(S)
|
initialize curses terminal and structures
|
kill
|
kill(S)
|
sends a signal to the process group
|
pause
|
pause(S)
|
suspend process until signal is received
|
printw
|
curses(S)
|
display message in curses window
|
refresh
|
curses(S)
|
restores screen display
|
reset_prog_mode
|
curses(S)
|
restore to curses mode
|
reset_shell_mode
|
curses(S)
|
restore to non-curses mode
|
sigaction
|
sigaction(S)
|
examine and change signal action
|
sigaddset
|
sigset(S)
|
add signal to signal mask
|
sigemptyset
|
sigset(S)
|
get empty signal set and clear mask
|
signal
|
signal(S)
|
traditional signal handler
|
sigprocmask
|
sigprocmask(S)
|
block signals from mask
|
termio
|
termio(M)
|
terminal interface
|
termios
|
termios(M)
|
POSIX terminal interface
|
Footnotes
-
Note that job control is not supported
under the Korn shell on XENIX releases.
© 2003 Caldera International, Inc. All rights reserved.
SCO OpenServer Release 5.0.7 -- 11 February 2003