diff options
| -rw-r--r-- | Cargo.lock | 20 | ||||
| -rw-r--r-- | Cargo.toml | 4 | ||||
| -rw-r--r-- | README.md | 60 | ||||
| -rw-r--r-- | WINDOWS_GUIDE.txt | 39 | ||||
| -rw-r--r-- | dpibreak.1.in | 22 | ||||
| -rw-r--r-- | dpibreak.1.md | 13 | ||||
| -rw-r--r-- | res/windows_bats/service_install.bat | 27 | ||||
| -rw-r--r-- | res/windows_bats/service_remove.bat | 22 | ||||
| -rw-r--r-- | src/main.rs | 6 | ||||
| -rw-r--r-- | src/opt.rs | 178 | ||||
| -rw-r--r-- | src/platform/linux.rs | 36 | ||||
| -rw-r--r-- | src/platform/windows.rs | 46 |
12 files changed, 362 insertions, 111 deletions
@@ -235,6 +235,15 @@ dependencies = [ ] [[package]] +name = "daemonize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8bfdaacb3c887a54d41bdf48d3af8873b3f5566469f8ba21b92057509f116e" +dependencies = [ + "libc", +] + +[[package]] name = "dispatch2" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -253,12 +262,14 @@ dependencies = [ "anyhow", "criterion", "ctrlc", + "daemonize", "etherparse 0.18.2", "nfq-updated", "nix 0.27.1", "serde_json", "socket2", "windivert", + "windows-services", "winres", ] @@ -780,6 +791,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] +name = "windows-services" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cffa4f5d8d05f500769005f4fa875e9b50059c8c6c84cc5dc810848d895f8ba9" +dependencies = [ + "windows-link", +] + +[[package]] name = "windows-sys" version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -38,9 +38,11 @@ nfq = { package = "nfq-updated", version = "0.2.6" } # nfq-updated: to us as_raw socket2 = { version = "0.6", features = ["all"] } nix = { version = "0.27.1", features = ["fs", "poll"] } serde_json = "1.0.145" +daemonize = "0.5.0" [target.'cfg(windows)'.dependencies] windivert = { version = "0.6.0" } +windows-services = "0.26" [build-dependencies] winres = "0.1" @@ -61,4 +63,4 @@ bench = [] [[bench]] name = "hoptab_bench" harness = false -required-features = ["bench"]
\ No newline at end of file +required-features = ["bench"] @@ -35,29 +35,32 @@ section on [dpibreak(1)](./dpibreak.1.md#OPTIONS) for more information.) ## Quickstart +Download the release from +<https://github.com/dilluti0n/dpibreak/releases/latest>. ### Windows -1. Download and unzip the release from - <https://github.com/dilluti0n/dpibreak/releases/latest>. -2. Double-click `dpibreak.exe` or `start_fake.bat` to use -[fake](#fake). (See `WINDOWS_GUIDE.txt` for more information.) +- Double-click `dpibreak.exe` or `start_fake.bat` (To use +[fake](#fake)). +- Run `service_install.bat` with administrator privileges to + automatically run per boot (To remove, run `service_remove.bat`). +- See `WINDOWS_GUIDE.txt` for more information. ### Linux -1. Download release tarball from - <https://github.com/dilluti0n/dpibreak/releases/latest>. -2. Extract and install: +1. Extract and install: ```bash tar -xf DPIBreak-X.Y.Z-x86_64-unknown-linux-musl.tar.gz cd DPIBreak-X.Y.Z-x86_64-unknown-linux-musl sudo make install ``` -3. To run: +2. To run: ```bash sudo dpibreak -sudo dpibreak --fake --fake-ttl 8 +sudo dpibreak -D # run as daemon +sudo dpibreak --fake-autottl # enable fake packet injection +sudo dpibreak -D --fake-autottl dpibreak --help -man 1 dpibreak # manual +man 1 dpibreak # manual ``` -4. To uninstall: +3. To uninstall: ```bash sudo make uninstall ``` @@ -67,16 +70,13 @@ Available in the [GURU](https://wiki.gentoo.org/wiki/Project:GURU) repository. ```bash -eselect repository enable guru -emaint sync -r guru -echo net-misc/dpibreak ~amd64 > /etc/portage/package.accept_keywords/dpibreak -emerge --ask net-misc/dpibreak +sudo eselect repository enable guru +sudo emaint sync -r guru +echo 'net-misc/dpibreak ~amd64' | sudo tee /etc/portage/package.accept_keywords/dpibreak +sudo emerge --ask net-misc/dpibreak ``` -### Linux with Cargo (crates.io) -Install Rust toolchain from -<https://www.rust-lang.org/learn/get-started>. - +### For rust developers (crates.io) ```bash cargo install dpibreak ``` @@ -145,9 +145,17 @@ For introducing the circumvention idea: - [zapret](https://github.com/bol-van/zapret) by bol-van - [SpoofDPI](https://github.com/xvzc/SpoofDPI) by xzvc -## See more +## Notice +Copyright 2025-2026 Dilluti0n. + +Licensed under GPL-3.0-or-later. + + + +<details> +<summary>See more (alternative tools & useful links)</summary> -Alternative tools: +#### Alternative tools: - [Green Tunnel](https://github.com/SadeghHayeri/GreenTunnel) by SadeghHayeri (for MacOS, Linux and Windows) - [DPI Tunnel CLI](https://github.com/nomoresat/DPITunnel-cli) by @@ -170,16 +178,10 @@ Alternative tools: Waujito (for OpenWRT/Entware routers and Linux) - [NoDPI](https://github.com/GVCoder09/NoDPI/) for Windows and Linux -Useful links: +#### Useful links: - <https://geneva.cs.umd.edu/papers/geneva_ccs19.pdf> - <https://github.com/bol-van/zapret/blob/master/docs/readme.en.md> - <https://deepwiki.com/bol-van/zapret/3-dpi-circumvention-techniques> - <https://www.ias.edu/security/deep-packet-inspection-dead-and-heres-why> - <https://www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/> - -## Notice -Copyright 2025-2026 Dilluti0n. - -Licensed under GPL-3.0-or-later. - - +</details> diff --git a/WINDOWS_GUIDE.txt b/WINDOWS_GUIDE.txt index efd9afa..91fb971 100644 --- a/WINDOWS_GUIDE.txt +++ b/WINDOWS_GUIDE.txt @@ -1,9 +1,36 @@ -DPIBreak Windows users' guide +DPIBreak Windows user guide +=========================== -- Double click dpibreak.exe to run with normal mode. -- Double click start_fake.bat to run with fake mode (see --fake part - of manual). -- After it starts, a console window will open. Keep this window open +See dpibreak.1.md for full manual. + +Normal start: +* Double click dpibreak.exe to run with normal mode. +* Double click start_fake.bat to run with fake mode. + - See `--fake' of manual for more information. +* After it starts, a console window will open. Keep this window open while using the program. -- See dpibreak.1.md for full manual. +Windows service (automatically start at boot): +* Install & start: Right click service_install.bat -> Run as administrator + - DPIBreak will automatically start per boot + - See `-D, --daemon' of manual for more information. +* Uninstall: Right click service_remove.bat -> Run as administrator + +--- + +DPIBreak Windows 사용 안내 +========================= + +전체 설명서는 dpibreak.1.md를 참고하세요. + +일반 실행: +* dpibreak.exe를 더블 클릭하면 일반 모드로 실행됩니다. +* start_fake.bat을 더블 클릭하면 fake 모드로 실행됩니다. + - 자세한 내용은 설명서의 `--fake' 항목을 참고하세요. +* 실행 후 콘솔 창이 열립니다. 프로그램 사용 중에는 이 창을 닫지 마세요. + +Windows 서비스 (부팅 시 자동 실행): +* 설치 및 시작: service_install.bat 우클릭 -> 관리자 권한으로 실행 + - 부팅 시 DPIBreak가 자동으로 시작됩니다. + - 자세한 내용은 설명서의 `-D, --daemon' 항목을 참고하세요. +* 제거: service_remove.bat 우클릭 -> 관리자 권한으로 실행 diff --git a/dpibreak.1.in b/dpibreak.1.in index 0c2a7da..b62cae5 100644 --- a/dpibreak.1.in +++ b/dpibreak.1.in @@ -58,6 +58,20 @@ startup. .SH OPTIONS .TP +.BR \-D ", " \-\-daemon +Run as a background daemon. Logs are written to +.B /tmp/dpibreak.log. +If a daemon is already running, it will fail with "unable to lock pid +file". To stop it, run +.B kill \(gacat /tmp/dpibreak.pid\(ga +as root. +.RS +.PP +On Windows, this option enters the service controller entry +point. Example: +.B sc create dpibreak binPath= \(dqdpibreak.exe \-D\(dq start= auto; sc start dpibreak +.RE +.TP .B \-\-delay\-ms \fI<u64>\fR Delay in milliseconds to apply between fragmented pieces of the ClientHello. Typical values are 0–1000; larger values may increase @@ -131,6 +145,14 @@ Run with default options: .B dpibreak .RE .PP +Run as daemon with +.B fake +feature: +.PP +.RS +.B dpibreak \-D \-\-fake\-autottl +.RE +.PP Run with a 10 ms delay and verbose logging: .PP .RS diff --git a/dpibreak.1.md b/dpibreak.1.md index 6d528fa..f5760b2 100644 --- a/dpibreak.1.md +++ b/dpibreak.1.md @@ -40,6 +40,15 @@ program opens the driver automatically at startup. ## OPTIONS +**-D**, **--daemon** +Run as a background daemon. Logs are written to **/tmp/dpibreak.log.** +If a daemon is already running, it will fail with "unable to lock pid +file". To stop it, run **kill \`cat /tmp/dpibreak.pid\`** as root. + +On Windows, this option enters the service controller entry point. +Example: **sc create dpibreak binPath= "dpibreak.exe -D" start= auto; sc +start dpibreak** + **--delay-ms *\<u64\>*** Delay in milliseconds to apply between fragmented pieces of the ClientHello. Typical values are 0–1000; larger values may increase @@ -96,6 +105,10 @@ Run with default options: > **dpibreak** +Run as daemon with **fake** feature: + +> **dpibreak -D --fake-autottl** + Run with a 10 ms delay and verbose logging: > **dpibreak --delay-ms 10 --loglevel debug** diff --git a/res/windows_bats/service_install.bat b/res/windows_bats/service_install.bat new file mode 100644 index 0000000..327f37e --- /dev/null +++ b/res/windows_bats/service_install.bat @@ -0,0 +1,27 @@ +@REM SPDX-FileCopyrightText: 2026 Dilluti0n <[email protected]> +@REM SPDX-License-Identifier: GPL-3.0-or-later + +@echo Please run this script with administrator privileges. +@echo Right click, select "Run as administrator". +@echo. +@echo Press any key if you are running this as administrator. +@pause + +@set "ARGS=-D --fake-ttl 8 --fake-autottl" +@set "EXE=%~dp0dpibreak.exe" +@set "SERVNAME=dpibreak" + +sc stop %SERVNAME% +sc delete %SERVNAME% +sc create %SERVNAME% binPath= "\"%EXE%\" %ARGS%" start= auto +sc description %SERVNAME% "Simple and efficient DPI circumvention tool in Rust." +sc start %SERVNAME% +@if %ERRORLEVEL% equ 0 ( + @echo. + @echo Installation complete. %SERVNAME% is running and will start automatically on boot. + @echo To stop and uninstall the service, run service_remove.bat. +) else ( + @echo. + @echo Installation failed. Make sure to run this script as administrator. +) +@pause diff --git a/res/windows_bats/service_remove.bat b/res/windows_bats/service_remove.bat new file mode 100644 index 0000000..1eb8183 --- /dev/null +++ b/res/windows_bats/service_remove.bat @@ -0,0 +1,22 @@ +@REM SPDX-FileCopyrightText: 2026 Dilluti0n <[email protected]> +@REM SPDX-License-Identifier: GPL-3.0-or-later + +@echo Please run this script with administrator privileges. +@echo Right click, select "Run as administrator". +@echo. +@echo Press any key if you are running this as administrator. +@pause + +@set "SERVNAME=dpibreak" + +sc stop %SERVNAME% +sc delete %SERVNAME% +@if %ERRORLEVEL% equ 0 ( + @echo. + @echo Service %SERVNAME% removed successfully. + @sc stop windivert >nul 2>&1 +) else ( + @echo. + @echo Service %SERVNAME% not found or already removed. +) +@pause diff --git a/src/main.rs b/src/main.rs index a36bd48..90efdcc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -64,7 +64,11 @@ fn splash_banner() { fn main_0() -> Result<()> { trap_exit()?; - opt::parse_args(); + let opt = opt::Opt::from_args()?; + if opt.daemon { + platform::daemonize_1(); + } + opt.set_opt()?; splash_banner(); let _guard = EnsureCleanup; @@ -23,6 +23,7 @@ static OPT_DELAY_MS: OnceLock<u64> = OnceLock::new(); #[cfg(target_os = "linux")] static OPT_QUEUE_NUM: OnceLock<u16> = OnceLock::new(); #[cfg(target_os = "linux")] static OPT_NFT_COMMAND: OnceLock<String> = OnceLock::new(); +const DEFAULT_DAEMON: bool = false; #[cfg(debug_assertions)] const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Debug; #[cfg(not(debug_assertions))] const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Warning; const DEFAULT_NO_SPLASH: bool = false; @@ -37,6 +38,109 @@ const DEFAULT_DELAY_MS: u64 = 0; #[cfg(target_os = "linux")] const DEFAULT_QUEUE_NUM: u16 = 1; #[cfg(target_os = "linux")] const DEFAULT_NFT_COMMAND: &str = "nft"; +pub struct Opt { + pub daemon: bool, + log_level: LogLevel, + no_splash: bool, + fake: bool, + fake_ttl: u8, + fake_autottl: bool, + fake_badsum: bool, + delay_ms: u64, + #[cfg(target_os = "linux")] queue_num: u16, + #[cfg(target_os = "linux")] nft_command: String, +} + +impl Opt { + pub fn from_args() -> Result<Self> { + let mut daemon = DEFAULT_DAEMON; + let mut log_level = DEFAULT_LOG_LEVEL; + let mut delay_ms = DEFAULT_DELAY_MS; + let mut no_splash = DEFAULT_NO_SPLASH; + let mut fake = DEFAULT_FAKE; + let mut fake_ttl = DEFAULT_FAKE_TTL; + let mut fake_autottl = DEFAULT_FAKE_AUTOTTL; + let mut fake_badsum = DEFAULT_FAKE_BADSUM; + + #[cfg(target_os = "linux")] + let mut queue_num: u16 = DEFAULT_QUEUE_NUM; + #[cfg(target_os = "linux")] + let mut nft_command = String::from(DEFAULT_NFT_COMMAND); + + let mut args = std::env::args().skip(1); // program name + + let mut warned_loglevel_deprecated = false; + + while let Some(arg) = args.next() { + let argv = arg.as_str(); + + match argv { + "-h" | "--help" => { usage(); std::process::exit(0); } + "-D" | "--daemon" => { + no_splash = true; + // if it is unchanged explicitly by argument, set it to info + if log_level == DEFAULT_LOG_LEVEL { + log_level = LogLevel::Info; + } + daemon = true; + } + "--delay-ms" => { delay_ms = take_value(&mut args, argv)?; } + "--log-level" | "--loglevel" => { + if argv == "--loglevel" && !warned_loglevel_deprecated { + // FIXME(on release): remove this on v1.0.0 + warned_loglevel_deprecated = true; + eprintln!("Note: `{arg}' has been deprecated since v0.1.1. Use `--log-level' instead."); + } + log_level = take_value(&mut args, argv)?; + } + "--no-splash" => { no_splash = true; } + + "--fake" => { fake = true; } + "--fake-ttl" => { fake = true; fake_ttl = take_value(&mut args, argv)?; } + "--fake-autottl" => { fake = true; fake_autottl = true } + "--fake-badsum" => { fake = true; fake_badsum = true } + + #[cfg(target_os = "linux")] + "--queue-num" => { queue_num = take_value(&mut args, argv)?; } + + #[cfg(target_os = "linux")] + "--nft-command" => { nft_command = take_value(&mut args, argv)?; } + + _ => { return Err(anyhow!("argument: unknown: {}", arg)); } + } + } + + Ok(Opt { + daemon: daemon, + log_level: log_level, + no_splash: no_splash, + fake: fake, + fake_ttl: fake_ttl, + fake_autottl: fake_autottl, + fake_badsum: fake_badsum, + delay_ms: delay_ms, + #[cfg(target_os = "linux")] queue_num: queue_num, + #[cfg(target_os = "linux")] nft_command: nft_command, + }) + } + + pub fn set_opt(self) -> Result<()> { + set_opt("OPT_LOG_LEVEL", &OPT_LOG_LEVEL, self.log_level)?; + set_opt("OPT_NO_SPLASH", &OPT_NO_SPLASH, self.no_splash)?; + + set_opt("OPT_DELAY_MS", &OPT_DELAY_MS, self.delay_ms)?; + set_opt("OPT_FAKE", &OPT_FAKE, self.fake)?; + set_opt("OPT_FAKE_TTL", &OPT_FAKE_TTL, self.fake_ttl)?; + set_opt("OPT_FAKE_AUTOTTL", &OPT_FAKE_AUTOTTL, self.fake_autottl)?; + set_opt("OPT_FAKE_BADSUM", &OPT_FAKE_BADSUM, self.fake_badsum)?; + + #[cfg(target_os = "linux")] set_opt("OPT_QUEUE_NUM", &OPT_QUEUE_NUM, self.queue_num)?; + #[cfg(target_os = "linux")] set_opt("OPT_NFT_COMMAND", &OPT_NFT_COMMAND, self.nft_command)?; + + Ok(()) + } +} + pub fn no_splash() -> bool { *OPT_NO_SPLASH.get().unwrap_or(&DEFAULT_NO_SPLASH) } @@ -91,6 +195,7 @@ where fn usage() { println!("Usage: dpibreak [OPTIONS]\n"); println!("Options:"); + println!(" -D, --daemon Run as daemon; kill `pidof dpibreak` to stop."); println!(" --delay-ms <u64> (default: {DEFAULT_DELAY_MS})"); #[cfg(target_os = "linux")] println!(" --queue-num <u16> (default: {DEFAULT_QUEUE_NUM})"); @@ -121,76 +226,3 @@ fn set_opt<T: std::fmt::Display>( Ok(()) } - -fn parse_args_1() -> Result<()> { - let mut log_level = DEFAULT_LOG_LEVEL; - let mut delay_ms = DEFAULT_DELAY_MS; - let mut no_splash = DEFAULT_NO_SPLASH; - let mut fake = DEFAULT_FAKE; - let mut fake_ttl = DEFAULT_FAKE_TTL; - let mut fake_autottl = DEFAULT_FAKE_AUTOTTL; - let mut fake_badsum = DEFAULT_FAKE_BADSUM; - - #[cfg(target_os = "linux")] - let mut queue_num: u16 = DEFAULT_QUEUE_NUM; - #[cfg(target_os = "linux")] - let mut nft_command = String::from(DEFAULT_NFT_COMMAND); - - let mut args = std::env::args().skip(1); // program name - - let mut warned_loglevel_deprecated = false; - - while let Some(arg) = args.next() { - let argv = arg.as_str(); - - match argv { - "-h" | "--help" => { usage(); std::process::exit(0); } - "--delay-ms" => { delay_ms = take_value(&mut args, argv)?; } - "--log-level" | "--loglevel" => { - if argv == "--loglevel" && !warned_loglevel_deprecated { - // FIXME(on release): remove this on v1.0.0 - warned_loglevel_deprecated = true; - eprintln!("Note: `{arg}' has been deprecated since v0.1.1. \ -Use `--log-level' instead."); - } - log_level = take_value(&mut args, argv)?; - } - "--no-splash" => { no_splash = true; } - - "--fake" => { fake = true; } - "--fake-ttl" => { fake = true; fake_ttl = take_value(&mut args, argv)?; } - "--fake-autottl" => { fake = true; fake_autottl = true } - "--fake-badsum" => { fake = true; fake_badsum = true } - - #[cfg(target_os = "linux")] - "--queue-num" => { queue_num = take_value(&mut args, argv)?; } - - #[cfg(target_os = "linux")] - "--nft-command" => { nft_command = take_value(&mut args, argv)?; } - - _ => { return Err(anyhow!("argument: unknown: {}", arg)); } - } - } - - set_opt("OPT_LOG_LEVEL", &OPT_LOG_LEVEL, log_level)?; - set_opt("OPT_NO_SPLASH", &OPT_NO_SPLASH, no_splash)?; - - set_opt("OPT_DELAY_MS", &OPT_DELAY_MS, delay_ms)?; - set_opt("OPT_FAKE", &OPT_FAKE, fake)?; - set_opt("OPT_FAKE_TTL", &OPT_FAKE_TTL, fake_ttl)?; - set_opt("OPT_FAKE_AUTOTTL", &OPT_FAKE_AUTOTTL, fake_autottl)?; - set_opt("OPT_FAKE_BADSUM", &OPT_FAKE_BADSUM, fake_badsum)?; - - #[cfg(target_os = "linux")] set_opt("OPT_QUEUE_NUM", &OPT_QUEUE_NUM, queue_num)?; - #[cfg(target_os = "linux")] set_opt("OPT_NFT_COMMAND", &OPT_NFT_COMMAND, nft_command)?; - - Ok(()) -} - -pub fn parse_args() { - if let Err(e) = parse_args_1() { - log_println!(LogLevel::Error, "{e}"); - usage(); - std::process::exit(1); - } -} diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 32fd819..e6cf577 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -220,3 +220,39 @@ pub fn run() -> Result<()> { Ok(()) } + +const PKG_NAME: &str = env!("CARGO_PKG_NAME"); +const DAEMON_PREFIX: &str = "/tmp"; + +fn daemonize() -> Result<()> { + use std::fs; + use daemonize::Daemonize; + + let log_file = fs::File::create(format!("{DAEMON_PREFIX}/{PKG_NAME}.log"))?; + let pid_file = format!("{DAEMON_PREFIX}/{PKG_NAME}.pid"); + + let daemonize = Daemonize::new() + .pid_file(&pid_file) + .chown_pid_file(true) + .working_directory(DAEMON_PREFIX) + .stdout(log_file); + + daemonize.start()?; + + // TODO: detach damonize and opt.rs and use log_println here + println!("start as daemon: pid {}", std::process::id()); + + Ok(()) +} + +pub fn daemonize_1() { + const EXIT_DAEMON_FAIL: i32 = 2; + + match daemonize() { + Ok(_) => {}, + Err(e) => { + println!("{PKG_NAME}: fail to start as daemon: {e}"); + std::process::exit(EXIT_DAEMON_FAIL); + } + } +} diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 37a6654..9ad2474 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -22,6 +22,7 @@ use windivert::{ }; use std::sync::{atomic::Ordering, LazyLock, Mutex, MutexGuard}; use crate::{log::LogLevel, log_println, splash}; +use crate::opt; fn windivert_filter() -> String { let base = "(outbound and tcp and tcp.DstPort == 443 \ @@ -93,8 +94,10 @@ pub fn send_to_raw(pkt: &[u8]) -> Result<()> { Ok(()) } +use crate::RUNNING; + pub fn run() -> Result<()> { - use crate::{handle_packet, RUNNING, MESSAGE_AT_RUN}; + use crate::{handle_packet, MESSAGE_AT_RUN}; use super::PACKET_SIZE_CAP; let mut windivert_buf = vec![0u8; 65536]; @@ -115,3 +118,44 @@ pub fn run() -> Result<()> { Ok(()) } + +fn service_main() -> Result<()> { + let opt = opt::Opt::from_args()?; + opt.set_opt()?; + bootstrap()?; + run()?; + cleanup()?; + + Ok(()) +} + +fn service_main_1() { + if service_main().is_err() { + std::process::exit(1); + } + std::process::exit(0); +} + +pub fn daemonize_1() { + use windows_services::Command; + + match windows_services::Service::new() + .can_stop() + .run(|_, command| { + match command { + Command::Start => { + std::thread::spawn(|| service_main_1()); + } + Command::Stop => { + RUNNING.store(false, Ordering::SeqCst); + } + _ => {} + } + }) { + Ok(_) => {} + Err(e) => { + println!("{e}"); + std::process::exit(1); + } + }; +} |
