ONC+ Developer's Guide

Appendix F Writing a Port Monitor With the Service Access Facility (SAF)

This appendix gives a brief description of the functions a port monitor must perform to run under the service access facility (SAF) and service access controller (SAC).

The appendix covers the following topics:

What Is the SAF?

The service access facility (SAF) generalizes the procedures for service access so that login access on the local system and network access to local services are managed in similar ways. Under the SAF, systems can access services using a variety of port monitors, including ttymon, the listener, and port monitors written expressly for a user's application.

The manner in which a port monitor observes and manages access ports is specific to the port monitor and not to any component of the SAF. Users can therefore extend their systems by developing and installing their own port monitors. This ability to extend the SAF is one of its important features.

Relative to the SAF, a service is a process that is started. No restrictions are on the functions a service can provide.

The SAF consists of a controlling process, the service access controller (SAC), and two administrative levels corresponding to two levels in the supporting directory structure. The top administrative level is concerned with port monitor administration, the lower level with service administration.

From an administrative point of view, the SAF consists of the following components:

What Is the SAC?

The service access controller (SAC) is the SAF's controlling process. The SAC is started by init() by means of an entry in /etc/inittab. Its function is to maintain the port monitors on the system in the state specified by the system administrator.

Use the administrative command sacadm to tell the SAC to change the state of a port monitor. sacadm can also be used to add or remove a port monitor from SAC supervision and to list information about port monitors known to the SAC.

The SAC's administrative file contains a unique tag for each port monitor known to the SAC and the path name of the command used to start each port monitor.

The SAC performs three main functions:

Basic Port Monitor Functions

A port monitor is a process that is responsible for monitoring a set of homogeneous, incoming ports on a machine. A port monitor's major purpose is to detect incoming service requests and to dispatch them appropriately.

A port is an externally seen access point on a system. A port can be an address on a network (TSAP or PSAP), a hardwired terminal line, an incoming phone line, and so on. The definition of what constitutes a port is strictly a function of the port monitor itself.

A port monitor performs certain basic functions. Some of these functions are required to conform to the SAF. Other functions can be specified by the requirements and design of the port monitor itself.

Port monitors have two main functions:

Port Management

The first function of a port monitor is to manage a port. The actual details of how a port is managed are defined by the person who defines the port monitor. A port monitor can handle multiple ports simultaneously.

Some examples of port management are setting the line speed on incoming phone connections, binding an appropriate network address, reinitializing the port when the service terminates, outputting a prompt, and so on.

Activity Monitoring

The second function of a port monitor is to monitor the port or ports for which it is responsible for indications of activity. Two types of activity can be detected.

  1. The first activity is an indication to the port monitor to take some port monitor-specific action. Pressing the Break key to indicate that the line speed should be cycled is an example of a port monitor activity. Not all port monitors need to recognize and respond to the same indications. The indication used to attract the attention of the port monitor is defined by the person who defines the port monitor.

  2. The second activity is an incoming service request. When a service request is received, a port monitor must be able to determine which service is being requested from the port on which the request is received. Note that the same service can be available on more than one port.

Other Port Monitor Functions

This section briefly describes other port monitor functions.

Restricting Access to the System

A port monitor must be able to restrict access to the system without disturbing services that are still running. In order to do so, a port monitor must maintain two internal states: enabled and disabled. The port monitor starts in the state indicated by the ISTATE environment variable provided by the SAC. See SAC/Port Monitor Interface.

Enabling or disabling a port monitor affects all ports for which the port monitor is responsible. If a port monitor is responsible for a single port, only that port is affected. If a port monitor is responsible for multiple ports, the entire collection of ports is affected.

Enabling or disabling a port monitor is a dynamic operation. It causes the port monitor to change its internal state. The effect does not persist across new invocations of the port monitor.

Enabling or disabling an individual port is a static operation. It causes a change to an administrative file. The effect of this change persists across new invocations of the port monitor.

Creating utmpx Entries

Port monitors are responsible for creating utmpx entries with the type field set to USER_PROCESS for services they start, if this action has been specified, that is, if -fu was specified in the pmadm line that added the service. These utmpx entries can in turn be modified by the service. When the service terminates, the utmpx entry must be set to DEAD_PROCESS.

Port Monitor Process IDs and Lock Files

