Job control under ksh

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:

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


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