Skip to content

Instantly share code, notes, and snippets.

@tsutsui
Last active December 3, 2025 23:50
Show Gist options
  • Select an option

  • Save tsutsui/cd384da1aa204ea51e685426fa15be05 to your computer and use it in GitHub Desktop.

Select an option

Save tsutsui/cd384da1aa204ea51e685426fa15be05 to your computer and use it in GitHub Desktop.
CISC-NEWS NWS-1750 PROM のネットワークブート仕様に対応するサーバー側デーモンの関数実装レベルの設計書

remotediskd(8) 関数仕様(案)

0. 全体方針

  • 単一プロセス・単一スレッドのイベントループ型デーモン。

  • コマンドラインオプション → struct rdd_cliopt に詰める。

  • struct rdd_config は「現在有効な設定 + クライアント状態」をまとめて持つ。

  • シグナルハンドラは volatile sig_atomic_t フラグを立てるだけ。

    • 実際の設定リロード、終了処理はイベントループ側で行う。
  • ネットワークブートは頻度が低い前提なので、効率よりも実装の素直さ・可読性優先。


1. データ構造定義

1.1 共通構造体 extern.h(公開インタフェース)

/* コマンドラインオプション */
struct rdd_cliopt {
	const char *conf_path;   /* -c path: 設定ファイル */
	const char *ifname;      /* -i interface: 受信/送信に使うインタフェース */
	const char *image_root;  /* -s dir: イメージファイルのルートディレクトリ */

	int         debug_mode;  /* -d: 1=フォアグラウンド (デーモン化しない) */
	int         debug_level; /* -d/-v の累積回数に応じたログ詳細レベル */
};

/* デーモン全体の設定+状態 */
struct rdd_config {
	/* 設定パス関連 */
	char   *config_path;     /* 設定ファイルパス (デフォルト: /etc/remotediskd.conf) */
	char   *image_root;      /* イメージルート (デフォルト: /tftpboot) */
	char   *ifname;          /* 使用インタフェース名 (NULL なら自動選択) */

	/* デバッグ / ログ */
	int     debug;           /* デバッグモード (-d) なら 1: デーモン化しない */
	int     debug_level;     /* ログ詳細度 */

	/* インタフェース情報 */
	int     if_index;        /* if_nametoindex() 結果 */
	struct in_addr if_addr;  /* 応答に使う IPv4 アドレス  */
	int     if_mtu;          /* MTU (必要なら) */

	/* RPC / SRQ 用ソケット */
	int     srq_sock;        /* UDP 3073 */
	int     rpc_sock;        /* UDP 3072 */

	/* クライアント配列 */
	struct rdd_client *clients;
	int     nclients;

	/* イメージ idle タイムアウト(秒) */
	time_t  idle_timeout;    /* デフォルト 300 秒程度を想定 */

	/* 未知クライアントログ抑止用 (MAC 単位で簡易キャッシュ) */
	time_t  unknown_log_time[UNKNOWN_CACHE_SIZE];
	uint8_t unknown_log_mac[UNKNOWN_CACHE_SIZE][6];
};
/*
 * 設定ファイル中の 1 エントリに対応する単位。
 * - hostname
 * - image_list で指定された unit ごとのイメージパス
 * - ランタイム状態(各ユニットの open/mmap 情報・最終アクセス時刻など)
 */
struct rdd_client {
	char           *hostname;     /* remotediskd.conf の host 名 */

	struct rdd_image_unit *units; /* ユニット配列 */
	int             nunits;       /* ユニット数 */
};

/etc/ethers による MAC 解決は config 読み込み時には行わず、 実際の RPC 受信時に client_lookup()(client.c)側から行う。

1.1.1 struct rdd_cliopt

コマンドラインオプションを一時的に保持する構造体。 getopt() の結果はすべてここに詰める。config には直接触らない。

struct rdd_cliopt {
	const char *conf_path;   /* -c path: 設定ファイル */
	const char *ifname;      /* -i interface: 受信/送信に使うインタフェース */
	const char *image_root;  /* -s dir: イメージファイルのルートディレクトリ */

	int         debug_mode;  /* -d: 1=フォアグラウンド (デーモン化しない) */
	int         debug_level; /* -d/-v の累積回数に応じたログ詳細レベル */
};
  • conf_path / ifname / image_rootconfig_init() 内で strdup() / estrdup() して rdd_config にコピーされる。
  • debug_modecfg->debug に反映(デーモン化を抑制)。
  • debug_levelcfg->debug_level にコピー。