When a port monitor starts, it writes its process ID into a file named _pid in the current directory and places an advisory lock on the file.

Changing the Service Environment: Running doconfig()

Before invoking the service designated in the port monitor administrative file, _pmtab, a port monitor must arrange for the per-service configuration script to be run, if one exists, by calling the library function doconfig(). Because the per-service configuration script can specify the execution of restricted commands, as well as for other security reasons, port monitors are invoked with root permissions. The details of how services are invoked are specified by the person who defines the port monitor.

Terminating a Port Monitor

A port monitor must terminate itself gracefully on receipt of the signal SIGTERM. The termination sequence is as follows:

  1. The port monitor enters the stopping state. No further service requests are accepted.

  2. Any attempt to re-enable the port monitor is ignored.

  3. The port monitor yields control of all ports for which it is responsible. A new instantiation of the port monitor must be able to start correctly while a previous instantiation is stopping.

  4. The advisory lock on the process ID file is released. After this lock is released, the contents of the process ID file are undefined and a new invocation of the port monitor can be started.

SAF Files

This section briefly covers the files used by the SAF.

Port Monitor Administrative File

A port monitor's current directory contains an administrative file named _pmtab. _pmtab is maintained by the pmadm command in conjunction with a port monitor-specific administrative command.

The port monitor administrative command for a listen port monitor is nlsadmin(); the port monitor administrative command for ttymon is ttyadm(). Any port monitor written by a user must be provided with an administrative command specific to that port monitor to perform similar functions.

Per-Service Configuration Files

A port monitor's current directory also contains the per-service configuration scripts, if they exist. The names of the per-service configuration scripts correspond to the service tags in the _pmtab file.

Private Port Monitor Files

A port monitor can create private files in the directory /var/saf/tag, where tag is the name of the port monitor. Examples of private files are log files or temporary files.

SAC/Port Monitor Interface

The SAC creates two environment variables for each port monitor it starts:

The SAC communicates with port monitors through FIFOs. A port monitor should open _pmpipe, in the current directory, to receive messages from the SAC and ../_sacpipe to send return messages to the SAC.

Message Formats

This section describes the messages that can be sent from the SAC to a port monitor (SAC messages), and from a port monitor to the SAC (port monitor messages). These messages are sent through FIFOs and are in the form of C structures. See Example F–2.

SAC Messages

The format of messages from the SAC is defined by the structure sacmsg:

struct sacmsg {
 	int sc_size; /* size of optional data portion */
 	char sc_type; /* type of message */
};

The SAC can send four types of messages to port monitors. The type of message is indicated by setting the sc_type field of the sacmsg structure to one of the following:

A port monitor must respond to every message sent by the SAC.

Port Monitor Messages

The format of messages from a port monitor to the SAC is defined by the structure pmmsg.

struct pmmsg {
 	char pm_type;                /* type of message */
 	unchar pm_state;             /* current state of port monitor */
 	char pm_maxclass;            /* maximum message class this port
                                   monitor understands */
 	char pm_tag[PMTAGSIZE + 1];  /* port monitor's tag */
 	int pm_size;                 /* size of optional data portion */
};

Port monitors can send two types of messages to the SAC. The type of message is indicated by setting the pm_type field of the pmmsg structure to one of the following values:

For both types of messages, set the pm_tag field to the port monitor's tag and the pm_state field to the port monitor's current state. Valid states are:

The current state reflects any changes caused by the last message from the SAC.

The status message is the normal return message. The negative acknowledgement should be sent only when the message received is not understood.

pm_size indicates the size of the optional-data part of the message. pm_maxclass is used to specify a message class. Both fields are discussed in Message Classes. In the Solaris environment, always set pm_maxclass to 1 and sc_size to 0.

Port monitors can never initiate messages; they can only respond to messages that they receive.

Message Classes

The concept of message class has been included to accommodate possible SAF extensions. The preceding messages are all class 1 messages. None of these messages contains a variable data portion. All pertinent information is contained in the message header.

If new messages are added to the protocol, they are defined as new message classes, for example, class 2. The first message that the SAC sends to a port monitor is always a class 1 message. Because all port monitors by definition understand class 1 messages, the first message that the SAC sends is guaranteed to be understood. In its response to the SAC, the port monitor sets the pm_maxclass field to the maximum message class number for that port monitor. The SAC does not send messages to a port monitor from a class with a larger number than the value of pm_maxclass. Requests fail if they require messages of a higher class than the port monitor can understand. For the Solaris environment, always set pm_maxclass to 1.

