ONC+ 開発ガイド

ポートモニタのサンプルプログラム

例 F-1 に示すのは、SAC からのメッセージに応答する以外は何もしないポートモニタのサンプルプログラムです。


例 F-1 ポートモニタのサンプル

# include <stdlib.h>
# include <stdio.h>
# include <unistd.h>
# include <fcntl.h>
# include <signal.h>
# include <sac.h>
 
char Scratch[BUFSIZ]; /* スクラッチバッファ */
char Tag[PMTAGSIZE + 1]; /* ポートモニタのタグ */
FILE *Fp; /* ログファイルへのファイルポインタ */
FILE *Tfp; /* pid ファイルへのファイルポインタ */
char State; /* ポートモニタの現在の状態 */
 
main(argc, argv)
	int argc;
 char *argv[];
{
	char *istate;
 strcpy(Tag, getenv("PMTAG"));
/*
 * ポートモニタのプライベートディレクトリにログファイルをオープンします。
 */
	sprintf(Scratch, "/var/saf/%s/log", Tag);
 Fp = fopen(Scratch, "a+");
	if (Fp == (FILE *)NULL)
 	exit(1);
	log(Fp, "starting");
/*
 * 初期状態 (enabled または disabled) を取り出して、
 * State をそれに合わせて設定します。
 */
	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);
/*
 * pid ファイルを設定してロックし、ポートモニタがアクティブなことを示します。
 */
	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);
	}
/*
 * sac からのポーリングメッセージを処理します。...この関数は終了しません。
 */
	handlepoll();
	pause();
 fclose(Tfp);
	fclose(Fp);
}
 
handlepoll()
{
 int pfd; /* 入力パイプのファイル記述子 */
	int sfd; /* 出力パイプのファイル記述子 */
 struct sacmsg sacmsg; /* 入力メッセージ */
	struct pmmsg pmmsg; /* 出力メッセージ */
/*
 * sac からのメッセージを受信するためのパイプをオープンします。
 */
	pfd = open("_pmpipe", O_RDONLY|O_NONBLOCK);
 if (pfd < 0) {
		log(Fp, "_pmpipe open failed");
 	exit(1);
	}
/*
 * sac へメッセージを送信するためのパイプをオープンします。
 */
	sfd = open("../_sacpipe", O_WRONLY);
	if (sfd < 0) {
		log(Fp, "_sacpipe open failed");
 	exit(1);
	}
/*
 * 応答メッセージの構築を開始します。ここでは、クラス 1 のメッセージだけを
 * サポートします。
 */
 strcpy(pmmsg.pm_tag, Tag);
	pmmsg.pm_size = 0;
 pmmsg.pm_maxclass = 1;
/*
 * sac からのメッセージへの応答を続けます。
 */
 	for (;;) {
 		if (read(pfd, &sacmsg, sizeof(sacmsg)) != sizeof(sacmsg)) {
 			log(Fp, "_pmpipe read failed");
 			exit(1);
 		}
/*
 * メッセージのタイプを判定して、正しい応答を返します。
 */
 		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:
 				/* 内部状態が変わっていることに注意 */
 				log(Fp, "Got SC_ENABLE message");
 				pmmsg.pm_type = PM_STATUS;
 				State = PM_ENABLED;
 				pmmsg.pm_state = State;
 				break;
 			case SC_DISABLE:
 				/*内部状態が変わっていることに注意 */
 				log(Fp, "Got SC_DISABLE message");
 				pmmsg.pm_type = PM_STATUS;
 				State = PM_DISABLED;
 				pmmsg.pm_state = State;
 				break;
 		 case SC_READDB:
 			/*
				 * 完全なポートモニタの場合は、ここで _pmtab を読み込んで
				 * 必要なアクションを起こします。 
				 */
 				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;
 		}
/*
 * ポーリングに対する応答を返し、
 * 現在の状態を知らせます。
 */
 		if (write(sfd, &pmmsg, sizeof(pmmsg)) != sizeof(pmmsg))
 			log(Fp, "sanity response failed");
 	}
}
/*
 * 一般的なログ機能
 */
log(fp, msg)
 FILE *fp;
	char *msg;
{
	fprintf(fp, "%d; %s¥n", getpid(), msg);
	fflush(fp);
}

