- Implementa Windows Service (raven-service) para operacoes privilegiadas - Comunicacao via Named Pipes sem necessidade de UAC adicional - Adiciona single-instance para evitar multiplos icones na bandeja - Corrige todos os warnings do clippy (rustdesk, lib, usb_control, agent) - Remove fallback de elevacao para evitar UAC desnecessario - USB Policy e RustDesk provisioning agora usam o servico quando disponivel 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
268 lines
8.3 KiB
Rust
268 lines
8.3 KiB
Rust
//! Raven Service - Servico Windows para operacoes privilegiadas
|
|
//!
|
|
//! Este servico roda como LocalSystem e executa operacoes que requerem
|
|
//! privilegios de administrador, como:
|
|
//! - Aplicar politicas de USB
|
|
//! - Provisionar e configurar RustDesk
|
|
//! - Modificar chaves de registro em HKEY_LOCAL_MACHINE
|
|
//!
|
|
//! O app Raven UI comunica com este servico via Named Pipes.
|
|
|
|
mod ipc;
|
|
mod rustdesk;
|
|
mod usb_policy;
|
|
|
|
use std::ffi::OsString;
|
|
use std::time::Duration;
|
|
use tracing::{error, info};
|
|
use windows_service::{
|
|
define_windows_service,
|
|
service::{
|
|
ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus,
|
|
ServiceType,
|
|
},
|
|
service_control_handler::{self, ServiceControlHandlerResult},
|
|
service_dispatcher,
|
|
};
|
|
|
|
const SERVICE_NAME: &str = "RavenService";
|
|
const SERVICE_DISPLAY_NAME: &str = "Raven Desktop Service";
|
|
const SERVICE_DESCRIPTION: &str = "Servico do Raven Desktop para operacoes privilegiadas (USB, RustDesk)";
|
|
const PIPE_NAME: &str = r"\\.\pipe\RavenService";
|
|
|
|
define_windows_service!(ffi_service_main, service_main);
|
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
// Configura logging
|
|
init_logging();
|
|
|
|
// Verifica argumentos de linha de comando
|
|
let args: Vec<String> = std::env::args().collect();
|
|
|
|
if args.len() > 1 {
|
|
match args[1].as_str() {
|
|
"install" => {
|
|
install_service()?;
|
|
return Ok(());
|
|
}
|
|
"uninstall" => {
|
|
uninstall_service()?;
|
|
return Ok(());
|
|
}
|
|
"run" => {
|
|
// Modo de teste: roda sem registrar como servico
|
|
info!("Executando em modo de teste (nao como servico)");
|
|
run_standalone()?;
|
|
return Ok(());
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
// Inicia como servico Windows
|
|
info!("Iniciando Raven Service...");
|
|
service_dispatcher::start(SERVICE_NAME, ffi_service_main)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn init_logging() {
|
|
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
|
|
|
// Tenta criar diretorio de logs
|
|
let log_dir = std::env::var("PROGRAMDATA")
|
|
.map(|p| std::path::PathBuf::from(p).join("RavenService").join("logs"))
|
|
.unwrap_or_else(|_| std::path::PathBuf::from("C:\\ProgramData\\RavenService\\logs"));
|
|
|
|
let _ = std::fs::create_dir_all(&log_dir);
|
|
|
|
// Arquivo de log
|
|
let log_file = log_dir.join("service.log");
|
|
let file = std::fs::OpenOptions::new()
|
|
.create(true)
|
|
.append(true)
|
|
.open(&log_file)
|
|
.ok();
|
|
|
|
let filter = EnvFilter::try_from_default_env()
|
|
.unwrap_or_else(|_| EnvFilter::new("info"));
|
|
|
|
if let Some(file) = file {
|
|
tracing_subscriber::registry()
|
|
.with(filter)
|
|
.with(fmt::layer().with_writer(file).with_ansi(false))
|
|
.init();
|
|
} else {
|
|
tracing_subscriber::registry()
|
|
.with(filter)
|
|
.with(fmt::layer())
|
|
.init();
|
|
}
|
|
}
|
|
|
|
fn service_main(arguments: Vec<OsString>) {
|
|
if let Err(e) = run_service(arguments) {
|
|
error!("Erro ao executar servico: {}", e);
|
|
}
|
|
}
|
|
|
|
fn run_service(_arguments: Vec<OsString>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
info!("Servico iniciando...");
|
|
|
|
// Canal para shutdown
|
|
let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel::<()>();
|
|
let shutdown_tx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_tx)));
|
|
|
|
// Registra handler de controle do servico
|
|
let shutdown_tx_clone = shutdown_tx.clone();
|
|
let status_handle = service_control_handler::register(SERVICE_NAME, move |control| {
|
|
match control {
|
|
ServiceControl::Stop | ServiceControl::Shutdown => {
|
|
info!("Recebido comando de parada");
|
|
if let Ok(mut guard) = shutdown_tx_clone.lock() {
|
|
if let Some(tx) = guard.take() {
|
|
let _ = tx.send(());
|
|
}
|
|
}
|
|
ServiceControlHandlerResult::NoError
|
|
}
|
|
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
|
|
_ => ServiceControlHandlerResult::NotImplemented,
|
|
}
|
|
})?;
|
|
|
|
// Atualiza status para Running
|
|
status_handle.set_service_status(ServiceStatus {
|
|
service_type: ServiceType::OWN_PROCESS,
|
|
current_state: ServiceState::Running,
|
|
controls_accepted: ServiceControlAccept::STOP | ServiceControlAccept::SHUTDOWN,
|
|
exit_code: ServiceExitCode::Win32(0),
|
|
checkpoint: 0,
|
|
wait_hint: Duration::default(),
|
|
process_id: None,
|
|
})?;
|
|
|
|
info!("Servico em execucao, aguardando conexoes...");
|
|
|
|
// Cria runtime Tokio
|
|
let runtime = tokio::runtime::Runtime::new()?;
|
|
|
|
// Executa servidor IPC
|
|
runtime.block_on(async {
|
|
tokio::select! {
|
|
result = ipc::run_server(PIPE_NAME) => {
|
|
if let Err(e) = result {
|
|
error!("Erro no servidor IPC: {}", e);
|
|
}
|
|
}
|
|
_ = async {
|
|
let _ = shutdown_rx.await;
|
|
} => {
|
|
info!("Shutdown solicitado");
|
|
}
|
|
}
|
|
});
|
|
|
|
// Atualiza status para Stopped
|
|
status_handle.set_service_status(ServiceStatus {
|
|
service_type: ServiceType::OWN_PROCESS,
|
|
current_state: ServiceState::Stopped,
|
|
controls_accepted: ServiceControlAccept::empty(),
|
|
exit_code: ServiceExitCode::Win32(0),
|
|
checkpoint: 0,
|
|
wait_hint: Duration::default(),
|
|
process_id: None,
|
|
})?;
|
|
|
|
info!("Servico parado");
|
|
Ok(())
|
|
}
|
|
|
|
fn run_standalone() -> Result<(), Box<dyn std::error::Error>> {
|
|
let runtime = tokio::runtime::Runtime::new()?;
|
|
|
|
runtime.block_on(async {
|
|
info!("Servidor IPC iniciando em modo standalone...");
|
|
|
|
tokio::select! {
|
|
result = ipc::run_server(PIPE_NAME) => {
|
|
if let Err(e) = result {
|
|
error!("Erro no servidor IPC: {}", e);
|
|
}
|
|
}
|
|
_ = tokio::signal::ctrl_c() => {
|
|
info!("Ctrl+C recebido, encerrando...");
|
|
}
|
|
}
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn install_service() -> Result<(), Box<dyn std::error::Error>> {
|
|
use windows_service::{
|
|
service::{ServiceAccess, ServiceErrorControl, ServiceInfo, ServiceStartType},
|
|
service_manager::{ServiceManager, ServiceManagerAccess},
|
|
};
|
|
|
|
info!("Instalando servico...");
|
|
|
|
let manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CREATE_SERVICE)?;
|
|
|
|
let exe_path = std::env::current_exe()?;
|
|
|
|
let service_info = ServiceInfo {
|
|
name: OsString::from(SERVICE_NAME),
|
|
display_name: OsString::from(SERVICE_DISPLAY_NAME),
|
|
service_type: ServiceType::OWN_PROCESS,
|
|
start_type: ServiceStartType::AutoStart,
|
|
error_control: ServiceErrorControl::Normal,
|
|
executable_path: exe_path,
|
|
launch_arguments: vec![],
|
|
dependencies: vec![],
|
|
account_name: None, // LocalSystem
|
|
account_password: None,
|
|
};
|
|
|
|
let service = manager.create_service(&service_info, ServiceAccess::CHANGE_CONFIG)?;
|
|
|
|
// Define descricao
|
|
service.set_description(SERVICE_DESCRIPTION)?;
|
|
|
|
info!("Servico instalado com sucesso: {}", SERVICE_NAME);
|
|
println!("Servico '{}' instalado com sucesso!", SERVICE_DISPLAY_NAME);
|
|
println!("Para iniciar: sc start {}", SERVICE_NAME);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn uninstall_service() -> Result<(), Box<dyn std::error::Error>> {
|
|
use windows_service::{
|
|
service::ServiceAccess,
|
|
service_manager::{ServiceManager, ServiceManagerAccess},
|
|
};
|
|
|
|
info!("Desinstalando servico...");
|
|
|
|
let manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CONNECT)?;
|
|
|
|
let service = manager.open_service(
|
|
SERVICE_NAME,
|
|
ServiceAccess::STOP | ServiceAccess::DELETE | ServiceAccess::QUERY_STATUS,
|
|
)?;
|
|
|
|
// Tenta parar o servico primeiro
|
|
let status = service.query_status()?;
|
|
if status.current_state != ServiceState::Stopped {
|
|
info!("Parando servico...");
|
|
let _ = service.stop();
|
|
std::thread::sleep(Duration::from_secs(2));
|
|
}
|
|
|
|
// Remove o servico
|
|
service.delete()?;
|
|
|
|
info!("Servico desinstalado com sucesso");
|
|
println!("Servico '{}' removido com sucesso!", SERVICE_DISPLAY_NAME);
|
|
|
|
Ok(())
|
|
}
|