For any given port monitor, messages of class pm_maxclass and messages of all classes with values lower than pm_maxclass are valid. Thus, if the pm_maxclass field is set to 3, the port monitor understands messages of classes 1, 2, and 3. Port monitors cannot generate messages; they can only respond to messages. A port monitor's response must be of the same class as the originating message.

Because only the SAC can generate messages, this protocol function— even if the port monitor is capable of dealing with messages of a higher class than the SAC can generate.

pm_size is an element of the pmmsg structure. sc_size is an element of the sacmsg structure. These elements indicate the size of the optional-data part of the message. The format of this part of the message is undefined. Its definition is inherent in the type of message. For the Solaris environment, always set both sc_size and pm_size to 0.

Port Monitor Administrative Interface

This section discusses the administrative files available under the SAC.

SAC Administrative File _sactab

The service access controller's administrative file contains information about all the port monitors for which the SAC is responsible. This file exists on the delivered system. Initially, the file is empty except for a single comment line that contains the version number of the SAC. You add port monitors to the system by making entries in the SAC's administrative file. These entries should be made using the administrative command sacadm with a -a option. sacadm is also used to remove entries from the SAC's administrative file.

Each entry in the SAC's administrative file contains the information shown in the following table.

Table F–1 Service Access Controller _sactab File

Fields 

Description 

PMTAG

A unique tag that identifies a particular port monitor. The system administrator is responsible for naming a port monitor. This tag is then used by the SAC to identify the port monitor for all administrative purposes. PMTAG can consist of up to 14 alphanumeric characters.

PMTYPE

The type of the port monitor. In addition to its unique tag, each port monitor has a type designator. The type designator identifies a group of port monitors that are different invocations of the same entity. ttymon and listen are examples of valid port monitor types. The type designator is used to facilitate the administration of groups of related port monitors. Without a type designator, the system administrator has no way of knowing which port monitor tags correspond to port monitors of the same type. PMTYPE can consist of up to 14 alphanumeric characters.

FLGS

The flags that are currently defined are: 

-d When it is started, do not enable the port monitor.

-x Do not start the port monitor.

If no flag is specified, the default action is taken. By default, a port monitor is started and enabled. 

RCNT

The number of times a port monitor can fail before being placed in a failed state. After a port monitor enters the failed state, the SAC does not try to restart it. If a count is not specified when the entry is created, this field is set to 0. A restart count of 0 indicates that the port monitor is not to be restarted when it fails.

COMMAND

A string representing the command that starts the port monitor. The first component of the string, the command itself, must be a full path name. 

Port Monitor Administrative File _pmtab

Each port monitor has two directories for its exclusive use. The current directory contains files defined by the SAF (_pmtab, _pid) and the per-service configuration scripts, if they exist. The directory /var/saf/pmtag, where pmtag is the tag of the port monitor, is available for the port monitor's private files.

Each port monitor has its own administrative file. Use the pmadm command to add, remove, or modify service entries in this file. Each time a change is made using pmadm, the corresponding port monitor rereads its administrative file. Each entry in a port monitor's administrative file defines how the port monitor treats a specific port and what service is to be invoked on that port.

Some fields must be present for all types of port monitors. Each entry must include a service tag to identify the service uniquely and an identity to be assigned to the service when it is started, for example, root.

The combination of a service tag and a port monitor tag uniquely define an instance of a service. You can use the same service tag to identify a service under a different port monitor. The record must also contain port monitor-specific data (for example, for a ttymon port monitor, this data includes the prompt string which is meaningful to ttymon). Each type of port monitor must provide a command that takes the necessary port monitor-specific data as arguments and outputs this data in a form suitable for storage in the file. The ttyadm command provides the formatting for ttymon, nlsadmin for listen. For a user-defined port monitor, you also must supply a similar administrative command.

Each service entry in the port monitor administrative file must have the following format and contain the following information:

svctag:flgs:id:reserved:reserved:reserved:pmspecific# comment 

SVCTAG is a unique tag that identifies a service. This tag is unique only for the port monitor through which the service is available. Other port monitors can offer the same or other services with the same tag. A service requires both a port monitor tag and a service tag to identify it uniquely.

