ONC+ 開発ガイド

クライアント接続クロージャーコールバックを使用した例

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

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