MENU

【代码札记】命令行控制ASMB8-ikvm远程管理卡开启服务器

September 21, 2019 • 瞎折腾

时间过得真快呢,仿佛上一次更新就在昨天。定眼一瞧才发现这转眼间又到了月底。

中秋节的时候把家里的路由器换了,解决的最大问题就是原来的路由器内网设备不能互通的毛病,于是床底下的服务器又终于可以开起来了。但是由于在床底下,所以晚上不关的话会很吵(双路吹震天了解一下),故每晚都得关上才能睡觉。而第二天要开就得跑到床底下按开关,很是麻烦。后来买了华硕的ASMB8-ikvm远程管理卡,它可以通过网页管理服务器,也能捕获板载显卡的输出,远程设定bios、看开机画面都没有问题的。但是插上别的显卡之后板载显卡就会自己关闭,导致管理卡捕获不到视频信号,而Bios中也没看到相关的设置,我怀疑是华硕和英伟达商量好了一起让我买丽台卡。

ASMB8-ikvm的网页帮了很大的忙,但是只能使用网页来操作,我平时在大学宿舍就很不方便。如果利用Frp做一个反向代理,则需要直接把管理网页暴露在公网上,我不放心。于是就找了个树莓派24小时运行,把树莓派的SSH暴露在公网上,通过SSH访问内网的管理卡来操作。但是SSH隧道使用起来非常麻烦,于是我就抓了抓包,准备写个命令行工具帮我开。这次没有使用Java,因为树莓派2B+的性能有限,虽然Java现在优化的已经不错了,但是还是感觉臃肿。这次决定使用最近在学的Rust,也是作为一个尝试和实践吧。

代码如下:

use reqwest::Client;
use reqwest::header::{HeaderMap, HeaderValue};

fn main() {
    let client = Client::builder()
        .cookie_store(true)
        .build()
        .unwrap();

    let auth = do_login(&client, "用户名", "密码");
    let status = is_power_up(&client, &auth);
    if status {
        println!("Server already booted.");
    } else {
        power_up(&client, &auth);
    }
}

struct Auth {
    session: String,
    csrf: String
}

fn do_login(client: &Client, user: &str, passwd: &str) -> Auth {
    let params = [("WEBVAR_USERNAME", user), ("WEBVAR_PASSWORD", passwd)];
    let mut res = client.post("http://192.168.1.81/rpc/WEBSES/create.asp")
        .form(&params)
        .send().unwrap_or_else(|_| {
            panic!("Cannot do login.");
        });

    let body = res.text().unwrap();
    let raws: Vec<&str> = body.split("'").collect();

    Auth {
        session: String::from(raws[3]),
        csrf:  String::from(raws[11])
    }
}

fn is_power_up(client: &Client, auth: &Auth) -> bool {
    let mut headers = HeaderMap::new();
    let mut cookie = String::from("SessionCookie=");
    cookie.push_str(&auth.session);
    headers.append("Cookie", HeaderValue::from_str(cookie.as_str()).unwrap());

    let mut res = client.get("http://192.168.1.81/rpc/hoststatus.asp")
        .header("CSRFTOKEN", &auth.csrf)
        .headers(headers)
        .send().unwrap();

    if res.status().is_success() {
        res.text().unwrap().contains("{ 'JF_STATE' : 1 }")
    } else {
        true
    }
}

fn power_up(client: &Client, auth: &Auth) {
    let mut headers = HeaderMap::new();
    let mut cookie = String::from("SessionCookie=");
    cookie.push_str(&auth.session);
    headers.append("Cookie", HeaderValue::from_str(cookie.as_str()).unwrap());

    let mut res = client.post("http://192.168.1.81/rpc/hostctl.asp")
        .body("WEBVAR_POWER_CMD=1")
        .header("CSRFTOKEN", &auth.csrf)
        .headers(headers)
        .send().unwrap();

    if res.status().is_success() {
        if is_power_up(client, auth) {
            println!("Server should be started.");
        } else {
            eprintln!("ASMB8-ikvm respond success, but server didn't powering up.");
        }
    } else {
        eprintln!("Cannot power up server: HTTP status code {}, respond: {}", res.status().as_u16(), res.text().unwrap());
    }
}

依赖

reqwest = "0.9.20"

大概的流程就是先用给定的用户名密码登陆,获取到一个session和一个CSRF token。再获取当前的电源状态,如果已经开机了就不再操作了,关机状态下才执行开机命令。其中需要注意的一个坑就是如果电源已经开了,再发送开机请求会导致管理卡进入类似崩溃的状态,网页、SSH什么的都不再响应了,必须关掉电源再上电才能恢复。

代码写完了测试也没毛病了。可问题是编译咋办呢。以前老写Java,写好了打个Jar放哪都能跑,但是Rust是直接出二进制的。Windows下想要出能在Arm上运行的二进制,就得要交叉编译。但是Windows下对Rust的交叉编译好像确实有点费劲,我是没弄出来。所以直接把代码拷贝到树莓派上,然后在树莓派上安装Rust进行编译。耗时40多分钟。编译结果如下:

谷歌硬盘

实际使用的话,其实感觉还挺方面。毕竟自己写的代码嘛,好(手动滑稽

-End-


知识共享许可协议
【代码札记】命令行控制ASMB8-ikvm远程管理卡开启服务器天空 Blond 采用 知识共享 署名 - 非商业性使用 - 相同方式共享 4.0 国际 许可协议进行许可。
本许可协议授权之外的使用权限可以从 https://skyblond.info/about.html 处获得。

Last Modified: January 12, 2021
Archives QR Code
QR Code for this page
Tipping QR Code