SVCTAG may consist of up to 14 alphanumeric characters. The service entries are defined in the following table.

Table F–2 SVCTAG Service Entries

Service Entries 

Description 

FLGS

Flags with the following meanings might currently be included in this field:  

-x Do not enable this port. By default, the port is enabled.

-u Create a utmpx entry for this service. By default, no utmpx entry is created for the service.

ID

The identity under which the service is to be started. The identity has the form of a login name as it appears in /etc/passwd.

PMSPECIFIC

Examples of port monitor information are addresses, the name of a process to execute, or the name of a STREAMS pipe through which to pass a connection. This information varies to meet the needs of each different type of port monitor.

COMMENT

A comment associated with the service entry.


Note –

Port monitors might ignore the -u flag if creating a utmpx entry for the service is not appropriate to the manner in which the service is to be invoked. Some services might not start properly unless utmpx entries have been created for them, for example, login.


Each port monitor administrative file must contain one special comment of the form:

# VERSION=value 

In this case, value is an integer that represents the port monitor's version number. The version number defines the format of the port monitor administrative file. This comment line is created automatically when a port monitor is added to the system. It appears on a line by itself, before the service entries.

SAC Administrative Command sacadm

sacadm is the administrative command for the upper level of the SAF hierarchy, that is, for port monitor administration. See the sacadm(1M) man page. Under the SAF, port monitors are administered by using the sacadm command to make changes in the SAC's administrative file. sacadm performs the following functions:

Port Monitor Administrative Command pmadm

pmadm is the administrative command for the lower level of the SAF hierarchy, that is, for service administration. See the pmadm(1M) man page. A port can have only one service associated with it, although the same service might be available through more than one port. pmadm performs the following functions:

In order to identify an instance of a service uniquely, the pmadm command must identify both the service (-s) and the port monitor or port monitors through which the service is available (-p or -t).

Monitor-Specific Administrative Command

In the previous section, two pieces of information included in the _pmtab file were described: the port monitor's version number and the port monitor part of the service entries in the port monitor's _pmtab file. When you add a new port monitor, the version number must be known so that the _pmtab file can be correctly initialized. When you add a new service, the port monitor part of the _pmtab entry must be formatted correctly.

Each port monitor must have an administrative command to perform these two tasks. The person who defines the port monitor must also define such an administrative command and its input options. When the command is invoked with these options, the information required for the port monitor part of the service entry must be correctly formatted for inclusion in the port monitor's _pmtab file and must be written to the standard output. To request the version number, the command must be invoked with a -V option. When it is invoked in this way, the port monitor's current version number must be written to the standard output.

If the command fails for any reason during the execution of either of these tasks, no data should be written to standard output.

Port Monitor/Service Interface

The interface between a port monitor and a service is determined solely by the service. Two mechanisms for invoking a service are presented here as examples.

New Service Invocations

The first interface is for services that are started anew with each request. This interface requires the port monitor to first fork() a child process. The child eventually becomes the designated service by performing an exec(). Before the exec() happens, the port monitor might take some port monitor-specific action. However, one action that must occur is the interpretation of the per-service configuration script, if one is present. This interpretation is done by calling the library routine doconfig().

Standing Service Invocations

The second interface is for invocations of services that are actively running. To use this interface, a service must have one end of a stream pipe open and be prepared to receive connections through it.

Port Monitor Requirements

To implement a port monitor, several generic requirements must be met. This section summarizes these requirements. In addition to the port monitor itself, you must supply an administrative command.

Initial Environment

When a port monitor is started, it expects an initial execution environment in which:

Important Files

Relative to its current directory, the key files listed in the following table exist for a port monitor.

Table F–3 Key Port Monitor Files

File 

Description 

_config

The port monitor's configuration script. The port monitor configuration script is run by the SAC. The SAC is started by init() as a result of an entry in /etc/inittab that calls the SAC.

_pid

The file into which the port monitor writes its process ID.

_pmtab

The port monitor's administrative file. This file contains information about the ports and services for which the port monitor is responsible.  

_pmpipe

The FIFO through which the port monitor receives messages from the SAC. 

svctag

The per-service configuration script for the service with the tag svctag.

../_sacpipe

The FIFO through which the port monitor sends messages to the SAC. 

