reload_self/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3use std::{
4  env,
5  os::unix::process::CommandExt,
6  process::{Command, Stdio},
7};
8
9use log::{error, info};
10use nix::unistd;
11use tokio::{
12  signal::unix::{SignalKind, signal},
13  task,
14};
15pub use tokio_util::sync::CancellationToken;
16
17/// 监听 `SIGHUP` 信号以触发进程重载,并返回一个 CancellationToken。
18pub fn listen() -> Result<CancellationToken, std::io::Error> {
19  let mut stream = signal(SignalKind::hangup())?;
20
21  let token = CancellationToken::new();
22  let token_for_task = token.clone();
23
24  task::spawn(async move {
25    stream.recv().await;
26    info!("接收到 SIGHUP 信号,开始重载进程");
27
28    let current_exe = match env::current_exe() {
29      Ok(path) => path,
30      Err(e) => {
31        error!("NO EXE PATH {}", e);
32        return;
33      }
34    };
35
36    // 获取传递给当前进程的参数
37    let args: Vec<String> = env::args().collect();
38
39    info!("启动新的子进程: {} {:?}", current_exe.display(), &args[1..]);
40
41    let mut command = Command::new(current_exe);
42
43    unsafe {
44      command
45        .args(&args[1..])
46        .stdin(Stdio::null())
47        .stdout(Stdio::inherit())
48        .stderr(Stdio::inherit())
49        .pre_exec(|| {
50          // 创建新的会话,完全脱离控制终端和父进程
51          unistd::setsid().map_err(std::io::Error::other)?;
52          Ok(())
53        });
54    }
55
56    match command.spawn() {
57      Ok(child) => {
58        info!("成功启动新的子进程,PID: {} ; 母进程开始关闭。", child.id());
59        token_for_task.cancel();
60      }
61      Err(e) => {
62        error!("启动新进程失败: {}", e);
63      }
64    }
65  });
66
67  Ok(token)
68}