-
-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(daemon): add daemon service for Windows #152
feat(daemon): add daemon service for Windows #152
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #152 +/- ##
=======================================
Coverage 36.94% 36.94%
=======================================
Files 23 23
Lines 720 720
Branches 2 2
=======================================
Hits 266 266
Misses 454 454
☔ View full report in Codecov by Sentry. |
… 10049 error on Windows
WalkthroughThe pull request introduces significant architectural changes to the server, adding OS-specific modules for managing service lifecycles on Windows, Linux, and macOS. It includes a new installation script for Debian packages, PowerShell scripts for installing, logging, and uninstalling the ClamAV Desktop service on Windows, and modifies the WebSocket connection URL in the dashboard commands for enhanced security. Changes
Recent review detailsConfiguration used: CodeRabbit UI Files ignored due to path filters (2)
Files selected for processing (6)
Additional comments not posted (4)
TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
@@ -4,7 +4,7 @@ set -e | |||
yarn release:deb | |||
# Allow `_apt` user to access the `deb` file | |||
mv ./src-tauri/target/release/bundle/deb/clamav-desktop_0.0.0_amd64.deb /tmp | |||
if dpkg -l | grep -qw twps-desktop; then | |||
if dpkg -l | grep -qw clamav-desktop; then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve error handling and verification after package removal.
The script continues execution even if the package removal fails due to the || true
usage. Consider adding a check after the removal command to ensure that the package was indeed removed before proceeding with the installation. This can prevent potential conflicts or issues during the installation of the new package.
# Check if the script is run as administrator, otherwise relaunch it with elevated privileges | ||
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { | ||
Write-Host "[uninstall_windows_service] Must be run as an administrator. Relaunching with elevated privileges..." | ||
|
||
# Relaunch script with elevated privileges using 'RunAs' | ||
Start-Process powershell "-ExecutionPolicy Bypass -File $PSCommandPath" -Verb RunAs | ||
|
||
exit | ||
} | ||
|
||
$serviceName = "clamav-desktop-daemon" | ||
$daemonBinaryPath = "C:\Program Files\ClamAV Desktop\clamav-desktop-daemon.exe" | ||
$daemonFolderPath = "C:\Program Files\ClamAV Desktop" | ||
|
||
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue | ||
if ($null -ne $service) { | ||
if ($service.Status -eq 'Running') { | ||
Stop-Service -Name $serviceName | ||
Write-Host "[uninstall_windows_service] Service '$serviceName' stopped successfully." | ||
} | ||
|
||
sc.exe delete $serviceName | ||
Write-Host "[uninstall_windows_service] Service '$serviceName' uninstalled successfully." | ||
} else { | ||
Write-Host "[uninstall_windows_service] Service '$serviceName' does not exist." | ||
} | ||
|
||
if (Test-Path $daemonFolderPath) { | ||
Remove-Item -Path $daemonFolderPath -Recurse -Force | ||
Write-Host "[uninstall_windows_service] Daemon binary and folder removed successfully." | ||
} else { | ||
Write-Host "[uninstall_windows_service] Daemon folder '$daemonFolderPath' does not exist." | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well-structured script with comprehensive handling of service uninstallation.
The script effectively checks and handles various states of the service and associated files. Consider enhancing the user messages to include more details about what the script is doing at each step, which could improve the user's understanding and troubleshooting capabilities.
# Check if the script is run as administrator, otherwise relaunch it with elevated privileges | ||
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { | ||
Write-Host "[install_windows_service] Must be run as an administrator. Relaunching with elevated privileges..." | ||
|
||
# Relaunch script with elevated privileges using 'RunAs' | ||
Start-Process powershell "-ExecutionPolicy Bypass -File $PSCommandPath" -Verb RunAs | ||
|
||
exit | ||
} | ||
|
||
$daemonBinaryPath = "C:\Program Files\ClamAV Desktop\clamav-desktop-daemon.exe" | ||
$serviceName = "clamav-desktop-daemon" | ||
|
||
if (-Not (Test-Path ".\daemon\target\debug\clamav-desktop-daemon.exe")) { | ||
throw "[install_windows_service] Error: Daemon binary not found. Make sure it is built and available in ./daemon/target/debug/." | ||
} | ||
|
||
if (-Not (Test-Path "C:\Program Files\ClamAV Desktop")) { | ||
New-Item -Path "C:\Program Files\ClamAV Desktop" -ItemType Directory | ||
} | ||
|
||
Copy-Item -Path ".\daemon\target\debug\clamav-desktop-daemon.exe" -Destination $daemonBinaryPath -Force | ||
|
||
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue | ||
if ($null -ne $service) { | ||
throw "[install_windows_service] Error: Service '$serviceName' already exists. Installation aborted." | ||
} | ||
|
||
sc.exe create $serviceName binPath= $daemonBinaryPath start= auto DisplayName= "ClamAV Desktop Daemon" | ||
Write-Host "[install_windows_service] Service '$serviceName' installed successfully." | ||
|
||
Start-Service -Name $serviceName | ||
if ($?) { | ||
Write-Host "[install_windows_service] Service '$serviceName' started successfully." | ||
} else { | ||
Write-Host "[install_windows_service] Failed to start the service '$serviceName'." | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well-structured script with comprehensive handling of service installation.
The script effectively checks and handles various states of the service and associated files. Consider adding retry mechanisms or more detailed error handling for scenarios where the service fails to start, which could improve robustness and troubleshooting capabilities.
# Service name | ||
$serviceName = "clamav-desktop-daemon" | ||
|
||
# Display the status of the service | ||
Write-Host "Fetching status of service '$serviceName'..." | ||
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue | ||
|
||
if ($null -ne $service) { | ||
$serviceStatus = Get-Service -Name $serviceName | ||
Write-Host "[log_windows_service] Service '$serviceName' is currently $($serviceStatus.Status)." | ||
} else { | ||
Write-Host "[log_windows_service] Service '$serviceName' does not exist." | ||
|
||
exit | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Service status fetching is robust.
The script robustly fetches and displays the status of the service, handling cases where the service might not exist. This ensures that the script can be used reliably in various states of the service lifecycle.
Consider adding more detailed logging for different service states or errors to enhance troubleshooting capabilities.
# Fetch logs from Windows Event Viewer and continuously monitor new ones | ||
Write-Host "Tailing logs for service '$serviceName'... (Press Ctrl+C to stop)" | ||
$lastTime = (Get-Date).AddMinutes(-5) # Start by fetching logs from the last 5 minutes | ||
|
||
while ($true) { | ||
# Fetch the most recent logs | ||
$eventLogs = Get-WinEvent -LogName System -FilterXPath "*[System[Provider[@Name='$serviceName'] and TimeCreated[timediff(@SystemTime) <= 300000]]]" -ErrorAction SilentlyContinue | ||
|
||
# Display new logs if any | ||
if ($eventLogs) { | ||
$eventLogs | Format-Table TimeCreated, Message -AutoSize | ||
} else { | ||
Write-Host "[log_windows_service] No new logs..." | ||
} | ||
|
||
# Update the time to only fetch new logs in the next loop | ||
$lastTime = (Get-Date) | ||
Start-Sleep -Seconds 5 # Poll every 5 seconds | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Log fetching and monitoring implemented effectively.
The script effectively fetches and displays logs from the Windows Event Viewer, continuously monitoring for new logs. This functionality is crucial for real-time monitoring and troubleshooting.
To improve performance, consider adjusting the polling interval based on the frequency of log updates or implementing a more event-driven approach if supported by the system.
|
||
fn my_service_main(_arguments: Vec<OsString>) { | ||
if let Err(e) = run_service() { | ||
eprintln!("Service failed: {}", e); | ||
} | ||
} | ||
|
||
fn run_service() -> windows_service::Result<()> { | ||
let (shutdown_tx, shutdown_rx) = mpsc::channel::<()>(); | ||
|
||
let event_handler = { | ||
let shutdown_tx = shutdown_tx.clone(); | ||
move |control_event| -> service_control_handler::ServiceControlHandlerResult { | ||
match control_event { | ||
ServiceControl::Stop => { | ||
// Report that the service is stopping | ||
shutdown_tx.send(()).unwrap(); | ||
service_control_handler::ServiceControlHandlerResult::NoError | ||
} | ||
ServiceControl::Interrogate => { | ||
// Service is running, report no error | ||
service_control_handler::ServiceControlHandlerResult::NoError | ||
} | ||
_ => service_control_handler::ServiceControlHandlerResult::NotImplemented, | ||
} | ||
} | ||
}; | ||
|
||
// Register the control handler with Windows SCM | ||
let status_handle = | ||
service_control_handler::register("clamav-desktop-daemon", event_handler)?; | ||
|
||
// Report service is starting | ||
let start_pending_status = ServiceStatus { | ||
service_type: ServiceType::OWN_PROCESS, | ||
current_state: ServiceState::StartPending, | ||
controls_accepted: ServiceControlAccept::STOP, | ||
process_id: None, | ||
exit_code: ServiceExitCode::Win32(0), | ||
checkpoint: 0, | ||
wait_hint: Duration::from_secs(30), // Hinting SCM that it will take time to start | ||
}; | ||
status_handle.set_service_status(start_pending_status)?; | ||
|
||
// Start the Tokio runtime | ||
let rt = Runtime::new().unwrap(); | ||
rt.block_on(async { | ||
// Start the server asynchronously | ||
tokio::spawn(async { | ||
start_server().await; | ||
}); | ||
}); | ||
|
||
// Report service is now running | ||
let running_status = ServiceStatus { | ||
service_type: ServiceType::OWN_PROCESS, | ||
current_state: ServiceState::Running, | ||
controls_accepted: ServiceControlAccept::STOP, | ||
process_id: None, | ||
exit_code: ServiceExitCode::Win32(0), | ||
checkpoint: 0, | ||
wait_hint: Duration::from_secs(0), | ||
}; | ||
status_handle.set_service_status(running_status)?; | ||
|
||
// Block until we receive a shutdown signal | ||
shutdown_rx.recv().unwrap(); | ||
|
||
// Report service is stopping | ||
let stop_pending_status = ServiceStatus { | ||
service_type: ServiceType::OWN_PROCESS, | ||
current_state: ServiceState::StopPending, | ||
controls_accepted: ServiceControlAccept::empty(), | ||
process_id: None, | ||
exit_code: ServiceExitCode::Win32(0), | ||
checkpoint: 0, | ||
wait_hint: Duration::from_secs(30), | ||
}; | ||
status_handle.set_service_status(stop_pending_status)?; | ||
|
||
// Properly clean up and stop the service | ||
let stop_status = ServiceStatus { | ||
service_type: ServiceType::OWN_PROCESS, | ||
current_state: ServiceState::Stopped, | ||
controls_accepted: ServiceControlAccept::empty(), | ||
process_id: None, | ||
exit_code: ServiceExitCode::Win32(0), | ||
checkpoint: 0, | ||
wait_hint: Duration::from_secs(0), | ||
}; | ||
status_handle.set_service_status(stop_status)?; | ||
|
||
Ok(()) | ||
} | ||
|
||
pub fn run() -> Result<()> { | ||
service_dispatcher::start("clamav-desktop-daemon", ffi_service_main)?; | ||
|
||
Ok(()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Windows service handler module is robustly implemented.
The use of the windows_service
crate to define and manage the Windows service is appropriate. The module handles service control events effectively and uses a Tokio runtime to ensure the server runs asynchronously.
Consider adding more detailed error handling and logging, especially around service control events, to enhance troubleshooting and maintainability.
Description
Adding dev service installation for
clamav-desktop-daemon
for Windows.Checklist