时间过得真快呢,仿佛上一次更新就在昨天。定眼一瞧才发现这转眼间又到了月底。
中秋节的时候把家里的路由器换了,解决的最大问题就是原来的路由器内网设备不能互通的毛病,于是床底下的服务器又终于可以开起来了。但是由于在床底下,所以晚上不关的话会很吵(双路吹震天了解一下),故每晚都得关上才能睡觉。而第二天要开就得跑到床底下按开关,很是麻烦。后来买了华硕的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(¶ms)
.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 处获得。