1.1.2 struct rdd_image_unit

1 ユニット(1 つのイメージファイル)の状態。

struct rdd_image_unit {
	char   *path;         /* config_read() 時に image_root と結合して決まるフルパス */
	int     fd;           /* オープン済みならファイルディスクリプタ、未openなら -1 */
	void   *map;          /* mmap アドレス or NULL */
	size_t  map_len;      /* mmap 長さ (ファイルサイズ) */
	time_t  last_access;  /* 最後に READ された時刻 (idle cleanup 用) */
};
  • path は設定ファイル読み込み時に /tftpboot 等と結合して決まる。
  • fd, map, map_len, last_accessimage.c が管理
  • OPEN 時に fd / map / map_len / last_access を設定。
  • READ 時に last_access を更新。
  • CLOSE または idle cleanup で munmap() / close() / fd=-1 / map=NULL / map_len=0

1.1.3 struct rdd_client

設定済みクライアント 1 台分の情報。

struct rdd_client {
	char           *hostname;     /* remotediskd.conf の host 名 */

	struct rdd_image_unit *units; /* ユニット配列 */
	int             nunits;       /* ユニット数 */
};
  • hostname は設定ファイルから。

  • /etc/ethers との対応づけは config 読み込み時には行わない。 実際の要求受信時に、client_lookup() 内で「MAC → hostname 逆引き」し、その hostname とここで設定された hostname を比較してクライアントを特定する。

  • unitsnunits 長の配列。各要素が image_unit

    • 設定ファイル:diskunit0.img,diskunit1.imgnunits=2, units[0].path=".../diskunit0.img", units[1].path=".../diskunit1.img" など(実際には image_root と結合したパスを保持)。

1.1.4 struct rdd_config

サーバ全体の設定とランタイム状態。

struct rdd_config {
	/* 設定パス関連 */
	char   *config_path;     /* 設定ファイルパス (デフォルト: /etc/remotediskd.conf) */
	char   *image_root;      /* イメージルート (デフォルト: /tftpboot) */
	char   *ifname;          /* 使用インタフェース名 (NULL なら自動選択) */

	/* デバッグ / ログ */
	int     debug;           /* デバッグモード (-d) なら 1: デーモン化しない */
	int     debug_level;     /* ログ詳細度 */

	/* インタフェース情報 */
	int     if_index;        /* if_nametoindex() 結果 */
	struct in_addr if_addr;  /* 応答に使う IPv4 アドレス  */
	int     if_mtu;          /* MTU (必要なら) */

	/* RPC / SRQ 用ソケット */
	int     srq_sock;        /* UDP 3073 */
	int     rpc_sock;        /* UDP 3072 */

	/* クライアント配列 */
	struct rdd_client *clients;
	int     nclients;

	/* イメージ idle タイムアウト(秒) */
	time_t  idle_timeout;    /* デフォルト 300 秒程度を想定 */

	/* 未知クライアントログ抑止用 (MAC 単位で簡易キャッシュ) */
	time_t  unknown_log_time[UNKNOWN_CACHE_SIZE];
	uint8_t unknown_log_mac[UNKNOWN_CACHE_SIZE][6];
};
  • config_init() が:

    • デフォルト値設定
    • rdd_cliopt を反映
    • idle_timeout にデフォルト値を設定 (例: 300)。
  • config_read() / config_reload() が:

    • clients[] を構築 /再構築。
    • image_root を基準に units[i].path を絶対パス化して保存。

2. モジュール別関数仕様

以降はファイルごとの関数と、その入出力・責務を定義します。


2.1 main.c

2.1.1 役割

  • エントリポイント。
  • コマンドラインオプション解析。
  • rdd_clioptrdd_config 初期化。
  • ログ/シグナル/インタフェース/ソケット/設定ファイルの初期化。
  • デーモン化。
  • イベントループ。
  • 終了時 cleanup。

2.1.2 関数

static void usage(const char *progname)
  • 入力:

    • progname:表示用プログラム名 (通常 argv[0])。
  • 処理:

    • fprintf(stderr, ...) で オプション一覧と簡潔な説明を出す。
  • 出力:

    • 返り値なし。呼び出し側が exit(EXIT_FAILURE) 等を行う。

