Added Windows service mode!
This commit is contained in:
Generated
+62
-20
@@ -84,7 +84,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "alfis"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"bincode",
|
||||
@@ -125,6 +125,7 @@ dependencies = [
|
||||
"uuid",
|
||||
"web-view",
|
||||
"winapi",
|
||||
"windows-service",
|
||||
"winres",
|
||||
"x25519-dalek",
|
||||
]
|
||||
@@ -1738,6 +1739,12 @@ dependencies = [
|
||||
"webkit2gtk-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@@ -1769,6 +1776,17 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-service"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd9db37ecb5b13762d95468a2fc6009d4b2c62801243223aabd44fca13ad13c8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"widestring",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.36.1"
|
||||
@@ -1789,19 +1807,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc 0.42.0",
|
||||
"windows_i686_gnu 0.42.0",
|
||||
"windows_i686_msvc 0.42.0",
|
||||
"windows_x86_64_gnu 0.42.0",
|
||||
"windows_aarch64_msvc 0.42.2",
|
||||
"windows_i686_gnu 0.42.2",
|
||||
"windows_i686_msvc 0.42.2",
|
||||
"windows_x86_64_gnu 0.42.2",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc 0.42.0",
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc 0.42.2",
|
||||
"windows_i686_gnu 0.42.2",
|
||||
"windows_i686_msvc 0.42.2",
|
||||
"windows_x86_64_gnu 0.42.2",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.0"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
|
||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
@@ -1811,9 +1853,9 @@ checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.0"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
@@ -1823,9 +1865,9 @@ checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.0"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
@@ -1835,9 +1877,9 @@ checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.0"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
@@ -1847,15 +1889,15 @@ checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.0"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
|
||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.0"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
|
||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
@@ -1865,9 +1907,9 @@ checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.0"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "alfis"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
authors = ["Revertron <alfis@revertron.com>"]
|
||||
edition = "2021"
|
||||
build = "build.rs"
|
||||
@@ -52,6 +52,7 @@ open = { version = "3.0.3", optional = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3.9", features = ["impl-default", "wincon", "shellscalingapi"] }
|
||||
windows-service = "0.6.0"
|
||||
thread-priority = "0.10.0"
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
|
||||
|
||||
+141
-55
@@ -6,7 +6,7 @@
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
use std::{env, thread};
|
||||
use std::{env, fs, thread};
|
||||
|
||||
use getopts::{Matches, Options};
|
||||
#[allow(unused_imports)]
|
||||
@@ -20,17 +20,23 @@ use std::fs::{File, OpenOptions};
|
||||
use std::io::{Seek, SeekFrom, Write};
|
||||
use std::process::exit;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::thread::JoinHandle;
|
||||
|
||||
use alfis::event::Event;
|
||||
use alfis::eventbus::{post, register};
|
||||
use alfis::keystore::create_key;
|
||||
use alfis::{dns_utils, Block, Bytes, Chain, Context, Keystore, Miner, Network, Settings, Transaction, ALFIS_DEBUG, ALFIS_TRACE, DB_NAME, ORIGIN_DIFFICULTY};
|
||||
#[cfg(windows)]
|
||||
use crate::win_service::start_service;
|
||||
|
||||
#[cfg(feature = "webgui")]
|
||||
mod web_ui;
|
||||
#[cfg(windows)]
|
||||
mod win_service;
|
||||
|
||||
const SETTINGS_FILENAME: &str = "alfis.toml";
|
||||
const LOG_TARGET_MAIN: &str = "alfis::Main";
|
||||
const CONFIG: &str = include_str!("../alfis.toml");
|
||||
|
||||
fn main() {
|
||||
#[allow(unused_assignments, unused_mut)]
|
||||
@@ -55,6 +61,12 @@ fn main() {
|
||||
opts.optflag("t", "trace", "Show trace messages, more than debug");
|
||||
opts.optflag("b", "blocks", "List blocks from DB and exit");
|
||||
opts.optflag("g", "generate", "Generate new config file. Generated config will be printed to console.");
|
||||
#[cfg(windows)]
|
||||
{
|
||||
opts.optflag("", "install", "Install self as Windows service.");
|
||||
opts.optflag("", "uninstall", "Uninstall self as Windows service.");
|
||||
opts.optflag("", "service", "Run as Windows service.");
|
||||
}
|
||||
opts.optopt("k", "gen-key", "Generate new keys and save them to file.", "FILE");
|
||||
opts.optopt("l", "log", "Write log to file", "FILE");
|
||||
opts.optopt("s", "status", "Write status to file", "FILE");
|
||||
@@ -79,7 +91,40 @@ fn main() {
|
||||
}
|
||||
|
||||
if opt_matches.opt_present("g") {
|
||||
println!("{}", include_str!("../alfis.toml"));
|
||||
println!("{}", CONFIG);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
if opt_matches.opt_present("install") {
|
||||
let progdata = env::var("PROGRAMDATA").expect("Failed to get APPDATA directory");
|
||||
|
||||
// Create a new directory inside the AppData directory
|
||||
let new_directory = format!("{}\\ALFIS", progdata);
|
||||
fs::create_dir_all(&new_directory).expect("Failed to create directory");
|
||||
|
||||
// Change the current directory to the new directory
|
||||
env::set_current_dir(&new_directory).expect("Failed to change directory");
|
||||
let mut file = File::create("alfis.toml").expect("Failed to create alfis.toml in AppData\\ALFIS");
|
||||
file.write_all(CONFIG.as_bytes()).expect("Failed to write alfis.toml");
|
||||
|
||||
use crate::win_service::*;
|
||||
install_service(SERVICE_NAME, &program);
|
||||
// Without explicitly detaching the console cmd won't redraw it's prompt.
|
||||
unsafe {
|
||||
FreeConsole();
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
if opt_matches.opt_present("uninstall") {
|
||||
use crate::win_service::*;
|
||||
uninstall_service(SERVICE_NAME);
|
||||
// Without explicitly detaching the console cmd won't redraw it's prompt.
|
||||
unsafe {
|
||||
FreeConsole();
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -97,10 +142,22 @@ fn main() {
|
||||
};
|
||||
|
||||
#[cfg(feature = "webgui")]
|
||||
let no_gui = opt_matches.opt_present("n");
|
||||
let mut no_gui = opt_matches.opt_present("n");
|
||||
#[cfg(not(feature = "webgui"))]
|
||||
let no_gui = true;
|
||||
|
||||
if opt_matches.opt_present("service") {
|
||||
let appdata = env::var("PROGRAMDATA").expect("Failed to get APPDATA directory");
|
||||
|
||||
// Create a new directory inside the AppData directory
|
||||
let new_directory = format!("{}\\ALFIS", appdata);
|
||||
fs::create_dir_all(&new_directory).expect("Failed to create directory");
|
||||
|
||||
// Change the current directory to the new directory
|
||||
env::set_current_dir(&new_directory).expect("Failed to change directory");
|
||||
no_gui = true;
|
||||
}
|
||||
|
||||
if let Some(path) = opt_matches.opt_str("w") {
|
||||
env::set_current_dir(Path::new(&path)).unwrap_or_else(|_| panic!("Unable to change working directory to '{}'", &path));
|
||||
}
|
||||
@@ -131,33 +188,22 @@ fn main() {
|
||||
|
||||
let settings = Settings::load(&config_name).unwrap_or_else(|| panic!("Cannot load settings from {}!", &config_name));
|
||||
debug!(target: LOG_TARGET_MAIN, "Loaded settings: {:?}", &settings);
|
||||
let chain: Chain = Chain::new(&settings, DB_NAME);
|
||||
if opt_matches.opt_present("b") {
|
||||
for i in 1..(chain.get_height() + 1) {
|
||||
if let Some(block) = chain.get_block(i) {
|
||||
info!(target: LOG_TARGET_MAIN, "{:?}", &block);
|
||||
}
|
||||
let settings_copy = settings.clone();
|
||||
|
||||
let context = match create_context(opt_matches.opt_present("b"), settings) {
|
||||
Some(value) => value,
|
||||
None => return,
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
if opt_matches.opt_present("service") {
|
||||
if let Err(e) = start_service(settings_copy, context) {
|
||||
error!("Unable to start service: {}", e);
|
||||
exit(1);
|
||||
}
|
||||
info!("Service is stopped.");
|
||||
return;
|
||||
}
|
||||
info!("Blocks count: {}, domains count: {}, users count: {}", chain.get_height(), chain.get_domains_count(), chain.get_users_count());
|
||||
let settings_copy = settings.clone();
|
||||
let mut keys = Vec::new();
|
||||
if !settings.key_files.is_empty() {
|
||||
for name in &settings.key_files {
|
||||
match Keystore::from_file(name, "") {
|
||||
None => {
|
||||
warn!("Error loading keyfile from {}", name);
|
||||
}
|
||||
Some(keystore) => {
|
||||
info!("Successfully loaded keyfile {}", name);
|
||||
keys.push(keystore);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let context = Context::new(env!("CARGO_PKG_VERSION").to_owned(), settings, keys, chain);
|
||||
let context: Arc<Mutex<Context>> = Arc::new(Mutex::new(context));
|
||||
|
||||
// If we just need to generate keys
|
||||
if let Some(filename) = opt_matches.opt_str("k") {
|
||||
@@ -192,34 +238,7 @@ fn main() {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if let Ok(mut context) = context.lock() {
|
||||
context.chain.check_chain(settings_copy.check_blocks);
|
||||
match context.chain.get_block(1) {
|
||||
None => {
|
||||
info!(target: LOG_TARGET_MAIN, "No blocks found in DB");
|
||||
}
|
||||
Some(block) => {
|
||||
trace!(target: LOG_TARGET_MAIN, "Loaded DB with origin {:?}", &block.hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let dns_server_ok = if settings_copy.dns.threads > 0 {
|
||||
dns_utils::start_dns_server(&context, &settings_copy)
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
let mut miner_obj = Miner::new(Arc::clone(&context));
|
||||
miner_obj.start_mining_thread();
|
||||
let miner: Arc<Mutex<Miner>> = Arc::new(Mutex::new(miner_obj));
|
||||
|
||||
let mut network = Network::new(Arc::clone(&context));
|
||||
let network = thread::Builder::new().name(String::from("Network")).spawn(move || {
|
||||
// Give UI some time to appear :)
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
network.start();
|
||||
}).expect("Could not start network thread!");
|
||||
let (dns_server_ok, miner, network) = start_services(&settings_copy, &context);
|
||||
|
||||
create_genesis_if_needed(&context, &miner);
|
||||
if no_gui {
|
||||
@@ -243,6 +262,73 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
fn create_context(only_print_blocks: bool, settings: Settings) -> Option<Arc<Mutex<Context>>> {
|
||||
let chain: Chain = Chain::new(&settings, DB_NAME);
|
||||
if only_print_blocks {
|
||||
for i in 1..(chain.get_height() + 1) {
|
||||
if let Some(block) = chain.get_block(i) {
|
||||
info!(target: LOG_TARGET_MAIN, "{:?}", &block);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
info!("Blocks count: {}, domains count: {}, users count: {}", chain.get_height(), chain.get_domains_count(), chain.get_users_count());
|
||||
let keys = load_keys(&settings);
|
||||
let context = Context::new(env!("CARGO_PKG_VERSION").to_owned(), settings, keys, chain);
|
||||
let context: Arc<Mutex<Context>> = Arc::new(Mutex::new(context));
|
||||
Some(context)
|
||||
}
|
||||
|
||||
fn load_keys(settings: &Settings) -> Vec<Keystore> {
|
||||
let mut keys = Vec::new();
|
||||
if !settings.key_files.is_empty() {
|
||||
for name in &settings.key_files {
|
||||
match Keystore::from_file(name, "") {
|
||||
None => {
|
||||
warn!("Error loading keyfile from {}", name);
|
||||
}
|
||||
Some(keystore) => {
|
||||
info!("Successfully loaded keyfile {}", name);
|
||||
keys.push(keystore);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
keys
|
||||
}
|
||||
|
||||
pub fn start_services(settings: &Settings, context: &Arc<Mutex<Context>>) -> (bool, Arc<Mutex<Miner>>, JoinHandle<()>) {
|
||||
if let Ok(mut context) = context.lock() {
|
||||
context.chain.check_chain(settings.check_blocks);
|
||||
match context.chain.get_block(1) {
|
||||
None => {
|
||||
info!(target: LOG_TARGET_MAIN, "No blocks found in DB");
|
||||
}
|
||||
Some(block) => {
|
||||
trace!(target: LOG_TARGET_MAIN, "Loaded DB with origin {:?}", &block.hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let dns_server_ok = if settings.dns.threads > 0 {
|
||||
dns_utils::start_dns_server(&context, &settings)
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
let mut miner_obj = Miner::new(Arc::clone(&context));
|
||||
miner_obj.start_mining_thread();
|
||||
let miner: Arc<Mutex<Miner>> = Arc::new(Mutex::new(miner_obj));
|
||||
|
||||
let mut network = Network::new(Arc::clone(&context));
|
||||
let network = thread::Builder::new().name(String::from("Network")).spawn(move || {
|
||||
// Give UI some time to appear :)
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
network.start();
|
||||
}).expect("Could not start network thread!");
|
||||
(dns_server_ok, miner, network)
|
||||
}
|
||||
|
||||
/// Sets up logger in accordance with command line options
|
||||
fn setup_logger(opt_matches: &Matches, console_attached: bool) {
|
||||
let mut level = LevelFilter::Info;
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, mpsc, Mutex};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use lazy_static::lazy_static;
|
||||
use log::{error, info};
|
||||
|
||||
use windows_service::{define_windows_service, service::{
|
||||
ServiceControl, ServiceExitCode, ServiceState, ServiceStatus, ServiceType,
|
||||
}, service_control_handler::ServiceControlHandlerResult, service_dispatcher, Result, service_control_handler};
|
||||
use windows_service::service::ServiceControlAccept;
|
||||
use alfis::{Context, Settings};
|
||||
use crate::start_services;
|
||||
|
||||
// Define the service entry point and its behavior
|
||||
define_windows_service!(ffi_service_main, alfis_service_main);
|
||||
|
||||
pub const SERVICE_NAME: &str = "ALFIS";
|
||||
pub const SERVICE_DESCRIPTION: &str = "Alternative Free Identity System, DNS on a smallest blockchain.";
|
||||
|
||||
lazy_static! {
|
||||
// Sending parameters through static variables. Don't do this!
|
||||
static ref SETTINGS: Mutex<(Option<Settings>, Option<Arc<Mutex<Context>>>)> = Mutex::new((None, None));
|
||||
}
|
||||
|
||||
pub fn start_service(settings: Settings, context: Arc<Mutex<Context>>) -> Result<()> {
|
||||
if let Ok(mut option) = SETTINGS.lock() {
|
||||
let _ = option.0.insert(settings);
|
||||
let _ = option.1.insert(context);
|
||||
}
|
||||
// Register the service entry point and control handler
|
||||
service_dispatcher::start(SERVICE_NAME, ffi_service_main)
|
||||
}
|
||||
|
||||
fn alfis_service_main(_arguments: Vec<OsString>) {
|
||||
if let Err(e) = run_service_logic() {
|
||||
error!("Error while starting service: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn run_service_logic() -> Result<()> {
|
||||
let (shutdown_tx, shutdown_rx) = mpsc::channel();
|
||||
let event_handler = move |control_event| -> ServiceControlHandlerResult {
|
||||
info!("Event: {:?}", &control_event);
|
||||
match control_event {
|
||||
ServiceControl::Stop => {
|
||||
// Handle stop event and return control back to the system.
|
||||
shutdown_tx.send(()).unwrap();
|
||||
ServiceControlHandlerResult::NoError
|
||||
}
|
||||
// All services must accept Interrogate even if it's a no-op.
|
||||
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
|
||||
_ => ServiceControlHandlerResult::NotImplemented,
|
||||
}
|
||||
};
|
||||
|
||||
// Register system service event handler
|
||||
let status_handle = service_control_handler::register(SERVICE_NAME, event_handler)?;
|
||||
|
||||
let next_status = ServiceStatus {
|
||||
// Should match the one from system service registry
|
||||
service_type: ServiceType::OWN_PROCESS,
|
||||
// The new state
|
||||
current_state: ServiceState::Running,
|
||||
// Accept stop events when running
|
||||
controls_accepted: ServiceControlAccept::STOP,
|
||||
// Used to report an error when starting or stopping only, otherwise must be zero
|
||||
exit_code: ServiceExitCode::Win32(0),
|
||||
// Only used for pending states, otherwise must be zero
|
||||
checkpoint: 0,
|
||||
// Only used for pending states, otherwise must be zero
|
||||
wait_hint: Duration::default(),
|
||||
// Unused for setting status
|
||||
process_id: None,
|
||||
};
|
||||
|
||||
// Tell the system that the service is running now
|
||||
status_handle.set_service_status(next_status)?;
|
||||
|
||||
let (settings, context) = {
|
||||
let mut lock = SETTINGS.lock().unwrap();
|
||||
(lock.0.take().unwrap(), lock.1.take().unwrap())
|
||||
};
|
||||
let (_dns_server_ok, _miner, _network) = start_services(&settings, &context);
|
||||
|
||||
loop {
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
// Poll shutdown event.
|
||||
match shutdown_rx.recv_timeout(Duration::from_secs(1)) {
|
||||
// Break the loop either upon stop or channel disconnect
|
||||
Ok(_) | Err(mpsc::RecvTimeoutError::Disconnected) => break,
|
||||
|
||||
// Continue work if no events were received within the timeout
|
||||
Err(mpsc::RecvTimeoutError::Timeout) => (),
|
||||
};
|
||||
}
|
||||
|
||||
let next_status = ServiceStatus {
|
||||
// Should match the one from system service registry
|
||||
service_type: ServiceType::OWN_PROCESS,
|
||||
// The new state
|
||||
current_state: ServiceState::Stopped,
|
||||
// Accept stop events when running
|
||||
controls_accepted: ServiceControlAccept::empty(),
|
||||
// Used to report an error when starting or stopping only, otherwise must be zero
|
||||
exit_code: ServiceExitCode::Win32(0),
|
||||
// Only used for pending states, otherwise must be zero
|
||||
checkpoint: 0,
|
||||
// Only used for pending states, otherwise must be zero
|
||||
wait_hint: Duration::default(),
|
||||
// Unused for setting status
|
||||
process_id: None,
|
||||
};
|
||||
status_handle.set_service_status(next_status)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Function to install a Windows service
|
||||
pub fn install_service(service_name: &str, bin_path: &str) {
|
||||
use windows_service::service_manager::*;
|
||||
use windows_service::service::*;
|
||||
let error = "Error creating service. Try to start with admin rights";
|
||||
let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE;
|
||||
let manager = ServiceManager::local_computer(None::<&str>, manager_access).expect(error);
|
||||
|
||||
let my_service_info = ServiceInfo {
|
||||
name: OsString::from(service_name),
|
||||
display_name: OsString::from(service_name),
|
||||
service_type: ServiceType::OWN_PROCESS,
|
||||
start_type: ServiceStartType::AutoStart,
|
||||
error_control: ServiceErrorControl::Normal,
|
||||
executable_path: PathBuf::from(bin_path),
|
||||
launch_arguments: vec![OsString::from("--service"), OsString::from("-l"), OsString::from("alfis_log.txt")],
|
||||
dependencies: vec![],
|
||||
account_name: None, // run as System
|
||||
account_password: None,
|
||||
};
|
||||
|
||||
let my_service = manager.create_service(&my_service_info, ServiceAccess::CHANGE_CONFIG | ServiceAccess::START).expect(error);
|
||||
let _ = my_service.set_description(&OsStr::new(SERVICE_DESCRIPTION));
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
match my_service.start(&[OsStr::new("--service")]) {
|
||||
Ok(_) => println!("Service successfully installed and started"),
|
||||
Err(e) => println!("Error starting service: {}", e)
|
||||
}
|
||||
}
|
||||
|
||||
// Function to uninstall a Windows service
|
||||
pub fn uninstall_service(service_name: &str) {
|
||||
use windows_service::service_manager::*;
|
||||
use windows_service::service::*;
|
||||
let error = "Error creating service. Try to start with admin rights";
|
||||
let manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CONNECT).expect(error);
|
||||
let service_access = ServiceAccess::QUERY_STATUS | ServiceAccess::STOP | ServiceAccess::DELETE;
|
||||
match manager.open_service(&OsStr::new(service_name), service_access) {
|
||||
Ok(service) => {
|
||||
let _ = service.stop();
|
||||
thread::sleep(Duration::from_secs(2));
|
||||
let _ = service.delete();
|
||||
}
|
||||
Err(e) => println!("Error opening service. Try running with admin rights: {}", e)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user