ONC+ Developer's Guide

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 */