オプション仕様:

  • -i interface : 使用インタフェース名(省略時は自動選択)
  • -c config : 設定ファイルパス(省略時 /etc/remotediskd.conf
  • -s dir : イメージファイルのルートディレクトリ(省略時 /tftpboot
  • -d : デバッグモード(フォアグラウンド / ログ詳細度も 1 増加)
  • -v : ログ詳細度を 1 段階上げる
  • -h / ? : usage 表示
int main(int argc, char *argv[])

処理フロー(確定版)

  1. log_init(argv[0]);

  2. struct rdd_cliopt opt; memset(&opt, 0, sizeof(opt));

  3. getopt() でオプションを解析し、すべて opt に格納

    • -iopt.ifname
    • -copt.conf_path
    • -sopt.image_root
    • -dopt.debug_mode = 1; opt.debug_level++;
    • -vopt.debug_level++;
    • -h / ? or unknown → usage() を呼んで終了。
  4. struct rdd_config cfg;

  5. config_init(&cfg, &opt) を呼ぶ。

  6. signal_init() を呼ぶ。

    • 失敗したらエラーで終了。
  7. config_read(&cfg) を呼ぶ。

    • /etc/remotediskd.conf 読み込み。
    • エラーならログを出して終了。
  8. setup_interface(&cfg)

    • cfg.ifname が NULL なら自動選択ルールに従う。
    • IPv4 アドレスを cfg.if_addr に格納。
  9. rpc_setup_sockets(&cfg)

    • cfg.srq_sock / cfg.rpc_sock を作成しバインド。
  10. if (!cfg.debug) daemonize();

    • デバッグモードでなければバックグラウンド化。
    • デーモン化に失敗したらエラーログ出して終了。
  11. event_loop(&cfg) を呼ぶ。

  12. 戻り値に応じて exit code を決定(正常終了かエラー終了)。

  13. cleanup(&cfg);

  14. log_close();

  15. return。

static int event_loop(struct rdd_config *cfg)
  • 入力:

    • cfg: サーバ状態。
  • 処理:

    • for (;;) ループ。

    • fd_setcfg->srq_sock, cfg->rpc_sock を登録し select() または pselect() で待機。

    • タイムアウトを 1 秒程度に設定し、周期的に idle cleanup や SIGHUP 処理も行う。

    • reload_pending() が真なら:

      • clear_reload();
      • config_reload(cfg);(失敗したらログを出す。 → 失敗時の挙動は:**旧設定のまま継続でもよいが、ここでは「旧設定を保持して継続」**とする)
    • terminate_pending() が真なら:

      • clear_terminate();
      • ループを抜けて終了。
    • select() 戻り値 > 0 の場合:

      • FD_ISSET(cfg->srq_sock)rpc_srq_recv(cfg);
      • FD_ISSET(cfg->rpc_sock)rpc_recv(cfg);
    • 毎ループまたは 1 秒おきに:

      • now = time(NULL);
      • image_idle_cleanup(cfg, now); を呼ぶ。
  • 出力:

    • 0 : 正常終了(SIGTERM など)。
    • 非0 : 致命的エラーで終了(主要なエラーはログ出力済み)。
static void cleanup(struct rdd_config *cfg)
  • ソケットを閉じる:rpc_close_sockets(cfg);
  • 全イメージをクローズ:image_close_all(cfg);
  • クライアント配列解放:client_free_all(cfg);
  • コンフィグ周りのメモリ開放:config_free(cfg);

2.2 log.c

公開関数

void log_init(const char *progname);
void log_close(void);
void log_msg(int prio, const char *fmt, ...) __printflike(2, 3);
void log_debug(int level, const char *fmt, ...) __printflike(2, 3);

仕様

  • log_init():

    • openlog(progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
    • デバッグモード(cfg.debugopt.debug_mode)に応じて、stderr にも出すかどうかは実装側で決める。
  • log_close():

    • syslog 使用時は closelog()
  • log_msg():

    • syslog or stderr に共通メッセージ出力。
  • log_debug(level, ...):

    • level <= cfg->debug_level のときのみメッセージ出力。
    • cfg->debug_levelstruct rdd_config 内の値を参照する(log.c 内で extern struct rdd_config * もしくは setter 関数などで接続する想定)。

2.3 signal.c

公開関数

int  signal_init(void);
int  reload_pending(void);
int  terminate_pending(void);
void clear_reload(void);
void clear_terminate(void);

仕様

  • 内部に static volatile sig_atomic_t reload_flag, terminate_flag; を持つ。

  • signal_init():

    • SIGHUP → reload_flag = 1
    • SIGTERM / SIGINT → terminate_flag = 1
  • reload_pending() / terminate_pending():

    • それぞれのフラグを読み出して返す(クリアはしない)。
  • clear_reload() / clear_terminate():

    • フラグを 0 にする。
  • いずれの関数もシグナルマスクを変更しない。

  • SIGHUP が連続して来た場合、reload_pending() / clear_reload() の組み合わせで、最低でも 1 回は確実にリロードされる動きとする。


2.4 config.c

公開関数

void config_init(struct rdd_config *cfg, const struct rdd_cliopt *co);
int  config_read(struct rdd_config *cfg);
int  config_reload(struct rdd_config *cfg);
void config_free(struct rdd_config *cfg);

2.4.1 config_init()

  • cfg 全体をゼロクリア。

  • config_path / ifname / image_root / debug_level を:

    • co->conf_path / co->ifname / co->image_root / co->debug_level が非 NULL / 非 0 ならそちらを採用。

    • そうでない場合はビルドインのデフォルトを設定:

      • config_path : /etc/remotediskd.conf
      • image_root : /tftpboot
      • ifname : NULL(後で setup_interface() が決定)
      • debug_level : 0
  • debugco->debug_mode が真なら 1、それ以外は 0。

  • idle_timeout は定数デフォルト(例: 300 秒)。

  • unknown_log_time[] / unknown_log_mac[][] をゼロ初期化。

2.4.2 config_read()

  • 処理:

    1. fopen(cfg->config_path, "r")。失敗時はエラーをログして -1
    2. 行バッファ char line[1024]; を用いて fgets() で 1 行ずつ読む。
    3. 行ごとに parse_config_line(cfg, line, lineno) を呼ぶ。
    4. 最後に fclose() して 0 を返す(致命的エラーがあれば途中で -1)。
  • 行が長すぎる(末尾に改行無し)の場合:

    • 残りを読み捨てた上で、その行は警告して無視。

2.4.3 parse_config_line() の詳細

(static 関数として config.c 内部に定義)

  1. 行末の \n / \r を削る。

  2. 行頭の空白(space / tab)を飛ばす。

  3. 空行 or 行頭 # はコメントとして無視。

  4. hostname トークン抽出:

    • 空白・タブ・# までを hostname とする。
    • hostname が空なら警告して無視。
  5. hostname の後の空白を飛ばす。

  6. 2 列目(image_list)の生文字列を取り出す:

    • # をコメントの開始とするが、\# でエスケープされた # は有効文字とする。
    • img_start 〜 末尾までの文字列から、末尾空白をトリム。
    • 空なら「empty image list」として警告し、その行を無視。
  7. parse_image_list(img_start, &paths, &nunits, cfg->config_path, lineno) を呼び、カンマ区切り+\ エスケープ付き文字列を unit ごとのパス配列に変換。

    • 成功時: pathschar **estrdup() 済み)、nunits > 0
    • 失敗時: その行を無視。
  8. 新しい struct rdd_client を確保し、以下を設定:

    • client->hostname = estrdup(hostname);
    • client->nunits = nunits;
    • client->unitscalloc(nunits, sizeof(struct rdd_image_unit)) で確保し、 parse_image_list() で得た paths[i] について、
      • 先頭が / ならそのまま絶対パスとみなして units[i].pathestrdup()
      • そうでなければ cfg->image_root + "/" + paths[i] を連結して 絶対パス文字列を作り、units[i].pathestrdup()
    • ランタイム状態フィールド(fd/map/last_access 等)は「未オープン」状態で初期化。

※ ここでは /etc/ethersether_hostton() / ether_ntohost() による MAC 解決は行わない。 MAC → hostname の解決は client_lookup() 側で実施する。

2.4.4 parse_image_list() の仕様

static int
parse_image_list(const char *img_list,
    char ***out_paths, int *out_nunits,
    const char *conf_path, int lineno);
  • 文字単位のステートマシンでパース:

    • 状態:

      • OUTSIDE_TOKEN(トークン外、区切りを飛ばしている)
      • INSIDE_TOKEN(トークン構築中)
    • 区切り:

      • , / space / tab
    • エスケープ:

      • \XX をそのままトークンに追加

        • 行末の単独 \ はエラー。
  • トークンが完成するたびに estrdup(token) して paths[] に追加。

  • トークン長上限は PATH_MAX 相当のバッファ長で管理し、超えたら行ごと無視して警告。

  • 有効なトークンが 1 つもなければエラー(行無視)。

2.4.5 config_reload()

  • 実装方針:

    • 新しい struct rdd_config を別途作ってリロードが成功したら差し替える。失敗した場合は元の設定を維持。
  • 具体的には:

int
config_reload(struct rdd_config *cfg)
{
    struct rdd_config newcfg;

    /* cfg から path / image_root / ifname / debug_level などだけコピーして
       newcfg を config_init 相当で初期化(clients は空) */

    config_init_for_reload(&newcfg, cfg); /* 仮のヘルパ */

    if (config_read(&newcfg) != 0) {
        /* パース失敗: newcfg を捨てて旧 cfg で継続 */
        config_free(&newcfg);
        return -1;
    }

    /* ここで初めて旧 clients/images を閉じる */
    image_close_all(cfg);
    client_free_all(cfg);

    /* newcfg.clients などを cfg に移す(ポインタを丸ごとムーブ) */
    move_clients(&newcfg, cfg);

    config_free(&newcfg); /* cfg に移さなかった部分だけ片付け */

    return 0;
}
  • シグナルとの整合性:

    • 再読み込み中もシグナルハンドラはフラグを立てるだけなので、再入の心配はない。
    • event_loop() の次の周回で再度 reload_pending() が真なら、さらに config_reload() が呼ばれるだけ。

2.4.6 config_free()

  • (クライアントやイメージの解放は cleanup() 側で済ませている前提)
  • free(cfg->config_path), free(cfg->image_root), free(cfg->ifname) など、 config_init()strdup() した文字列を解放。
  • cfg のポインタフィールドを NULL に戻す(安全のため)。

2.5 client.c

公開関数

struct rdd_client *
client_lookup(struct rdd_config *,
    const uint8_t mac[6], const struct sockaddr_in *src);

void client_free_all(struct rdd_config *);

仕様

  • client_lookup():

    1. mac から /etc/ethers を使ってホスト名に逆引き:

      • ether_ntohost() などを使用し、hostname_from_ethers を得る。
    2. 逆引きに失敗した場合:

      • cfg->unknown_log_mac[] / cfg->unknown_log_time[] を参照。
      • 一定時間(例: 10 秒)以内に同じ MAC からの要求が続く場合はログを抑制。
      • 「unknown client (MAC=xx:xx:xx:xx:xx:xx) from src」が一定間隔でだけログに出る想定。
      • NULL を返す。
    3. 成功した場合:

      • 得られた hostname_from_etherscfg->clients[i].hostname を比較。
      • 一致するクライアント構造体を返す。
      • 見つからなければ unknown と同様にログを出す(ただしメッセージを変える)。
  • client_free_all():

    • cfg->clients 配列をなめて、

      • client->hostnamefree()
      • 各ユニット units[i].pathfree()
      • units 配列本体の free()
    • 最後に free(cfg->clients); cfg->clients = NULL; cfg->nclients = 0;


2.6 image.c

公開関数

int     image_open_unit(struct rdd_config *, struct rdd_client *, int unit);
ssize_t image_read(struct rdd_config *, struct rdd_client *, int unit,
            off_t off, void *buf, size_t len);
void    image_close_unit(struct rdd_config *, struct rdd_client *, int unit);
void    image_close_all(struct rdd_config *);      /* 全クライアント */
void    image_idle_cleanup(struct rdd_config *, time_t now);

仕様

  • image_open_unit():

    • unit 範囲チェック (0 <= unit < client->nunits)。
    • u = &client->units[unit] を取得。
    • u->pathconfig_read() 時に image_root と結合済みのフルパス として保持されている前提。
    • u->fd >= 0 かつ u->map != NULL なら既にオープン済み。last_access を更新して成功。
    • そうでなければ open(2)fstat(2) → 必要なら mmap(2) を行う。
    • 既に open 済みなら何もしない(ただし、RDOPEN のたびに再オープンする仕様を採るなら、その時点でいったん close → open する)。
  • image_read():

    • まだ open されていなければ image_open_unit() を呼ぶ。
    • off / len 分を mmapped 区間から memcpy() でコピー。
    • 実際のファイルサイズより先を読もうとした場合は短い read として扱う。
    • 読み出し成功時には、そのユニットの last_accesstime(NULL) などの現在時刻で更新。
  • image_close_unit():

    • 対応 fd / mmap を閉じ、ユニット状態を「未オープン」に戻す。
  • image_close_all():

    • cfg->clients の全クライアント・全ユニットに対して image_close_unit() を呼ぶ。
    • 終了時や config_reload() 前に使用。
  • image_idle_cleanup():

    • 全クライアント・全ユニットを走査し、
    • now - last_access > cfg->idle_timeout のユニットを image_close_unit() で閉じる。
    • event_loop() から 1 秒〜数秒ごとに呼ばれる。
  • RDOPEN / RDCLOSE との関係:

  • RDOPEN が来たら、そのクライアントに対して 全ユニットについて image_close_unit(cfg, client, unit) を呼び、 「新しいブート」の開始とみなす。

  • RDCLOSE が来たら、そのクライアントに対して同様に 全ユニットの image_close_unit() を呼び、明示的にリソース解放。

  • すべてのクライアントのイメージを一括で閉じたい場合(終了時や設定再読込時)は、 image_close_all(cfg) を使う。

    • PROM が RDCLOSE を送らないケースに備えて、idle timeout でもクリーンアップされる。

2.7 interface.c

公開関数

int setup_interface(struct rdd_config *cfg);

仕様

  • cfg->ifname が NULL の場合:

    • システムのインターフェース一覧から、

      • ループバック以外で
      • UP かつ AF_INET アドレスを持つ
      • 最も番号の小さいインターフェース を選択(rbootd(8) と同様のポリシー)。
    • 見つからなければエラー。

  • cfg->ifname が指定されている場合:

    • そのインターフェースが存在し、UP で IPv4 アドレスを持つことを確認。
    • 失敗したらエラー。
  • 将来的に、ここでインターフェースの IPv4 アドレスや MAC アドレスも cfg にキャッシュしておくと、ログや SRQ 応答で使いやすい。


2.8 rpc.c

公開関数

int  rpc_setup_sockets(struct rdd_config *cfg);
void rpc_close_sockets(struct rdd_config *cfg);
int  rpc_srq_recv(struct rdd_config *cfg);
int  rpc_recv(struct rdd_config *cfg);

2.8.1 rpc_setup_sockets()

  • SRQ ソケット(UDP, port 3073)と RPC ソケット(UDP, port 3072)を作成。

    • bind() には cfg->ifname に対応するアドレスを用いる。
  • 生成した FD を cfg->srq_sock / cfg->rpc_sock に格納。

  • 失敗時は 0 未満を返し、main() 側でエラーとして扱う。

2.8.2 rpc_close_sockets()

  • cfg->srq_sock / cfg->rpc_sock が有効なら close() する。

2.8.3 rpc_srq_recv()

  • SRQ パケットを受信し、NWS 側に対して SRQ リプライ(ポート通知)を返す。
  • 実装の詳細は既存プロトタイプ newsrd_bootd.c と同じ動きとし、 この仕様書ではプロトコル詳細には踏み込まない。
  • エラー時も出来る限り継続し、致命的なエラーのみ -1 を返す。

2.8.4 rpc_recv()

  • RD RPC パケットを受信し、RPC ヘッダをパースして、prog=0x20000000, vers=1 のみ処理対象とする。

  • proc フィールドに応じて:

    • RPCRDOPENrd_handle_open()
    • RPCRDREADrd_handle_read()
    • RPCRDCLOSErd_handle_close()
  • client_lookup(cfg, src_mac, &src_addr) を行い、対応クライアントを取得:

    • 見つからなければ、適切なエラーコードの RPC REPLY を返すか、仕様に従って黙殺。
  • ハンドラの戻り値に応じて、RPC REPLY を組み立てて送信。


2.9 rpc_rdops.c

公開関数

int rd_handle_open(struct rdd_config *, struct rdd_client *,
    const struct rd_request_open *, struct rd_reply_open *);

int rd_handle_read(struct rdd_config *, struct rdd_client *,
    const struct rd_request_read *, struct rd_reply_read *);

int rd_handle_close(struct rdd_config *, struct rdd_client *,
    const struct rd_request_close *, struct rd_reply_close *);

struct rd_request_* / struct rd_reply_* は RD プロトコル構造体。newsrd_bootd.c で使っていた定義をそのまま流用。

仕様

  • rd_handle_open():

    • PROM の RDOPEN 要求に応答。
    • クライアントに対して、全ユニットについて image_close_unit(cfg, client, unit) を呼び、 「新しいセッション」の開始とみなす。
    • 必要なら unit=0 のイメージ存在チェックだけ先に行うが、実際の open/mmapimage_read() まで遅延してもよい。
    • 正常なら reply->status = RDSTAT_OK 的な値を設定し 0 を返す。
  • rd_handle_read():

    • unit / offset / size をチェック(範囲外など)。
    • image_read(cfg, client, unit, off, buf, len) を呼び、結果を reply に詰める。
    • 読み出しサイズ 0(EOF)も許容。
  • rd_handle_close():

    • 全ユニットに対して image_close_unit() を呼んでそのクライアントのイメージをすべて閉じる。
    • reply に OK をセットして返す。
  • いずれも、プロトコルとしての「status コード」がどう扱われるかは、既存の stub 実装と NEWS PROM の観測結果を元にすり合わせて実装する。


3. コールツリー(主なパス)

3.1 起動〜メインループ

main
 ├─ log_init
 ├─ getopt → rdd_cliopt 設定
 ├─ config_init
 ├─ config_read
 ├─ signal_init
 ├─ setup_interface
 ├─ rpc_setup_sockets
 ├─ [if !cfg.debug] daemonize
 ├─ event_loop
 │   ├─ [毎ループ] reload_pending / terminate_pending
 │   ├─ [毎ループ] image_idle_cleanup
 │   └─ [I/O 有り]
 │       ├─ rpc_srq_recv (SRQソケット ready)
 │       └─ rpc_recv    (RPCソケット ready)
 │           └─ rpc_handle_{open,read,close}
 │               ├─ client_lookup
 │               ├─ image_open_unit
 │               ├─ image_read
 │               └─ image_close_unit / image_close_all
 ├─ cleanup
 │   ├─ rpc_close_sockets
 │   ├─ image_close_all
 │   ├─ client_free_all
 │   └─ config_free
 └─ log_close

3.2 SIGHUP (設定再読込)

[SIGHUP受信]
 sigaction handler → reload_flag = 1

[event_loop 内]
 reload_pending() → true
  ├─ clear_reload()
  └─ config_reload(cfg)
      ├─ (新しい clients 配列を構築)
      ├─ パース成功 → 旧 clients + images を破棄し、新しい構造に差し替え
      └─ パース失敗 → ログだけ出して旧設定を保持

3.3 idle タイムアウトによるイメージクローズ

[event_loop]
 └─ image_idle_cleanup(cfg, now)
      └─ for all clients & units
          └─ if fd>=0 && last_access>0 && now - last_access > idle_timeout
                └─ image_close_unit(cfg, cl, unit)

4. エラー処理とログポリシ

  • 起動時の致命的エラー(設定ファイルが読めない、ソケットが開けない等):

    • log_msg(LOG_ERR, ...) を出して非 0 で終了。
  • 設定ファイルの個々の行の問題:

    • 行の形式が不正、hostname が空、image_list が空など:
      • LOG_WARNING を出し、その行は無視して次の行へ進む。
  • unknown client:

    • client_lookup() で MAC → hostname 逆引きに失敗した場合、あるいは hostname は得られたものの cfg->clients[] に該当がない場合:
      • 直近一定時間(例:60 秒)ログを出していなければ LOG_NOTICE を出す。
      • unknown_log_mac[] / unknown_log_time[] のキャッシュに記録して 同一 MAC からのスパムログを防止。
  • RPC 処理中のエラー:

    • できる限り「RPC のエラーコード」でクライアントに返す。
    • どうにもならない場合のみ致命的ログを出してサーバ自体を終了させる。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment