fix: disconnect VPN when sleep

This commit is contained in:
Kevin Yue
2025-01-12 14:41:03 +08:00
parent ec85e857bc
commit b188d61be1
22 changed files with 526 additions and 238 deletions

View File

@@ -16,7 +16,7 @@ clap.workspace = true
env_logger.workspace = true
inquire = "0.7"
log.workspace = true
tokio.workspace = true
tokio = { workspace = true, features = ["rt-multi-thread"] }
sysinfo.workspace = true
serde_json.workspace = true
whoami.workspace = true

View File

@@ -10,7 +10,7 @@ use tempfile::NamedTempFile;
use crate::{
connect::{ConnectArgs, ConnectHandler},
disconnect::DisconnectHandler,
disconnect::{DisconnectArgs, DisconnectHandler},
launch_gui::{LaunchGuiArgs, LaunchGuiHandler},
};
@@ -27,7 +27,7 @@ enum CliCommand {
#[command(about = "Connect to a portal server")]
Connect(Box<ConnectArgs>),
#[command(about = "Disconnect from the server")]
Disconnect,
Disconnect(DisconnectArgs),
#[command(about = "Launch the GUI")]
LaunchGui(LaunchGuiArgs),
}
@@ -102,7 +102,7 @@ impl Cli {
match &self.command {
CliCommand::Connect(args) => ConnectHandler::new(args, &shared_args).handle().await,
CliCommand::Disconnect => DisconnectHandler::new().handle(),
CliCommand::Disconnect(args) => DisconnectHandler::new(args).handle().await,
CliCommand::LaunchGui(args) => LaunchGuiHandler::new(args).handle().await,
}
}

View File

@@ -84,11 +84,11 @@ pub(crate) struct ConnectArgs {
#[arg(long, default_value = GP_USER_AGENT, help = "The user agent to use")]
user_agent: String,
#[arg(long, default_value = "Linux")]
#[arg(long, value_enum, default_value_t = ConnectArgs::default_os())]
os: Os,
#[arg(long)]
os_version: Option<String>,
#[arg(long, default_value_t = ConnectArgs::default_os_version())]
os_version: String,
#[arg(long, help = "Disable DTLS and ESP")]
no_dtls: bool,
@@ -113,12 +113,16 @@ pub(crate) struct ConnectArgs {
}
impl ConnectArgs {
fn os_version(&self) -> String {
if let Some(os_version) = &self.os_version {
return os_version.to_owned();
fn default_os() -> Os {
if cfg!(target_os = "macos") {
Os::Mac
} else {
Os::Linux
}
}
match self.os {
fn default_os_version() -> String {
match ConnectArgs::default_os() {
Os::Linux => format!("Linux {}", whoami::distro()),
Os::Windows => String::from("Microsoft Windows 11 Pro , 64-bit"),
Os::Mac => String::from("Apple Mac OS X 13.4.0"),
@@ -145,7 +149,7 @@ impl<'a> ConnectHandler<'a> {
GpParams::builder()
.user_agent(&self.args.user_agent)
.client_os(ClientOs::from(&self.args.os))
.os_version(self.args.os_version())
.os_version(self.args.os_version.clone())
.ignore_tls_errors(self.shared_args.ignore_tls_errors)
.certificate(self.args.certificate.clone())
.sslkey(self.args.sslkey.clone())
@@ -355,7 +359,7 @@ impl<'a> ConnectHandler<'a> {
None
};
let os_version = self.args.os_version();
let os_version = self.args.os_version.clone();
let verbose = self.shared_args.verbose.to_verbose_arg();
let auth_launcher = SamlAuthLauncher::new(&self.args.server)
.gateway(is_gateway)

View File

@@ -1,31 +1,63 @@
use crate::GP_CLIENT_LOCK_FILE;
use clap::Args;
use gpapi::utils::lock_file::gpservice_lock_info;
use log::{info, warn};
use std::fs;
use std::{fs, str::FromStr, thread, time::Duration};
use sysinfo::{Pid, Signal, System};
pub(crate) struct DisconnectHandler;
#[derive(Args)]
pub struct DisconnectArgs {
#[arg(
long,
required = false,
help = "The time in seconds to wait for the VPN connection to disconnect"
)]
wait: Option<u64>,
}
impl DisconnectHandler {
pub(crate) fn new() -> Self {
Self
pub struct DisconnectHandler<'a> {
args: &'a DisconnectArgs,
}
impl<'a> DisconnectHandler<'a> {
pub fn new(args: &'a DisconnectArgs) -> Self {
Self { args }
}
pub(crate) fn handle(&self) -> anyhow::Result<()> {
if fs::metadata(GP_CLIENT_LOCK_FILE).is_err() {
warn!("PID file not found, maybe the client is not running");
return Ok(());
pub async fn handle(&self) -> anyhow::Result<()> {
// Try to disconnect the CLI client
if let Ok(c) = fs::read_to_string(GP_CLIENT_LOCK_FILE) {
send_signal(c.trim(), Signal::Interrupt).unwrap_or_else(|err| {
warn!("Failed to send signal to client: {}", err);
});
};
// Try to disconnect the GUI service
if let Ok(c) = gpservice_lock_info().await {
send_signal(&c.pid.to_string(), Signal::User1).unwrap_or_else(|err| {
warn!("Failed to send signal to service: {}", err);
});
};
// sleep, to give the client and service time to disconnect
if let Some(wait) = self.args.wait {
thread::sleep(Duration::from_secs(wait));
}
let pid = fs::read_to_string(GP_CLIENT_LOCK_FILE)?;
let pid = pid.trim().parse::<usize>()?;
let s = System::new_all();
if let Some(process) = s.process(Pid::from(pid)) {
info!("Found process {}, killing...", pid);
if process.kill_with(Signal::Interrupt).is_none() {
warn!("Failed to kill process {}", pid);
}
}
Ok(())
}
}
fn send_signal(pid: &str, signal: Signal) -> anyhow::Result<()> {
let s = System::new_all();
let pid = Pid::from_str(pid)?;
if let Some(process) = s.process(pid) {
info!("Found process {}, sending signal...", pid);
if process.kill_with(signal).is_none() {
warn!("Failed to kill process {}", pid);
}
}
Ok(())
}