Skip to content

Commit

Permalink
feat(core/cloud): link freshclam sidecar to start_cloud_update() for …
Browse files Browse the repository at this point in the history
…Windows case
  • Loading branch information
ivangabriele committed Aug 26, 2024
1 parent b1492dd commit a2fea62
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 96 deletions.
185 changes: 123 additions & 62 deletions src-tauri/src/cloud/commands.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{env, process::Stdio};
use tauri::{AppHandle, Manager, State};
use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::process::Command;
use tokio::process::Command as TokioCommand;

use crate::debug;

Expand Down Expand Up @@ -38,90 +38,151 @@ pub async fn start_cloud_update(
app_handle: AppHandle,
shared_state: State<'_, state::CloudSharedState>,
) -> Result<(), ()> {
use tauri::api::process::{Command, CommandEvent};

debug!("start_cloud_update()", "Command call.");
println!("1");

let is_dev_mode = env::var("TAURI_DEV").is_ok();

// Update cloud state
let mut public_state_mutex_guard = shared_state.0.public.lock().await;
public_state_mutex_guard.is_running = true;
app_handle
.emit_all("cloud:state", &public_state_mutex_guard.clone())
.unwrap();

let mut command = if is_dev_mode {
let mut cmd = Command::new("pkexec");
cmd.args(["freshclam", "--daemon-notify"]);
cmd
} else {
let mut cmd = Command::new("freshclam");
cmd.args(["--daemon-notify"]);
cmd
};
if cfg!(target_os = "linux") {
println!("2");
// Update cloud state
let mut public_state_mutex_guard = shared_state.0.public.lock().await;
public_state_mutex_guard.is_running = true;
app_handle
.emit_all("cloud:state", &public_state_mutex_guard.clone())
.unwrap();

let mut child = command
.stderr(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.expect("Failed to spawn child process.");

let stdout = child
.stdout
.take()
.expect("Failed to attach standard output.");

let app_handle_clone_for_stdout = app_handle.clone();
let app_handle_clone_for_end = app_handle.clone();
tokio::spawn(async move {
let reader = BufReader::new(stdout);
let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
#[cfg(debug_assertions)]
{
println!("[libs::cli::run()] {}", line);
let mut command = if is_dev_mode {
let mut cmd = TokioCommand::new("pkexec");
cmd.args(["freshclam", "--daemon-notify"]);
cmd
} else {
let mut cmd = TokioCommand::new("freshclam");
cmd.args(["--daemon-notify"]);
cmd
};

let mut child = command
.stderr(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.expect("Failed to spawn child process.");

let stdout = child
.stdout
.take()
.expect("Failed to attach standard output.");

let app_handle_clone_for_stdout = app_handle.clone();
let app_handle_clone_for_end = app_handle.clone();
tokio::spawn(async move {
let reader = BufReader::new(stdout);
let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
#[cfg(debug_assertions)]
{
println!("[libs::cli::run()] {}", line);
}

let mut public_state_mutex_guard = app_handle_clone_for_stdout
.state::<state::CloudSharedState>()
.inner()
.0
.public
.lock()
.await;
public_state_mutex_guard.logs.push(line);

app_handle_clone_for_stdout
.emit_all("cloud:state", &public_state_mutex_guard.clone())
.unwrap();
}

let mut public_state_mutex_guard = app_handle_clone_for_stdout
let _ = child.wait().await.expect("Failed to wait for child exit.");

// Update the state to indicate the process is no longer running
let mut public_state_mutex_guard = app_handle_clone_for_end
.state::<state::CloudSharedState>()
.inner()
.0
.public
.lock()
.await;
public_state_mutex_guard.logs.push(line);
public_state_mutex_guard.is_running = false;

app_handle_clone_for_stdout
// Updated cloud state
app_handle_clone_for_end
.emit_all("cloud:state", &public_state_mutex_guard.clone())
.unwrap();
}

let _ = child.wait().await.expect("Failed to wait for child exit.");

// Update the state to indicate the process is no longer running
let mut public_state_mutex_guard = app_handle_clone_for_end
.state::<state::CloudSharedState>()
.inner()
.0
.public
.lock()
.await;
public_state_mutex_guard.is_running = false;

// Updated cloud state
app_handle_clone_for_end
.emit_all("cloud:state", &public_state_mutex_guard.clone())
.unwrap();
});
});

return Ok(());
}

if cfg!(target_os = "macos") {
println!("3");
return Ok(());
}

if cfg!(target_os = "windows") {
println!("4");

let (mut rx, _child) = Command::new_sidecar("freshclam")
.expect("failed to create `freshclam` binary command")
.spawn()
.expect("Failed to spawn sidecar");

let app_handle_clone_for_stdout = app_handle.clone();
tauri::async_runtime::spawn(async move {
// read events such as stdout
while let Some(event) = rx.recv().await {
let mut public_state_mutex_guard = app_handle_clone_for_stdout
.state::<state::CloudSharedState>()
.inner()
.0
.public
.lock()
.await;

if let CommandEvent::Stdout(ref line) = event {
#[cfg(debug_assertions)]
{
println!("[CommandEvent::Stdout] {}", line);
}

public_state_mutex_guard.logs.push(line.to_string());
}

if let CommandEvent::Stderr(ref line) = event {
#[cfg(debug_assertions)]
{
println!("[CommandEvent::Stderr] {}", line);
}

public_state_mutex_guard.logs.push(line.to_string());
}

app_handle_clone_for_stdout
.emit_all("cloud:state", &public_state_mutex_guard.clone())
.unwrap();
}
});

Ok(())
return Ok(());
}
println!("5");

panic!("Unsupported OS.");
}

#[cfg(not(tarpaulin_include))]
#[tauri::command]
pub fn start_cloud_daemon() -> () {
debug!("start_cloud_daemon()", "Command call.");

Command::new("systemctl")
TokioCommand::new("systemctl")
.args(["start", "clamav-freshclam"])
.stderr(Stdio::piped())
.stdout(Stdio::piped())
Expand All @@ -134,7 +195,7 @@ pub fn start_cloud_daemon() -> () {
pub fn stop_cloud_daemon() -> () {
debug!("stop_cloud_daemon()", "Command call.");

Command::new("systemctl")
TokioCommand::new("systemctl")
.args(["stop", "clamav-freshclam"])
.stderr(Stdio::piped())
.stdout(Stdio::piped())
Expand Down
78 changes: 45 additions & 33 deletions src/screens/Cloud.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { invoke } from '@tauri-apps/api'
import { listen } from '@tauri-apps/api/event'
import { useCallback, useEffect, useRef } from 'react'

import styled from 'styled-components'
import { Button } from '../elements/Button'
import { Logger } from '../elements/Logger'
import { useCachedState } from '../hooks/useCachedState'
Expand Down Expand Up @@ -37,7 +38,7 @@ export function Cloud() {

timerRef.current = window.setInterval(() => {
invoke('get_cloud_state')
}, 500)
}, 50000)

return () => {
if (timerRef.current) {
Expand All @@ -50,39 +51,50 @@ export function Cloud() {
<Screen isLoading={isLoading}>
<Logger hasForcedScroll>{logsAsString}</Logger>

{!!state && state.daemon_status === Core.CloudDaemonStatus.RUNNING && (
<>
<Button data-testid="cloud__button" onClick={stopCloudDaemon} style={{ marginTop: 16 }}>
Stop Cloud Daemon
<ActionsBox>
{!!state && state.status === Core.CloudDaemonStatus.RUNNING && (
<>
<Button data-testid="cloud__button" onClick={stopCloudDaemon} style={{ marginTop: 16 }}>
Stop Cloud Daemon
</Button>
</>
)}
{!!state && state.status === Core.CloudDaemonStatus.STOPPED && (
<Button data-testid="cloud__button" onClick={startCloudDaemon} style={{ marginTop: 16 }}>
Start Cloud Daemon
</Button>
</>
)}
{!!state && state.daemon_status === Core.CloudDaemonStatus.STOPPED && (
<Button data-testid="cloud__button" onClick={startCloudDaemon} style={{ marginTop: 16 }}>
Start Cloud Daemon
</Button>
)}
{!!state && state.daemon_status === Core.CloudDaemonStatus.UNKNOWN && (
<Button data-testid="cloud__button" disabled style={{ marginTop: 16 }}>
Loading...
</Button>
)}
{!!state && state.is_running ? (
<Button data-testid="cloud__button" disabled style={{ marginTop: 16 }}>
Updating Virus Database...
</Button>
) : (
<Button
data-testid="cloud__button"
disabled={!state || state.daemon_status !== Core.CloudDaemonStatus.STOPPED}
onClick={startCloudUpdate}
style={{ marginTop: 16 }}
>
{state?.daemon_status === Core.CloudDaemonStatus.STOPPED
? 'Update Virus Database'
: 'Stop the Cloud Daemon first if you want to update manually'}
</Button>
)}
)}
{!!state && state.status === Core.CloudDaemonStatus.UNKNOWN && (
<Button data-testid="cloud__button" disabled style={{ marginTop: 16 }}>
Loading...
</Button>
)}
{!!state && state.is_running ? (
<Button data-testid="cloud__button" disabled style={{ marginTop: 16 }}>
Updating Virus Database...
</Button>
) : (
<Button
data-testid="cloud__button"
disabled={!state || state.status !== Core.CloudDaemonStatus.STOPPED}
onClick={startCloudUpdate}
style={{ marginTop: 16 }}
>
{state?.status === Core.CloudDaemonStatus.STOPPED
? 'Update Virus Database'
: 'Stop the Cloud Daemon first if you want to update manually'}
</Button>
)}
</ActionsBox>
</Screen>
)
}

const ActionsBox = styled.div`
display: flex;
gap: 16px;
> button {
flex: 0.5;
}
`
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ export namespace Core {
// Cloud

export type CloudState = {
daemon_status: CloudDaemonStatus
is_ready: boolean
is_running: boolean
logs: string[]
status: CloudDaemonStatus
}

export enum CloudDaemonStatus {
Expand Down

0 comments on commit a2fea62

Please sign in to comment.