mirror of
https://github.com/house-of-vanity/v2-uri-parser.git
synced 2025-12-16 15:07:53 +00:00
Added --run mode to run xray with a parsed config.
This commit is contained in:
47
src/main.rs
47
src/main.rs
@@ -2,8 +2,10 @@ use clap::{value_parser, Arg, Command};
|
||||
pub mod config_models;
|
||||
mod parser;
|
||||
pub mod utils;
|
||||
mod xray_runner;
|
||||
|
||||
fn main() {
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let matches = Command::new("v2parser")
|
||||
.version("0.3.1")
|
||||
.about("Parses V2ray URI and generates JSON config for xray")
|
||||
@@ -33,12 +35,26 @@ fn main() {
|
||||
.help("Only print config meta data")
|
||||
.action(clap::ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("run")
|
||||
.long("run")
|
||||
.help("Run xray-core with the generated config")
|
||||
.action(clap::ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("xray_binary")
|
||||
.long("xray-binary")
|
||||
.help("Path to xray-core binary (default: xray from PATH)")
|
||||
.value_name("PATH"),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let uri = matches.get_one::<String>("uri").unwrap();
|
||||
let socksport = matches.get_one::<u16>("socksport").copied();
|
||||
let httpport = matches.get_one::<u16>("httpport").copied();
|
||||
let get_metadata = matches.get_flag("get_metadata");
|
||||
let run_mode = matches.get_flag("run");
|
||||
let xray_binary = matches.get_one::<String>("xray_binary").map(|s| s.as_str()).unwrap_or("xray-core");
|
||||
|
||||
if get_metadata {
|
||||
print!("{}", parser::get_metadata(uri));
|
||||
@@ -46,5 +62,32 @@ fn main() {
|
||||
}
|
||||
|
||||
let json_config = parser::create_json_config(uri, socksport, httpport);
|
||||
println!("{}", json_config);
|
||||
|
||||
if run_mode {
|
||||
// Run mode: start xray-core with the config
|
||||
let mut runner = xray_runner::XrayRunner::new();
|
||||
|
||||
match runner.start(&json_config, xray_binary).await {
|
||||
Ok(()) => {
|
||||
println!("xray-core started successfully. Press Ctrl+C to stop.");
|
||||
|
||||
// Wait for shutdown signal
|
||||
xray_runner::wait_for_shutdown_signal().await;
|
||||
|
||||
// Stop xray-core
|
||||
if let Err(e) = runner.stop().await {
|
||||
eprintln!("Error stopping xray-core: {}", e);
|
||||
} else {
|
||||
println!("xray-core stopped successfully.");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Failed to start xray-core: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Normal mode: just print the config
|
||||
println!("{}", json_config);
|
||||
}
|
||||
}
|
||||
|
||||
142
src/xray_runner.rs
Normal file
142
src/xray_runner.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
use std::process::Stdio;
|
||||
use tokio::process::{Child, Command};
|
||||
use tempfile::NamedTempFile;
|
||||
use std::io::Write;
|
||||
|
||||
pub struct XrayRunner {
|
||||
process: Option<Child>,
|
||||
config_file: Option<NamedTempFile>,
|
||||
}
|
||||
|
||||
impl XrayRunner {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
process: None,
|
||||
config_file: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start(&mut self, config_json: &str, xray_binary: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Create temporary config file with .json extension
|
||||
let mut temp_file = NamedTempFile::with_suffix(".json")?;
|
||||
temp_file.write_all(config_json.as_bytes())?;
|
||||
temp_file.flush()?;
|
||||
|
||||
let config_path = temp_file.path().to_path_buf();
|
||||
|
||||
// Start xray-core process
|
||||
let mut cmd = Command::new(xray_binary);
|
||||
cmd.arg("-config")
|
||||
.arg(&config_path)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit());
|
||||
|
||||
let child = cmd.spawn()?;
|
||||
|
||||
self.process = Some(child);
|
||||
self.config_file = Some(temp_file);
|
||||
|
||||
println!("Started xray-core with config: {:?}", config_path);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn stop(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
if let Some(mut process) = self.process.take() {
|
||||
println!("Stopping xray-core process...");
|
||||
|
||||
// Try graceful shutdown first
|
||||
if let Err(_) = process.kill().await {
|
||||
eprintln!("Failed to kill xray-core process gracefully");
|
||||
}
|
||||
|
||||
// Wait for process to exit
|
||||
match process.wait().await {
|
||||
Ok(status) => println!("xray-core exited with status: {}", status),
|
||||
Err(e) => eprintln!("Error waiting for xray-core to exit: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup config file
|
||||
if let Some(_) = self.config_file.take() {
|
||||
println!("Cleaned up temporary config file");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_running(&mut self) -> bool {
|
||||
if let Some(process) = &mut self.process {
|
||||
match process.try_wait() {
|
||||
Ok(Some(_)) => {
|
||||
// Process has exited
|
||||
self.process = None;
|
||||
false
|
||||
}
|
||||
Ok(None) => true, // Process is still running
|
||||
Err(_) => {
|
||||
// Error checking status, assume not running
|
||||
self.process = None;
|
||||
false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for XrayRunner {
|
||||
fn drop(&mut self) {
|
||||
if let Some(mut process) = self.process.take() {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
if let Some(pid) = process.id() {
|
||||
let _ = std::process::Command::new("kill")
|
||||
.arg("-TERM")
|
||||
.arg(pid.to_string())
|
||||
.output();
|
||||
}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let _ = process.start_kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn wait_for_shutdown_signal() {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use signal_hook::consts::signal::*;
|
||||
use signal_hook_tokio::Signals;
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
let mut signals = Signals::new(&[SIGINT, SIGTERM]).expect("Failed to create signals");
|
||||
|
||||
while let Some(signal) = signals.next().await {
|
||||
match signal {
|
||||
SIGINT | SIGTERM => {
|
||||
println!("\nReceived shutdown signal, stopping...");
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use tokio::signal::ctrl_c;
|
||||
|
||||
match ctrl_c().await {
|
||||
Ok(()) => {
|
||||
println!("\nReceived Ctrl+C, stopping...");
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Unable to listen for shutdown signal: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user