bitcoin/src/bitcoind.cpp

# bitcoin/src/bitcoind.cppの役割
bitcoind.cpp はBitcoin Coreのデーモン（バックグラウンドで動作するプログラム）であるbitcoindの起動と初期化を管理するためのものです。プログラムは主に以下のステップで構成されています：

- 必要なヘッダファイルのインクルード
- グローバル変数や定数の定義
- 初期化や設定のための関数
- メイン関数

# src/init/bitcoind.cppとの違い
ビットコインのビルドシステムでは、src/bitcoind.cppがエントリーポイントとして使用され、src/init/bitcoind.cppはその中で必要な初期化機能を提供する役割を果たしています。

つまり、プログラムが実行されるときは、まずsrc/bitcoind.cppのメイン関数が呼び出され、そこから必要に応じてsrc/init/bitcoind.cppの関数が使用されます。

# ヘッダファイルのインクルード
プログラムの先頭部分では、多くのヘッダファイルがインクルードされています。これらは、プログラムで使用する関数やクラスの定義を含むファイルです。

```c++
#include <config/bitcoin-config.h>
#include <chainparams.h>
#include <clientversion.h>
// その他多数のヘッダファイル
```

# グローバル変数や定数の定義
翻訳関数のポインタを定義していますが、ここでは未設定（nullptr）です。

```c++
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
```

# fork_daemon 関数
この関数は、プログラムをデーモン（バックグラウンドプロセス）として実行するためのものです。

```c++
int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd& endpoint)
{
    // 子プロセスと親プロセス間の通信のためのパイプを作成
    std::optional<TokenPipe> umbilical = TokenPipe::Make();
    if (!umbilical) {
        return -1; // パイプ作成失敗
    }

    // フォークの実行
    int pid = fork(); // プロセスを分岐
    if (pid < 0) {
        return -1; // fork失敗
    }
    if (pid != 0) {
        // 親プロセスの処理
        endpoint = umbilical->TakeReadEnd();
        umbilical->TakeWriteEnd().Close();

        int status = endpoint.TokenRead();
        if (status != 0) {
            endpoint.Close();
            return -1;
        }

        return pid;
    }

    // 子プロセスの処理
    endpoint = umbilical->TakeWriteEnd();
    umbilical->TakeReadEnd().Close();

    // セッションリーダーの設定
    if (setsid() < 0) {
        exit(1); // setsid失敗
    }

    // カレントディレクトリの変更
    if (!nochdir) {
        if (chdir("/") != 0) {
            exit(1); // chdir失敗
        }
    }

    // 標準入出力のリダイレクト
    if (!noclose) {
        int fd = open("/dev/null", O_RDWR);
        if (fd >= 0) {
            bool err = dup2(fd, STDIN_FILENO) < 0 || dup2(fd, STDOUT_FILENO) < 0 || dup2(fd, STDERR_FILENO) < 0;
            if (fd > 2) close(fd);
            if (err) {
                exit(1); // dup2失敗
            }
        } else {
            exit(1); // open失敗
        }
    }
    endpoint.TokenWrite(0); // 成功を示すトークンを送信
    return 0;
}
```

## デーモン化の理由

1. バックグラウンド実行: サービスやサーバープロセスは、ユーザーがログインしているターミナルセッションから独立して動作する必要があります
2. 端末からの切り離し: デーモンプロセスは標準入力、標準出力、標準エラー出力を端末から切り離し、/dev/nullにリダイレクトします。これにより、ログアウトや端末の閉鎖がプロセスに影響を与えません
3. 親プロセスの終了: デーモンプロセスは通常、親プロセスを終了させることで孤立し、システムの管理下で再起動や停止が管理されます

# ParseArgs 関数
ParseArgs関数は、ビットコインノードの起動時にコマンドライン引数を解析し、不正な引数やエラーがあれば適切なエラーメッセージを表示して初期化を中止する役割を持ちます。この関数は、引数の解析と初期設定を行う重要な部分です。

