この例では、メッセージログサーバーを実装しています。クライアントは、ログ (実体はテキストファイル) を開いたり、メッセージログを保存したり、ログを閉じたりするのにこのサーバーを使用することができます。
log.x ファイルは、ログプログラムのインタフェースを記述します。
enum log_severity { LOG_EMERG=0, LOG_ALERT=1, LOG_CRIT=2, LOG_ERR=3,
LOG_WARNING=4, LOG_NOTICE=5, LOG_INFO=6 };
program LOG {
version LOG_VERS1 {
int OPENLOG(string ident) = 1;
int CLOSELOG(int logID) = 2;
oneway WRITELOG(int logID, log_severity severity,
string message) = 3;
} = 1;
} = 0x20001971;
2 つのプロシージャ (OPENLOG および CLOSELOG) は、logID で指定されたログをそれぞれ開いたり閉じたりします。WRITELOG() プロシージャ (この例では oneway として宣言されている) は、開かれたログにメッセージを記録します。ログメッセージは、重要度属性およびテキストメッセージを含みます。
これは、ログサーバーの Makefile です。この Makefile を使用して、log.x ファイルを呼び出します。
RPCGEN = rpcgen CLIENT = logClient CLIENT_SRC = logClient.c log_clnt.c log_xdr.c CLIENT_OBJ = $(CLIENT_SRC:.c=.o) SERVER = logServer SERVER_SRC = logServer.c log_svc.c log_xdr.c SERVER_OBJ = $(SERVER_SRC:.c=.o) RPCGEN_FILES = log_clnt.c log_svc.c log_xdr.c log.h CFLAGS += -I. RPCGEN_FLAGS = -N -C LIBS = -lsocket -lnsl all: log.h ./$(CLIENT) ./$(SERVER) $(CLIENT): log.h $(CLIENT_OBJ) cc -o $(CLIENT) $(LIBS) $(CLIENT_OBJ) $(SERVER): log.h $(SERVER_OBJ) cc -o $(SERVER) $(LIBS) $(SERVER_OBJ) $(RPCGEN_FILES): log.x $(RPCGEN) $(RPCGEN_FLAGS) log.x clean: rm -f $(CLIENT_OBJ) $(SERVER_OBJ) $(RPCGEN_FILES)
logServer.c は、ログサーバーの実装を示します。ログサーバーは、ログメッセージを保存するためにファイルを開くため、openlog_1_svc() にクロージャー接続コールバックを登録します。クライアントプログラムが closelog() プロシージャを呼び出すことを忘れた (または呼び出す前にクラッシュした) 場合でも、ファイル記述子が閉じられるようにこのコールバックが使用されます。この例は、RPC サーバー内のクライアントに関連付けられたリソースを解放するのに接続クロージャーコールバック機能を使用する方法を例示しています。
#include "log.h"
#include <stdio.h>
#include <string.h>
#define NR_LOGS 3
typedef struct {
SVCXPRT* handle;
FILE* filp;
char* ident;
} logreg_t;
static logreg_t logreg[NR_LOGS];
static char* severityname[] = {"Emergency", "Alert", "Critical", "Error",
"Warning", "Notice", "Information"};
static void
close_handler(const SVCXPRT* handle, const bool_t);
static int
get_slot(SVCXPRT* handle)
{
int i;
for (i = 0; i < NR_LOGS; ++i) {
if (handle == logreg[i].handle) return i;
}
return -1;
}
static FILE*
_openlog(char* logname)
/*
* ログファイルを開く
*/
{
FILE* filp = fopen(logname, "a");
time_t t;
if (NULL == filp) return NULL;
time(&t);
fprintf(filp, "Log opened at %s\n", ctime(&t));
return filp;
}
static void
_closelog(FILE* filp)
{
time_t t;
time(&t);
fprintf(filp, "Log close at %s\n", ctime(&t));
/*
* ログファイルを閉じる
*/
fclose(filp);
}
int*
openlog_1_svc(char* ident, struct svc_req* req)
{
int slot = get_slot(NULL);
FILE* filp;
static int res;
time_t t;
if (-1 != slot) {
FILE* filp = _openlog(ident);
if (NULL != filp) {
logreg[slot].filp = filp;
logreg[slot].handle = req->rq_xprt;
logreg[slot].ident = strdup(ident);
/*
* クライアントが clnt_destroy を呼び出すか、
* クライアントの接続が切断されて clnt_destroy が
* 自動的に呼び出されると、サーバーは
* close_handler コールバックを実行する
*/
if (!svc_control(req->rq_xprt, SVCSET_RECVERRHANDLER,
(void*)close_handler)) {
puts("Server: Cannot register a connection closure
callback");
exit(1);
}
}
}
res = slot;
return &res;
}
int*
closelog_1_svc(int logid, struct svc_req* req)
{
static int res;
if ((logid>= NR_LOGS) || (logreg[logid].handle != req->rq_xprt)) {
res = -1;
return &res;
}
logreg[logid].handle = NULL;
_closelog(logreg[logid].filp);
res = 0;
return &res;
}
/*
* メッセージをログへ書き込むよう要求があると、
* write_log_1_svc が呼び出される
*/
void*
writelog_1_svc(int logid, log_severity severity, char* message,
struct svc_req* req)
{
if ((logid>= NR_LOGS) || (logreg[logid].handle != req->rq_xprt)) {
return NULL;
}
/*
* メッセージをファイルへ書き込む
*/
fprintf(logreg[logid].filp, "%s (%s): %s\n",
logreg[logid].ident, severityname[severity], message);
return NULL;
}
static void
close_handler(const SVCXPRT* handle, const bool_t dummy)
{
int i;
/*
* クライアントの接続が切断されると、closelog でログが閉じられる
*/
for (i = 0; i < NR_LOGS; ++i) {
if (handle == logreg[i].handle) {
logreg[i].handle = NULL;
_closelog(logreg[i].filp);
}
}
}
logClient.c ファイルは、ログサーバーを使用するクライアントを示しています。
#include "log.h"
#include <stdio.h>
#define MSG_SIZE 128
void
usage()
{
puts("Usage: logClient <logserver_addr>");
exit(2);
}
void
runClient(CLIENT* clnt)
{
char msg[MSG_SIZE];
int logID;
int* result;
/*
* クライアントがログを開く
*/
result = openlog_1("client", clnt);
if (NULL == result) {
clnt_perror(clnt, "openlog");
return;
}
logID = *result;
if (-1 == logID) {
puts("Cannot open the log.");
return;
}
while(1) {
struct rpc_err e;
/*
* クライアントがメッセージをログに書き込む
*/
puts("Enter a message in the log (\".\" to quit):");
fgets(msg, MSG_SIZE, stdin);
/*
* 末尾の CR を削除する
*/
msg[strlen(msg)-1] = 0;
if (!strcmp(msg, ".")) break;
if (writelog_1(logID, LOG_INFO, msg, clnt) == NULL) {
clnt_perror(clnt, "writelog");
return;
}
}
/*
* クライアントがログを閉じる
*/
result = closelog_1(logID, clnt);
if (NULL == result) {
clnt_perror(clnt, "closelog");
return;
}
logID = *result;
if (-1 == logID) {
puts("Cannot close the log.");
return;
}
}
int
main(int argc, char* argv[])
{
char* serv_addr;
CLIENT* clnt;
if (argc != 2) usage();
serv_addr = argv[1];
clnt = clnt_create(serv_addr, LOG, LOG_VERS1, "tcp");
if (NULL == clnt) {
clnt_pcreateerror("Cannot connect to log server");
exit(1);
}
runClient(clnt);
clnt_destroy(clnt);
}