Port Monitor Responsibilities

A port monitor is responsible for performing the following tasks in addition to its port monitor function:

A port monitor must perform the following tasks during service invocation:


Note –

Port monitors might ignore this flag if creating a utmp entry for the service does not make sense because of the manner in which the service is to be invoked. On the other hand, some services might not start properly unless utmp entries have been created for them.


Configuration Files and Scripts

This section describes configuration files and scripts.

Interpreting Configuration Scripts With doconfig()

The library routine doconfig(), defined in libnsl.so, interprets the configuration scripts contained in the files /etc/saf/_sysconfig, the per-system configuration file, /etc/saf/pmtag/_config, the per-port monitor configuration files, and /etc/saf/pmtag/svctag, the per-service configuration files. Its syntax is:

# include <sac.h>
int doconfig (int fd, char *script, long rflag);

rflag takes two values, NORUN and NOASSIGN, which may be OR'd. If rflag is zero, all commands in the configuration script are eligible to be interpreted. If rflag has the NOASSIGN bit set, the assign command is considered illegal and generates an error return. If rflag has the NORUN bit set, the run and runwait commands are considered illegal and generates error returns.

If a command in the script fails, the interpretation of the script ceases at that point and a positive integer is returned. This number indicates which line in the script failed. If a system error occurs, a value of -1 is returned.

If a script fails, the process with the environment being established should not be started.

In the following example, doconfig() is used to interpret a per-service configuration script.

. . .
 	if ((i = doconfig (fd, svctag, 0)) != 0){
 	error ("doconfig failed online %d of script %s",i,svctag);
}

Per-System Configuration File

The per-system configuration file, /etc/saf/_sysconfig, is delivered empty. You can use it to customize the environment for all services on the system by writing a command script in the interpreted language. This language is described in this chapter and on the doconfig(3NSL) man page. When the SAC is started, it calls the doconfig() function to interpret the per-system configuration script. The SAC is started when the system enters multiuser mode.

Per-Port Monitor Configuration Files

Per-port monitor configuration scripts (/etc/saf/pmtag/_config) are optional. They enable you to customize the environment for any given port monitor and for the services that are available through the ports for which that port monitor is responsible. Per-port monitor configuration scripts are written in the same language that is used for per-system configuration scripts.

The per-port monitor configuration script is interpreted when the port monitor is started. The port monitor is started by the SAC after the SAC has itself been started and after it has run its own configuration script, /etc/saf/_sysconfig.

The per-port monitor configuration script might override defaults provided by the per-system configuration script.

Per-Service Configuration Files

Per-service configuration files enable you to customize the environment for a specific service. For example, a service might require special privileges that are not available to the general user. Using the language described in the doconfig(3NSL) man page, you can write a script that grants or limits such special privileges to a particular service offered through a particular port monitor.

The per-service configuration might override defaults provided by higher-level configuration scripts. For example, the per-service configuration script might specify a set of STREAMS modules other than the default set.

Configuration Language

The language in which configuration scripts are written consists of a sequence of commands, each of which is interpreted separately. The following reserved keywords are defined: assign, push, pop, runwait, and run. The comment character is #. Blank lines are not significant. No line in a command script can exceed 1024 characters.

assign Keyword

The assign keyword is used to define environment variables.

assign variable=value

variable is the name of the environment variable and value is the value to be assigned to it. The value assigned must be a string constant. No form of parameter substitution is available. value can be quoted. The quoting rules are those that the shell uses for defining environment variables. assign fails if space cannot be allocated for the new variable or if any part of the specification is invalid.

push Keyword

The push keyword is used to push STREAMS modules onto the stream designated by fd. See the doconfig(3NSL) man page.

push module1[, module2, module3, ...]

module1 is the name of the first module to be pushed, module2 is the name of the second module to be pushed, and so on. The command fails if any of the named modules cannot be pushed. If a module cannot be pushed, the subsequent modules on the same command line are ignored and modules that have already been pushed are popped.

pop Keyword

The pop keyword is used to pop STREAMS modules off the designated stream.

pop [module]

If pop is invoked with no arguments, the top module on the stream is popped. If an argument is given, modules are popped one at a time until the named module is at the top of the stream. If the named module is not on the designated stream, the stream is left as it was and the command fails. If module is the special keyword ALL, then all modules on the stream are popped. Note that only modules above the topmost driver are affected.

