この例では、メッセージログサーバーを実装しています。クライアントは、ログ (実体はテキストファイル) を開いたり、メッセージログを保存したり、ログを閉じたりするのにこのサーバーを使用することができます。
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); }