Files
micro_servers/lci/extra/phs/src/main.cj
T
2026-08-02 20:15:07 +08:00

212 lines
6.0 KiB
Plaintext

package phs
import std.io.*;
import std.fs.*;
import std.collection.*;
import std.sync.*;
import std.process.*;
import stdx.net.http.*;
import stdx.encoding.json.*;
const ADDR = "127.0.0.1" /* 监听IP地址 */
const PORT = 5200u16 /* 端口 UInt16类型 */
class Ps {
private let ps = LinkedList<SubProcess>()
private let mtx = Mutex()
/* 创建进程 */
public func create(arr: Array<String>) {
let path = arr[0]
let args = arr[1..]
let subp: SubProcess = launch(path, args,
//stdIn: ProcessRedirect.Discard, /* WINDOWS中不支持输入重定向 */
//stdOut: ProcessRedirect.Discard, /* 加了这行之后就在控制台看不到输出了 */
//stdErr: ProcessRedirect.Discard
)
this.clear() /* 清理进程 */
synchronized(mtx) {
ps.addLast(subp)
return;
}
}
/* 返回进程列表 */
public func list() {
var msg = ""
println('1')
this.clear() /* 清理进程,有锁 */
println('2')
synchronized(mtx) {
println('3')
for (p in ps) {
if (msg != "") { msg += "\n"; }
msg += '${p.pid.toString()}\t'
msg += '${p.command} '
//for (i in p.arguments) { /* 加上这个会失败 */
// msg += '${i} '
//}
/* 启动时间 */
msg += '\t${p.userTime.toString()}'
}
println('4')
}
return msg;
}
/* 杀死已经存在的进程 */
public func kill(pid: Int64) {
this.clear() /* 清理 */
synchronized(mtx) {
for (p in ps) {
if (p.pid == pid) {
p.terminate(force: true)
return
}
}
}
}
/*
* 进程自启动
*
* FIXME: 支持注释
*/
public func autoRun(csvpath: String) {
let file = File(csvpath, Read)
let bytes = readToEnd(file)
let ctt = String.fromUtf8(bytes)
let cmds = ctt.split("\n", removeEmpty: true)
for (cmd in cmds) {
try{
let arr = cmd.split(" ", removeEmpty: true)
this.create(arr)
} catch (exp: Exception) {
;
}
}
file.close()
}
/* 清理已经死去的进程,默认不加锁 */
private func clear() {
synchronized(mtx) {
let len = ps.size
for (i in (len-1)..=0 : -1) {
try {
let p = ps.nodeAt(i).getOrThrow()
println("start...")
p.value.wait(timeout: 1 * Duration.millisecond) /* 进程有没有退出 */
println("end...")
ps.remove(p)
}
catch (exp: TimeoutException) { /* 如果进程没有结束,会运行到这里 */
;
println("skip...")
}
}
}
}
}
/*
* 将JSON数组转为字符串的数组
*
* Json对象的每个元素只能是字符串,否则会报错
*/
func Jarr_to_arr(arr: JsonArray): Array<String> {
let len = arr.size() // Json数组的长度
let ret = Array<String>(len, repeat: "")
for (i in 0..len) {
let option: Option<JsonValue> = arr.get(i)
let jitem: JsonValue = option.getOrThrow()
let jstr: JsonString = jitem.asString()
let str: String = jstr.getValue()
ret[i] = str
}
return ret
}
/* cj命令行参数不包含可执行文件名 */
main(_: Array<String>): Int64 {
/* 进程组 */
let ps = Ps()
ps.autoRun("./init.csv")
/* 创建http服务器 */
let server: Server = ServerBuilder()
.addr(ADDR) /* 设置地址 */
.port(PORT) /* 设置端口 */
.build()
/* 创建进程的HTTP方法 */
server.distributor.register("/api/v2/create", { context =>
if (context.request.method == "POST") {
let content = StringReader(context.request.body).readToEnd() /* 阅读请求体 */
let array = JsonValue.fromStr(content).asArray() /* 解析json格式的请求体 */
let net_args: Array<String> = Jarr_to_arr(array) /* json数组转原生数组 */
ps.create(net_args)
/* ---- 准备响应 ---- */
let Headers = HttpHeaders()
Headers.add("Content-Type", "text/html; charset=utf-8;")
context.responseBuilder.setHeaders(Headers)
context.responseBuilder.body('OK')
}
})
/*
* 输出进程列表的HTTP方法
*/
server.distributor.register("/api/v2/list", { context =>
if (context.request.method == "GET") {
println('a')
/* 准备表单字符串 */
let ret = ps.list()
println('b')
/**** 准备响应 ****/
let Headers = HttpHeaders()
Headers.add("Content-Type", "text/html; charset=utf-8;")
context.responseBuilder.setHeaders(Headers)
context.responseBuilder.body(ret)
println('c')
}
})
/* 杀死一个已经存在的进程 */
server.distributor.register("/api/v2/kill", { context =>
if (context.request.method == "POST") {
let content = StringReader(context.request.body).readToEnd() /* 阅读请求体 */
let target_pid = JsonValue.fromStr(content)
.asObject()
.get("pid")
.getOrThrow()
.asInt()
.getValue()
ps.kill(target_pid)
/**** 准备响应 ****/
let Headers = HttpHeaders()
Headers.add("Content-Type", "text/html; charset=utf-8;")
context.responseBuilder.setHeaders(Headers)
context.responseBuilder.body("done")
}
})
/* 服务器开始运行 */
server.serve()
return 0
}