```c++

// 関数の定義
static bool ParseArgs(ArgsManager& args, int argc, char* argv[])
{
    SetupServerArgs(args);
    // 引数の解析とエラーチェック
    std::string error;
    if (!args.ParseParameters(argc, argv, error)) {
        return InitError(Untranslated(strprintf("Error parsing command line arguments: %s", error)));
    }
    // 設定ファイルの初期化
    if (auto error = common::InitConfig(args)) {
        return InitError(error->message, error->details);
    }
    // 不正な引数のチェック
    for (int i = 1; i < argc; i++) {
        if (!IsSwitchChar(argv[i][0])) { // 各引数がスイッチキャラクタ（通常はハイフン-）で始まっているかを確認
            return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see bitcoind -h for a list of options.", argv[i])));
        }
    }
    // 正常終了
    return true;
}
```

# ProcessInitCommands 関数
ProcessInitCommands関数は、ビットコインデーモン (bitcoind) の起動時に、ヘルプやバージョン情報の表示を処理するための関数です。

コマンドライン引数に-helpや-versionが含まれている場合に、それに応じた情報を出力します。この関数は、ビットコインノードの初期化プロセス中に呼び出され、必要に応じて初期化を中断し、情報を表示してプログラムを終了させる役割を果たします。

```c++
// 関数の定義
static bool ProcessInitCommands(ArgsManager& args)
{
    // ヘルプやバージョン情報のリクエストをチェック
    if (HelpRequested(args) || args.IsArgSet("-version")) {
        // バージョン情報の出力準備
        std::string strUsage = PACKAGE_NAME " version " + FormatFullVersion() + "\n";

        // バージョン情報がリクエストされた場合の処理
        if (args.IsArgSet("-version")) {
            strUsage += FormatParagraph(LicenseInfo());
        // ヘルプがリクエストされた場合の処理
        } else {
            strUsage += "\nUsage:  bitcoind [options]                     Start " PACKAGE_NAME "\n";
            strUsage += args.GetHelpMessage();
        }

        // 情報の出力と関数の終了
        tfm::format(std::cout, "%s", strUsage);
        return true;
    }

    return false;
}
```

# AppInit 関数
この関数は、アプリケーションの初期化を行います。

```c++
// 関数の定義と初期化
static bool AppInit(NodeContext& node)
{
    bool fRet = false; // 初期化結果を格納するフラグ
    ArgsManager& args = *Assert(node.args);

    #if HAVE_DECL_FORK
    TokenPipeEnd daemon_ep; // デーモンプロセスとの通信に使用するパイプエンドポイント
    #endif

    std::any context{&node}; // ノードコンテキストを格納する汎用コンテナ
    // 初期化の試行
    try
    {
        args.SoftSetBoolArg("-server", true);
        InitLogging(args);
        InitParameterInteraction(args);

        // 基本設定とパラメータの相互作用の初期化
        if (!AppInitBasicSetup(args, node.exit_status)) {
            return false;
        }
        if (!AppInitParameterInteraction(args)) {
            return false;
        }

        // サニティチェックとコンテキストの初期化
        node.warnings = std::make_unique<node::Warnings>();
        node.kernel = std::make_unique<kernel::Context>();
        node.ecc_context = std::make_unique<ECC_Context>();

        // サニティチェックを行い、必要な条件が満たされているかを確認
        if (!AppInitSanityChecks(*node.kernel)) {
            return false;
        }

        // デーモン化の処理
        if (args.GetBoolArg("-daemon", DEFAULT_DAEMON) || args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
            #if HAVE_DECL_FORK
            tfm::format(std::cout, PACKAGE_NAME " starting\n");

            switch (fork_daemon(1, 0, daemon_ep)) {
                case 0:
                    if (!args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
                        daemon_ep.TokenWrite(1);
                        daemon_ep.Close();
                    }
                    break;
                case -1:
                    return InitError(Untranslated(strprintf("fork_daemon() failed: %s", SysErrorString(errno))));
                default: {
                    int token = daemon_ep.TokenRead();
                    if (token) {
                        exit(EXIT_SUCCESS);
                    } else {
                        tfm::format(std::cerr, "Error during initialization - check debug.log for details\n");
                        exit(EXIT_FAILURE);
                    }
                }
            }
            #else
            return InitError(Untranslated("-daemon is not supported on this operating system"));
            #endif
        }

        // データディレクトリのロックとインターフェースの初期化
        if (!AppInitLockDataDirectory()) {
            return false;
        }
        fRet = AppInitInterfaces(node) && AppInitMain(node);
    }
    // 初期化中に発生した例外をキャッチし、ログに記録
    catch (const std::exception& e) {
        PrintExceptionContinue(&e, "AppInit()");
    } catch (...) {
        PrintExceptionContinue(nullptr, "AppInit()");
    }

    // デーモンプロセスの終了処理
    #if HAVE_DECL_FORK
    if (daemon_ep.IsOpen()) {
        daemon_ep.TokenWrite(fRet); // 初期化結果を親プロセスに通知
        daemon_ep.Close();
    }
    #endif
    return fRet;
}
```