runwait Keyword

The runwait keyword runs a command and waits for it to complete.

runwait command

command is the path name of the command to be run. The command is run with /bin/sh -c prepended to it. Shell scripts can thus be executed from configuration scripts. The runwait command fails if command cannot be found or cannot be executed, or if command exits with a nonzero status.

run Keyword

The run keyword is identical to runwait except that it does not wait for command to complete.

run command

command is the path name of the command to be run. run does not fail unless it is unable to create a child process to execute the command.

Although they are syntactically indistinguishable, some of the commands available to run and runwait are interpreter built-in commands. Interpreter built-ins are used when it is necessary to alter the state of a process within the context of that process. The doconfig() interpreter built-in commands are similar to the shell special commands and, like these commands, they do not spawn another process for execution. See the sh(1) man page. The initial set of built-in commands is:

cd ulimit umask

Printing, Installing, and Replacing Configuration Scripts

This section describes the form of the SAC and port monitor administrative commands used to install the three types of configuration scripts. Per-system and per-port monitor configuration scripts are administered using the sacadm command. Per-service configuration scripts are administered using the pmadm command.

Per-System Configuration Scripts

Per-system configuration scripts are administered by using the sacadm command.

sacadm -G  [ -z script ]

The -G option is used to print or replace the per-system configuration script. The -G option by itself prints the per-system configuration script. The -G option in combination with a -z option replaces /etc/saf/_sysconfig with the contents of the file script. Other combinations of options with a -G option are invalid.

The _sysconfig file in the following example sets the time zone variable, TZ.

assign TZ=EST5EDT # set TZ
runwait echo SAC is starting > /dev/console

Per-Port Monitor Configuration Scripts

Per-port monitor configuration scripts are administered by using the sacadm command.

sacadm -g -p pmtag [ -z script ]

The -g option is used to print, install, or replace the per-port monitor configuration script. A -g option requires a -p option. The -g option with only a -p option prints the per-port monitor configuration script for port monitor pmtag. The -g option with a -p option and a -z option installs the file script as the per-port monitor configuration script for port monitor pmtag. Or, if /etc/saf/pmtag/_config exists, these options replace _config with the contents of script. Other combinations of options with -g are invalid.

In the _config file, the command /usr/bin/daemon is assumed to start a daemon process that builds and holds together a STREAMS multiplexor. By installing this configuration script, the command can be executed just before starting the port monitor that requires it.

# build a STREAMS multiplexor
run /usr/bin/daemon
runwait echo $PMTAG is starting > /dev/console

Per-Service Configuration Scripts

Per-service configuration scripts are interpreted by the port monitor before the service is invoked.

pmadm -g -p pmtag -s svctag [ -z script ]
 pmadm -g -s svctag -t type -z script

Note –

The SAC interprets both its own configuration file, _sysconfig, and the port monitor configuration files. Only the per-service configuration files are interpreted by the port monitors.


The -g option is used to print, install, or replace a per-service configuration script. The -g option with a -p option and a -s option prints the per-service configuration script for service svctag available through port monitor pmtag. The -g option with a -p option, a -s option, and a -z option installs the per-service configuration script contained in the file script as the per-service configuration script for service svctag available through port monitor pmtag. The -g option with a -s option, a -t option, and a -z option installs the file script as the per-service configuration script for service svctag available through any port monitor of type type. Other combinations of options with -g are invalid.

The following per-service configuration script controls two settings: It specifies the maximum file size for files created by a process by setting the process's ulimit to 4096. It also specifies the protection mask to be applied to files created by the process by setting umask to 077.

runwait ulimit 4096
runwait umask 077

Sample Port Monitor Code

The following code example is a “null” port monitor that simply responds to messages from the SAC.


Example F–1 Sample Port Monitor

# include <stdlib.h>
# include <stdio.h>
# include <unistd.h>
# include <fcntl.h>
# include <signal.h>
# include <sac.h>

char Scratch[BUFSIZ]; /* scratch buffer */
char Tag[PMTAGSIZE + 1]; /* port monitor's tag */
FILE *Fp; /* file pointer for log file */
FILE *Tfp; /* file pointer for pid file */
char State; /* port monitor's current state*/

