ONC+ 開発ガイド

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

次のコーディング例は、SAC からのメッセージに応答するだけの NULL ポートモニターです。


例 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);
}

次のコーディング例は sac.h ヘッダーファイルを示します。


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

/* utmpx ID のバイト長 */
# define IDLEN 4
/* utmpx ID で使用できるワイルド文字 */
# define SC_WILDC 0xff
/* ポートモニターのタグの最大バイト長 */
# define PMTAGSIZE 14
/*
 * doconfig() の rflag 値
 */
/* assign を許可しない */
# define NOASSIGN 0x1
/* run または runwait を許可しない */
# define NORUN 0x2
/*
 * SAC へのメッセージ (ヘッダーのみ) このヘッダーは常に固定。
 * size フィールド (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 の errno 値。pmadm と sacadm の両方が Saferrno を使用し、
 * これらの値はこの両者で共有される点に注意してください。
 */
# 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   /* 修復中 */