- 初期化フラグと引数の設定: 初期化フラグやコマンドライン引数の設定を行います
- ログとパラメータの初期化: ログの初期化と引数の相互作用の初期化を行います
- 基本設定とサニティチェック: 基本設定とサニティチェックを行います
- デーモン化: -daemonオプションが指定されている場合、プロセスをデーモン化します
- データディレクトリのロック: データディレクトリをロックし、他のプロセスからのアクセスを防ぎます
- インターフェースとメインプロセスの初期化: インターフェースとメインプロセスの初期化を行います
- 例外処理: 初期化中に発生する例外を処理します


# メイン関数 (MAIN_FUNCTION)
この関数は、プログラムのエントリーポイントです。全体の流れを管理します。
```c++
MAIN_FUNCTION
{   
    //Windows用のプリプロセッサディレクティブ
    #ifdef WIN32 
    common::WinCmdLineArgs winArgs; //Windows特有のコマンドライン引数の処理を行うためのオブジェクトを作成
    std::tie(argc, argv) = winArgs.get(); //コマンドライン引数を取得し、argcとargvに設定
    #endif

    //コンテキストオブジェクト
    NodeContext node;
    int exit_status; //プログラムの終了ステータスを格納する変数を宣言
    std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node, argc, argv, exit_status); //スマートポインタで初期化インターフェースを作成
    if (!init) {
        return exit_status;
    }

    //環境設定のセットアップ
    SetupEnvironment();
    noui_connect(); //ノンUIモードでシグナルハンドラを接続し、プログラムがシステムからのシグナル（例えば、終了シグナル）に適切に応答できるようにする
    util::ThreadSetInternalName("init"); //スレッド名の設定

    ArgsManager& args = *Assert(node.args); //コマンドライン引数を管理

    //初期化コマンドの処理
    if (!ParseArgs(args, argc, argv)) return EXIT_FAILURE; 
    if (ProcessInitCommands(args)) return EXIT_SUCCESS; 

    //アプリケーションの初期化と実行
    if (!AppInit(node) || !Assert(node.shutdown)->wait()) {
        node.exit_status = EXIT_FAILURE;
    }

    //中断とシャットダウン
    Interrupt(node); 
    Shutdown(node);

    //プログラムの終了
    return node.exit_status;
}
```


# まとめ
- ヘッダファイルのインクルード: 必要な機能やクラスを使用するためのファイルを読み込む
- グローバル変数や定数の定義: プログラム全体で使用する変数や定数を定義する
- fork_daemon 関数: プロセスをデーモン化するための関数
- ParseArgs 関数: コマンドライン引数を解析する関数
- ProcessInitCommands 関数: ヘルプやバージョン情報の表示を処理する関数
- AppInit 関数: アプリケーションの初期化を行う関数
- メイン関数: プログラムのエントリーポイント。全体の流れを管理する

この流れでプログラムが初期化され、実行されます。デーモン化、引数の解析、ログの初期化、サニティチェックなどが行われ、最終的にビットコインノードが起動します。