例 F-2 はヘッダーファイル sac.h を示しています。


例 F-2 ヘッダーファイル sac.h

/* utmpx id のバイト数 */
# define IDLEN 4
/* utmpx ids のワイルド文字 */
# define SC_WILDC 0xff
/* ポートモニタタグの最大長 (バイト数) */
# define PMTAGSIZE 14
/*
 * doconfig() のための rflag 値
 */
/* assign コマンドは許可しない */
# define NOASSIGN 0x1
/* run と runwait のコマンドは許可しない */
# define NORUN 0x2
/*
 * sac へのメッセージ (ヘッダーのみ)。このヘッダー形式はずっと固定されます。
 * サイズフィールド (pm_size) には、ヘッダーに続くオプションデータ部のサイズを
 * 指定します。オプションデータ部の形式は、メッセージタイプ (pm_type) によって
 * 厳密に定義されます。
 */
struct pmmsg {
	char pm_type;               /* メッセージタイプ */
	unchar pm_state;            /* ポートモニタの現在の状態 */
	char pm_maxclass;           /* ポートモニタが理解できる最大の
 				                メッセージクラス */
	char pm_tag[PMTAGSIZE + 1]; /* ポートモニタのタグ */
	int pm_size;                /* オプションデータ部のサイズ */
};
/*
 * pm_type の値
 */
# define PM_STATUS 1 /* 応答ステータス */
# define PM_UNKNOWN 2 /* 未知のメッセージを受信しました */
/*
 * pm_state の値
 */
/*
 * クラス 1 の応答
 */
# define PM_STARTING 1   /* ポートモニタは起動状態 */
# define PM_ENABLED 2    /* ポートモニタは有効状態 */
# define PM_DISABLED 3   /* ポートモニタは無効状態 */
# define PM_STOPPING 4   /* ポートモニタは停止状態 */
/*
 * ポートモニタへのメッセージ
 */
struct sacmsg {
	int sc_size;         /* オプションデータ部のサイズ */
	char sc_type;        /* メッセージタイプ */
};
/*
 * sc_type の値
 * 次に定義するコマンドは SAC がポートモニタに送信するコマンドです。
 * 拡張性を持たせるため、コマンドは各クラスに分類されます。
 * 後に定義されるクラスは、それまでに定義されたクラスのコマンドに、
 * そのクラスの新たなコマンドが追加されたスーパーセットです。
 * どのクラスもヘッダーは同じです。新たなコマンドは、
 * ヘッダーにオプションデータ部が追加される形で定義されます。
 * オプションデータ部の形式は、コマンドで自動的に決まります。
 * 重要な注: SAC から最初に送信されるメッセージは常にクラス 1 の
 * メッセージです。これに対して、ポートモニタは応答メッセージで
 * 自分が理解できる最大のクラスを知らせます。
 * もう 1 つ注意しなければならないのは、ポートモニタは必ず
 * 受信したメッセージと同じクラスで応答しなければならないことです。
 * (すなわち、クラス 1 のコマンドには必ずクラス 1 で応答します。)
 */
/*
 * クラス 1 のコマンド (現在は、クラス 1 のコマンドしかありません)
 */
# define SC_STATUS 1    /* ステータス要求 */
# define SC_ENABLE 2    /* 有効に設定 */
# define SC_DISABLE 3   /* 無効に設定 */
# define SC_READDB 4    /* pmtab の読み込みを要求 */
/*
 * Saferrno のエラー番号。Saferrno は pmadm と sacadm の両方で共有する
 * ことに注意してください。
 */
# define E_BADARGS 1   /* 引数またはコマンド行が不正 */
# define E_NOPRIV 2    /* ユーザーは操作特権を持っていない */
# define E_SAFERR 3    /* 一般的な SAF エラー */
# define E_SYSERR 4    /* システムエラー */
# define E_NOEXIST 5   /* 指定が無効 */
# define E_DUP 6       /* エントリが既に存在している */
# define E_PMRUN 7     /* ポートモニタが実行中 */
# define E_PMNOTRUN 8  /* ポートモニタが実行されていない */
# define E_RECOVER 9   /* 修復中 */