main(argc, argv)
	int argc;
	char *argv[];
{
	char *istate;
	strcpy(Tag, getenv("PMTAG"));
/*
 * open up a log file in port monitor's private directory
 */
	sprintf(Scratch, "/var/saf/%s/log", Tag);
	Fp = fopen(Scratch, "a+");
	if (Fp == (FILE *)NULL)
		exit(1);
	log(Fp, "starting");
/*
 * retrieve initial state (either "enabled" or "disabled") and set
 * State accordingly
 */
	istate = getenv("ISTATE");
	sprintf(Scratch, "ISTATE is %s", istate);
	log(Fp, Scratch);
	if (!strcmp(istate, "enabled"))
		State = PM_ENABLED;
	else if (!strcmp(istate, "disabled"))
		State = PM_DISABLED;
	else {
		log(Fp, "invalid initial state");
		exit(1);
	}
	sprintf(Scratch, "PMTAG is %s", Tag);
	log(Fp, Scratch);
/*
 * set up pid file and lock it to indicate that we are active
 */
	Tfp = fopen("_pid", "w");
	if (Tfp == (FILE *)NULL) {
		log(Fp, "couldn't open pid file");
		exit(1);
	}
	if (lockf(fileno(Tfp), F_TEST, 0) < 0) {
		log(Fp, "pid file already locked");
		exit(1);
	}
	fprintf(Tfp, "%d", getpid());
	fflush(Tfp);
	log(Fp, "locking file");
	if (lockf(fileno(Tfp), F_LOCK, 0) < 0) {
		log(Fp, "lock failed");
		exit(1);
	}
/*
 * handle poll messages from the sac ... this function never
returns
 */
	handlepoll();
	pause();
	fclose(Tfp);
	fclose(Fp);
}

handlepoll()
{
	int pfd; /* file descriptor for incoming pipe */
	int sfd; /* file descriptor for outgoing pipe */
	struct sacmsg sacmsg; /* incoming message */
	struct pmmsg pmmsg; /* outgoing message */
/*
 * open pipe for incoming messages from the sac
 */
	pfd = open("_pmpipe", O_RDONLY|O_NONBLOCK);
	if (pfd < 0) {
		log(Fp, "_pmpipe open failed");
		exit(1);
	}
/*
 * open pipe for outgoing messages to the sac
 */
	sfd = open("../_sacpipe", O_WRONLY);
	if (sfd < 0) {
		log(Fp, "_sacpipe open failed");
		exit(1);
	}
/*
 * start to build a return message; we only support class 1
messages
 */
	strcpy(pmmsg.pm_tag, Tag);
	pmmsg.pm_size = 0;
	pmmsg.pm_maxclass = 1;
/*
 * keep responding to messages from the sac
 */
 	for (;;) {
 		if (read(pfd, &sacmsg, sizeof(sacmsg)) != sizeof(sacmsg)) {
 			log(Fp, "_pmpipe read failed");
 			exit(1);
 		}
/*
 * determine the message type and respond appropriately
 */
 		switch (sacmsg.sc_type) {
 			case SC_STATUS:
 				log(Fp, "Got SC_STATUS message");
 				pmmsg.pm_type = PM_STATUS;
 				pmmsg.pm_state = State;
 				break;
 			case SC_ENABLE:
 				/*note internal state change below*/
 				log(Fp, "Got SC_ENABLE message");
 				pmmsg.pm_type = PM_STATUS;
 				State = PM_ENABLED;
 				pmmsg.pm_state = State;
 				break;
 			case SC_DISABLE:
 				/*noteinternalstatechangebelow*/
 				log(Fp, "Got SC_DISABLE message");
 				pmmsg.pm_type = PM_STATUS;
 				State = PM_DISABLED;
 				pmmsg.pm_state = State;
 				break;
 			case SC_READDB:
 				/*
				 * if this were a fully functional port monitor it
				 * would read _pmtab here and take appropriate action
				 */
 				log(Fp, "Got SC_READDB message");
 				pmmsg.pm_type = PM_STATUS;
 				pmmsg.pm_state = State;
 				break;
 			default:
 				sprintf(Scratch, "Got unknown message <%d>",
 				sacmsg.sc_type);
 				log(Fp, Scratch);
 				pmmsg.pm_type = PM_UNKNOWN;
 				pmmsg.pm_state = State;
 				break;
 		}
/*
 * send back a response to the poll
 * indicating current state
 */
 		if (write(sfd, &pmmsg, sizeof(pmmsg)) != sizeof(pmmsg))
 			log(Fp, "sanity response failed");
 	}
}
/*
 * general logging function
 */
