reload_self 0.1.27

Cross-platform process hot reload library / 跨平台进程热重载库
Documentation
#![cfg_attr(docsrs, feature(doc_cfg))]

use std::{env, process::Command};

use log::{error, info};
use tokio::task;
pub use tokio_util::sync::CancellationToken;

#[cfg(unix)]
mod unix;
#[cfg(windows)]
mod windows;

// Import platform-specific functions / 导入平台特定函数
#[cfg(unix)]
use unix::wait_reload;
#[cfg(windows)]
use windows::wait_reload;

/// Spawn new process with same executable and arguments / 使用相同的可执行文件和参数生成新进程
async fn spawn(token: CancellationToken) {
  let current_exe = match env::current_exe() {
    Ok(path) => path,
    Err(e) => {
      error!("NO EXE PATH {e}");
      return;
    }
  };

  // Get arguments passed to current process / 获取传递给当前进程的参数
  let args: Vec<String> = env::args().collect();

  info!("reload_self : {} {:?}", current_exe.display(), &args[1..]);

  let mut command = Command::new(current_exe);
  command.args(&args[1..]);

  // On Unix, keep the child process in the same process group / 在 Unix 上,让子进程保持在同一个进程组中
  #[cfg(unix)]
  {
    use std::os::unix::process::CommandExt;
    command.process_group(0);
  }

  // .stdin(Stdio::null())
  // .stdout(Stdio::inherit())
  // .stderr(Stdio::inherit());

  match command.spawn() {
    Ok(child) => {
      let pid = child.id();

      info!("reload_self new process PID={pid}");

      #[cfg(target_os = "linux")]
      {
        if std::env::var("NOTIFY_SOCKET").is_ok() {
          use sd_notify::NotifyState;
          if let Err(e) = sd_notify::notify(false, &[NotifyState::MainPid(pid)]) {
            log::error!("failed to notify systemd: {}", e);
          } else {
            log::info!("notified systemd MainPid={pid}");
          }
        }
      }

      // Wait 1 second before canceling / 等待1秒后再取消
      tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
      token.cancel();
    }
    Err(e) => {
      error!("reload_self : {e}");
    }
  }
}

/// Listen for platform-specific reload signal and return a CancellationToken.
/// On Unix systems: listens for SIGHUP signal
/// On Windows systems: listens for CTRL_BREAK_EVENT signal
///
/// 监听平台特定的重载信号并返回一个 CancellationToken。
/// Unix 系统:监听 SIGHUP 信号
/// Windows 系统:监听 CTRL_BREAK_EVENT 信号
pub fn listen() -> Result<CancellationToken, std::io::Error> {
  let token = CancellationToken::new();
  let token_for_signal = token.clone();

  task::spawn(async move {
    // Wait for platform-specific signal / 等待平台特定信号
    wait_reload().await;
    spawn(token_for_signal).await;
  });

  Ok(token)
}