fcmc 0.8.0

A CLI tool to check whether CTF challenge meta.toml configs are valid, and optionally test Docker setup
Documentation
mod challenge;
mod gamebox;
use std::collections::HashMap;

use anyhow::Result;
use bollard::{
    Docker,
    network::CreateNetworkOptions,
    query_parameters::{RemoveContainerOptions, StopContainerOptions},
    secret::Ipam,
};
pub use challenge::{ChallengeMeta, DockerMeta, FlagMeta};
pub use gamebox::{GameBoxConfig, GameBoxMeta};

pub async fn stop_and_remove(docker: &bollard::Docker, identifier: &str) -> Result<()> {
    // 1. 停止容器
    // 在 AWD 中,我们通常追求速度,t: Some(0) 表示立即发送信号,不等待优雅退出时间
    let stop_options = StopContainerOptions {
        t: 0.into(), // 注意:最新版 bollard 这里可能是 i64 或 u64,视版本而定
        ..Default::default()
    };

    let container_name = identifier;

    // 尝试停止容器,如果容器已经停止,Docker API 会返回 304,
    // 我们在这里捕获错误,防止程序因为容器已停而中断
    if let Err(e) = docker
        .stop_container(container_name, Some(stop_options))
        .await
    {
        // 如果是 404 (不存在) 或 304 (已停止),我们选择忽略它,继续执行删除
        println!(
            "⚠️ 停止容器 {} 时提示: {} (可能是已经停止了)",
            identifier, e
        );
    }

    // 2. 彻底删除容器
    let remove_options = RemoveContainerOptions {
        v: true,     // 关键:删除关联的匿名卷,防止磁盘空间被大量僵尸卷撑爆
        force: true, // 强制删除,即使停止失败也能强行抹除
        link: false,
    };
    // 执行删除
    match docker
        .remove_container(container_name, Some(remove_options))
        .await
    {
        Ok(_) => println!("🗑️ 容器 {} 已彻底清理", identifier),
        Err(e) => {
            eprintln!("❌ 删除容器 {} 时出错: {}", identifier, e);
        }
    }

    Ok(())
}

pub async fn remove_and_create_bridge_net(
    docker: &Docker,
    bridge_name: String,
    cidr: String,
) -> anyhow::Result<()> {
    // docker network create --driver bridge --subnet $TARGET_SUBNET/$TARGET_PREFIX $DOCKER_NET
    let _ = docker.remove_network(&bridge_name).await;

    let mut network_options = HashMap::new();
    // 关键:这行代码决定了你在 ifconfig 或 ip addr 里看到的网卡名
    network_options.insert(
        "com.docker.network.bridge.name".to_string(),
        bridge_name.clone(),
    );
    #[allow(deprecated)] // 这个版本的bollard还没有更新到最新的api
    let conf = CreateNetworkOptions {
        name: bridge_name,
        driver: "bridge".to_string(),
        internal: true,
        check_duplicate: true,
        ipam: Ipam {
            config: Some(vec![bollard::secret::IpamConfig {
                subnet: cidr.into(),
                ..Default::default()
            }]),
            ..Default::default()
        },
        options: network_options,
        ..Default::default()
    };

    docker.create_network(conf).await?;

    Ok(())
}