log(fp, msg)
	FILE *fp;
	char *msg;
{
	fprintf(fp, "%d; %s\n", getpid(), msg);
	fflush(fp);
}

The following code example shows the sac.h header file.


Example F–2 sac.h Header File

/* length in bytes of a utmpx id */
# define IDLEN 4
/* wild character for utmpx ids */
# define SC_WILDC 0xff
/* max len in bytes for port monitor tag */
# define PMTAGSIZE 14
/*
 * values for rflag in doconfig()
 */
/* don't allow assign operations */
# define NOASSIGN 0x1
/* don't allow run or runwait operations */
# define NORUN 0x2
/*
 * message to SAC (header only). This header is forever fixed. The
 * size field (pm_size) defines the size of the data portion of
the
 * message, which follows the header. The form of this optional
data
 * portion is defined strictly by the message type (pm_type).
 */
struct pmmsg {
	char pm_type;               /* type of message */
	unchar pm_state;            /* current state of pm */
	char pm_maxclass;           /* max message class this port
monitor
					                understands */
	char pm_tag[PMTAGSIZE + 1]; /* pm's tag */
	int pm_size;                /* size of opt data portion */
};
/*
 * pm_type values
 */
# define PM_STATUS 1 /* status response */
# define PM_UNKNOWN 2 /* unknown message was received */
/*
 * pm_state values
 */
/*
 * Class 1 responses
 */
# define PM_STARTING 1   /* monitor in starting state */
# define PM_ENABLED 2    /* monitor in enabled state */
# define PM_DISABLED 3   /* monitor in disabled state */
# define PM_STOPPING 4   /* monitor in stopping state */
/*
 * message to port monitor
 */
struct sacmsg {
	int sc_size;         /* size of optional data portion */
	char sc_type;        /* type of message */
};
/*
 * sc_type values
 * These represent commands that the SAC sends to a port monitor.
 * These commands are divided into "classes" for extensibility.
Each
 * subsequent "class" is a superset of the previous "classes" plus
 * the new commands defined within that "class". The header for
all
 * commands is identical; however, a command may be defined such
that
 * an optional data portion may be sent in addition to the header.
 * The format of this optional data piece is self-defining based
on
 * the command. Important note:the first message sent by the SAC
is 
 * always be a class 1 message. The port monitor response
indicates
 * the maximum class that it is able to understand. Another note
is
 * that port monitors should only respond to a message with an
 * equivalent class response (i.e. a class 1 command causes a
class 1
 * response).
 */
/*
 * Class 1 commands (currently, there are only class 1 commands)
 */
# define SC_STATUS 1    /* status request *
# define SC_ENABLE 2    /* enable request */
# define SC_DISABLE 3   /* disable request */
# define SC_READDB 4    /* read pmtab request */
/*
 * `errno' values for Saferrno, note that Saferrno is used by both
 * pmadm and sacadm and these values are shared between them
 */
# define E_BADARGS 1   /* bad args/ill-formed cmd line */
# define E_NOPRIV 2    /* user not priv for operation */
# define E_SAFERR 3    /* generic SAF error */
# define E_SYSERR 4    /* system error */
# define E_NOEXIST 5   /* invalid specification */
# define E_DUP 6       /* entry already exists */
# define E_PMRUN 7     /* port monitor is running */
# define E_PMNOTRUN 8  /* port monitor is not running */
# define E_RECOVER 9   /* in recovery */

Logic Diagram and Directory Structure

Figure F-1 is a logical diagram of the SAF. It illustrates how a single service access controller can spawn a number of port monitors on a per-system basis. This technique means that several monitors can run concurrently, providing for the simultaneous operation of several different protocols.

Figure F–1 SAF Logical Framework

 This graphic depicts a service access controller spawning multiple port monitors to handle multiple services.

The following figure shows is the corresponding directory structure diagram. Following the diagram is a description of the files and directories.

Figure F–2 SAF Directory Structure

Text describes graphic.

The scripts and files in the SAF directory structure are: