#![cfg_attr(docsrs, feature(doc_cfg))]
use std::{
env, fs,
os::unix::process::CommandExt,
process::{Command, Stdio},
};
use log::{error, info};
use nix::unistd;
use tokio::{
signal::unix::{SignalKind, signal},
task,
};
pub use tokio_util::sync::CancellationToken;
pub fn listen() -> Result<CancellationToken, std::io::Error> {
if let Ok(pid_file_path) = env::var("PID_FILE") {
let pid = std::process::id().to_string();
match fs::write(&pid_file_path, &pid) {
Ok(_) => info!("PID {pid} -> {pid_file_path}"),
Err(e) => error!("{pid_file_path}: {e}"),
}
}
let mut stream = signal(SignalKind::hangup())?;
let token = CancellationToken::new();
let token_for_task = token.clone();
task::spawn(async move {
stream.recv().await;
info!("接收到 SIGHUP 信号,开始重载进程");
let current_exe = match env::current_exe() {
Ok(path) => path,
Err(e) => {
error!("NO EXE PATH {}", e);
return;
}
};
let args: Vec<String> = env::args().collect();
info!("启动新的子进程: {} {:?}", current_exe.display(), &args[1..]);
let mut command = Command::new(current_exe);
unsafe {
command
.args(&args[1..])
.stdin(Stdio::null())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.pre_exec(|| {
unistd::setsid().map_err(std::io::Error::other)?;
Ok(())
});
}
match command.spawn() {
Ok(child) => {
let pid = child.id();
info!("成功启动新的子进程,PID: {} ; 母进程开始关闭。", pid);
token_for_task.cancel();
}
Err(e) => {
error!("启动新进程失败: {}", e);
}
}
});
Ok(token)
}