-
単一プロセス・単一スレッドのイベントループ型デーモン。
-
コマンドラインオプション →
struct rdd_clioptに詰める。 -
struct rdd_configは「現在有効な設定 + クライアント状態」をまとめて持つ。 -
シグナルハンドラは
volatile sig_atomic_tフラグを立てるだけ。- 実際の設定リロード、終了処理はイベントループ側で行う。
-
ネットワークブートは頻度が低い前提なので、効率よりも実装の素直さ・可読性優先。
/* コマンドラインオプション */
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)側から行う。
コマンドラインオプションを一時的に保持する構造体。 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_rootはconfig_init()内でstrdup()/estrdup()してrdd_configにコピーされる。debug_modeはcfg->debugに反映(デーモン化を抑制)。debug_levelはcfg->debug_levelにコピー。
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_accessは image.c が管理。- OPEN 時に
fd/map/map_len/last_accessを設定。 - READ 時に
last_accessを更新。 - CLOSE または idle cleanup で
munmap()/close()/fd=-1/map=NULL/map_len=0。
設定済みクライアント 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を比較してクライアントを特定する。 -
unitsはnunits長の配列。各要素がimage_unit。- 設定ファイル:
diskunit0.img,diskunit1.img→nunits=2,units[0].path=".../diskunit0.img",units[1].path=".../diskunit1.img"など(実際にはimage_rootと結合したパスを保持)。
- 設定ファイル:
サーバ全体の設定とランタイム状態。
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を絶対パス化して保存。
以降はファイルごとの関数と、その入出力・責務を定義します。
- エントリポイント。
- コマンドラインオプション解析。
rdd_cliopt→rdd_config初期化。- ログ/シグナル/インタフェース/ソケット/設定ファイルの初期化。
- デーモン化。
- イベントループ。
- 終了時 cleanup。
-
入力:
progname:表示用プログラム名 (通常argv[0])。
-
処理:
fprintf(stderr, ...)で オプション一覧と簡潔な説明を出す。
-
出力:
- 返り値なし。呼び出し側が
exit(EXIT_FAILURE)等を行う。
- 返り値なし。呼び出し側が
オプション仕様:
-i interface: 使用インタフェース名(省略時は自動選択)-c config: 設定ファイルパス(省略時/etc/remotediskd.conf)-s dir: イメージファイルのルートディレクトリ(省略時/tftpboot)-d: デバッグモード(フォアグラウンド / ログ詳細度も 1 増加)-v: ログ詳細度を 1 段階上げる-h/?: usage 表示
処理フロー(確定版):
-
log_init(argv[0]); -
struct rdd_cliopt opt; memset(&opt, 0, sizeof(opt)); -
getopt()でオプションを解析し、すべて opt に格納。-i→opt.ifname-c→opt.conf_path-s→opt.image_root-d→opt.debug_mode = 1; opt.debug_level++;-v→opt.debug_level++;-h/?or unknown →usage()を呼んで終了。
-
struct rdd_config cfg; -
config_init(&cfg, &opt)を呼ぶ。 -
signal_init()を呼ぶ。- 失敗したらエラーで終了。
-
config_read(&cfg)を呼ぶ。/etc/remotediskd.conf読み込み。- エラーならログを出して終了。
-
setup_interface(&cfg)。cfg.ifnameが NULL なら自動選択ルールに従う。- IPv4 アドレスを
cfg.if_addrに格納。
-
rpc_setup_sockets(&cfg)。cfg.srq_sock/cfg.rpc_sockを作成しバインド。
-
if (!cfg.debug) daemonize();- デバッグモードでなければバックグラウンド化。
- デーモン化に失敗したらエラーログ出して終了。
-
event_loop(&cfg)を呼ぶ。 -
戻り値に応じて exit code を決定(正常終了かエラー終了)。
-
cleanup(&cfg); -
log_close(); -
return。
-
入力:
cfg: サーバ状態。
-
処理:
-
for (;;)ループ。 -
fd_setにcfg->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: 致命的エラーで終了(主要なエラーはログ出力済み)。
- ソケットを閉じる:
rpc_close_sockets(cfg); - 全イメージをクローズ:
image_close_all(cfg); - クライアント配列解放:
client_free_all(cfg); - コンフィグ周りのメモリ開放:
config_free(cfg);
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.debugやopt.debug_mode)に応じて、stderr にも出すかどうかは実装側で決める。
-
log_close():- syslog 使用時は
closelog()。
- syslog 使用時は
-
log_msg():- syslog or stderr に共通メッセージ出力。
-
log_debug(level, ...):level <= cfg->debug_levelのときのみメッセージ出力。cfg->debug_levelはstruct rdd_config内の値を参照する(log.c内でextern struct rdd_config *もしくは setter 関数などで接続する想定)。
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
- SIGHUP →
-
reload_pending()/terminate_pending():- それぞれのフラグを読み出して返す(クリアはしない)。
-
clear_reload()/clear_terminate():- フラグを 0 にする。
-
いずれの関数もシグナルマスクを変更しない。
-
SIGHUP が連続して来た場合、
reload_pending()/clear_reload()の組み合わせで、最低でも 1 回は確実にリロードされる動きとする。
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);-
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.confimage_root:/tftpbootifname: NULL(後でsetup_interface()が決定)debug_level: 0
-
-
debugはco->debug_modeが真なら 1、それ以外は 0。 -
idle_timeoutは定数デフォルト(例: 300 秒)。 -
unknown_log_time[]/unknown_log_mac[][]をゼロ初期化。
-
処理:
fopen(cfg->config_path, "r")。失敗時はエラーをログして-1。- 行バッファ
char line[1024];を用いてfgets()で 1 行ずつ読む。 - 行ごとに
parse_config_line(cfg, line, lineno)を呼ぶ。 - 最後に
fclose()して 0 を返す(致命的エラーがあれば途中で-1)。
-
行が長すぎる(末尾に改行無し)の場合:
- 残りを読み捨てた上で、その行は警告して無視。
(static 関数として config.c 内部に定義)
-
行末の
\n/\rを削る。 -
行頭の空白(space / tab)を飛ばす。
-
空行 or 行頭
#はコメントとして無視。 -
hostname トークン抽出:
- 空白・タブ・
#までを hostname とする。 - hostname が空なら警告して無視。
- 空白・タブ・
-
hostname の後の空白を飛ばす。
-
2 列目(image_list)の生文字列を取り出す:
#をコメントの開始とするが、\#でエスケープされた#は有効文字とする。img_start〜 末尾までの文字列から、末尾空白をトリム。- 空なら「empty image list」として警告し、その行を無視。
-
parse_image_list(img_start, &paths, &nunits, cfg->config_path, lineno)を呼び、カンマ区切り+\エスケープ付き文字列を unit ごとのパス配列に変換。- 成功時:
pathsはchar **(estrdup()済み)、nunits > 0。 - 失敗時: その行を無視。
- 成功時:
-
新しい
struct rdd_clientを確保し、以下を設定:client->hostname = estrdup(hostname);client->nunits = nunits;client->unitsをcalloc(nunits, sizeof(struct rdd_image_unit))で確保し、parse_image_list()で得たpaths[i]について、- 先頭が
/ならそのまま絶対パスとみなしてunits[i].pathにestrdup() - そうでなければ
cfg->image_root+ "/" +paths[i]を連結して 絶対パス文字列を作り、units[i].pathにestrdup()
- 先頭が
- ランタイム状態フィールド(fd/map/last_access 等)は「未オープン」状態で初期化。
※ ここでは /etc/ethers の ether_hostton() / ether_ntohost() による MAC 解決は行わない。
MAC → hostname の解決は client_lookup() 側で実施する。
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
-
エスケープ:
-
\X→Xをそのままトークンに追加- 行末の単独
\はエラー。
- 行末の単独
-
-
-
トークンが完成するたびに
estrdup(token)してpaths[]に追加。 -
トークン長上限は
PATH_MAX相当のバッファ長で管理し、超えたら行ごと無視して警告。 -
有効なトークンが 1 つもなければエラー(行無視)。
-
実装方針:
- 新しい
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()が呼ばれるだけ。
- (クライアントやイメージの解放は
cleanup()側で済ませている前提) free(cfg->config_path),free(cfg->image_root),free(cfg->ifname)など、config_init()でstrdup()した文字列を解放。cfgのポインタフィールドを NULL に戻す(安全のため)。
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():-
macから/etc/ethersを使ってホスト名に逆引き:ether_ntohost()などを使用し、hostname_from_ethersを得る。
-
逆引きに失敗した場合:
cfg->unknown_log_mac[]/cfg->unknown_log_time[]を参照。- 一定時間(例: 10 秒)以内に同じ MAC からの要求が続く場合はログを抑制。
- 「unknown client (MAC=xx:xx:xx:xx:xx:xx) from src」が一定間隔でだけログに出る想定。
- NULL を返す。
-
成功した場合:
- 得られた
hostname_from_ethersとcfg->clients[i].hostnameを比較。 - 一致するクライアント構造体を返す。
- 見つからなければ unknown と同様にログを出す(ただしメッセージを変える)。
- 得られた
-
-
client_free_all():-
cfg->clients配列をなめて、client->hostnameのfree()、- 各ユニット
units[i].pathのfree()、 units配列本体のfree()。
-
最後に
free(cfg->clients); cfg->clients = NULL; cfg->nclients = 0;。
-
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->pathは config_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_accessをtime(NULL)などの現在時刻で更新。
- まだ open されていなければ
-
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 でもクリーンアップされる。
int setup_interface(struct rdd_config *cfg);-
cfg->ifnameが NULL の場合:-
システムのインターフェース一覧から、
- ループバック以外で
- UP かつ
AF_INETアドレスを持つ - 最も番号の小さいインターフェース
を選択(
rbootd(8)と同様のポリシー)。
-
見つからなければエラー。
-
-
cfg->ifnameが指定されている場合:- そのインターフェースが存在し、UP で IPv4 アドレスを持つことを確認。
- 失敗したらエラー。
-
将来的に、ここでインターフェースの IPv4 アドレスや MAC アドレスも
cfgにキャッシュしておくと、ログや SRQ 応答で使いやすい。
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);-
SRQ ソケット(UDP, port 3073)と RPC ソケット(UDP, port 3072)を作成。
bind()にはcfg->ifnameに対応するアドレスを用いる。
-
生成した FD を
cfg->srq_sock/cfg->rpc_sockに格納。 -
失敗時は 0 未満を返し、
main()側でエラーとして扱う。
cfg->srq_sock/cfg->rpc_sockが有効ならclose()する。
- SRQ パケットを受信し、NWS 側に対して SRQ リプライ(ポート通知)を返す。
- 実装の詳細は既存プロトタイプ
newsrd_bootd.cと同じ動きとし、 この仕様書ではプロトコル詳細には踏み込まない。 - エラー時も出来る限り継続し、致命的なエラーのみ
-1を返す。
-
RD RPC パケットを受信し、RPC ヘッダをパースして、
prog=0x20000000, vers=1のみ処理対象とする。 -
procフィールドに応じて:RPCRDOPEN→rd_handle_open()RPCRDREAD→rd_handle_read()RPCRDCLOSE→rd_handle_close()
-
client_lookup(cfg, src_mac, &src_addr)を行い、対応クライアントを取得:- 見つからなければ、適切なエラーコードの RPC REPLY を返すか、仕様に従って黙殺。
-
ハンドラの戻り値に応じて、RPC REPLY を組み立てて送信。
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/mmapはimage_read()まで遅延してもよい。 - 正常なら
reply->status = RDSTAT_OK的な値を設定し 0 を返す。
- PROM の
-
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 の観測結果を元にすり合わせて実装する。
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
[SIGHUP受信]
sigaction handler → reload_flag = 1
[event_loop 内]
reload_pending() → true
├─ clear_reload()
└─ config_reload(cfg)
├─ (新しい clients 配列を構築)
├─ パース成功 → 旧 clients + images を破棄し、新しい構造に差し替え
└─ パース失敗 → ログだけ出して旧設定を保持
[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)
-
起動時の致命的エラー(設定ファイルが読めない、ソケットが開けない等):
log_msg(LOG_ERR, ...)を出して非 0 で終了。
-
設定ファイルの個々の行の問題:
- 行の形式が不正、hostname が空、image_list が空など:
LOG_WARNINGを出し、その行は無視して次の行へ進む。
- 行の形式が不正、hostname が空、image_list が空など:
-
unknown client:
client_lookup()で MAC → hostname 逆引きに失敗した場合、あるいは hostname は得られたもののcfg->clients[]に該当がない場合:- 直近一定時間(例:60 秒)ログを出していなければ
LOG_NOTICEを出す。 unknown_log_mac[]/unknown_log_time[]のキャッシュに記録して 同一 MAC からのスパムログを防止。
- 直近一定時間(例:60 秒)ログを出していなければ
-
RPC 処理中のエラー:
- できる限り「RPC のエラーコード」でクライアントに返す。
- どうにもならない場合のみ致命的ログを出してサーバ自体を終了させる。