mailwolt/app/Livewire/Ui/Security/ClamavManager.php

182 lines
5.5 KiB
PHP

<?php
namespace App\Livewire\Ui\Security;
use App\Support\CacheVer;
use Illuminate\Support\Facades\Cache;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;
#[Layout('layouts.dvx')]
#[Title('Virenschutz · Mailwolt')]
class ClamavManager extends Component
{
public bool $running = false;
public bool $enabled = false;
public string $dbDate = '—';
public string $dbVersion = '—';
public ?int $ramMb = null;
public string $lastError = '';
public string $lastSuccess = '';
public bool $starting = false;
public int $startSecs = 0;
private const STARTING_FLAG = '/tmp/mw-clamav-starting';
public function mount(): void
{
$this->refresh();
$this->restoreStartingState();
}
private function restoreStartingState(): void
{
if (!file_exists(self::STARTING_FLAG)) return;
if ($this->running) {
@unlink(self::STARTING_FLAG);
return;
}
$elapsed = time() - (int) @file_get_contents(self::STARTING_FLAG);
if ($elapsed > 180) {
@unlink(self::STARTING_FLAG);
return;
}
$this->starting = true;
$this->startSecs = max(0, $elapsed);
}
public function refresh(): void
{
$this->lastError = '';
$this->lastSuccess = '';
$this->running = $this->serviceActive();
$this->enabled = $this->serviceEnabled();
$this->ramMb = $this->running ? $this->readRamMb() : null;
[$this->dbDate, $this->dbVersion] = $this->readDbInfo();
}
public function enable(): void
{
$this->lastError = '';
$this->lastSuccess = '';
[$ok, $msg] = $this->runCmd('enable');
if (!$ok) {
$this->lastError = $msg;
return;
}
file_put_contents(self::STARTING_FLAG, time());
$this->enabled = true;
$this->starting = true;
$this->startSecs = 0;
}
public function pollStatus(): void
{
if (!$this->starting) return;
$this->startSecs += 3;
$this->running = $this->serviceActive();
if ($this->running) {
@unlink(self::STARTING_FLAG);
$this->starting = false;
$this->startSecs = 0;
$this->enabled = $this->serviceEnabled();
$this->ramMb = $this->readRamMb();
$this->lastSuccess = 'ClamAV ist aktiv und läuft.';
Cache::forget(CacheVer::k('health:services'));
}
}
public function disable(): void
{
[$ok, $msg] = $this->runCmd('disable');
if ($ok) $this->lastSuccess = 'ClamAV wurde gestoppt und deaktiviert.';
else $this->lastError = $msg;
$this->running = $this->serviceActive();
$this->enabled = $this->serviceEnabled();
Cache::forget(CacheVer::k('health:services'));
}
public function updateDb(): void
{
[$ok, $msg] = $this->runCmd('freshclam');
if ($ok) $this->lastSuccess = 'Virensignaturen wurden aktualisiert.';
else $this->lastError = $msg;
[$this->dbDate, $this->dbVersion] = $this->readDbInfo();
}
private function runCmd(string $action): array
{
$out = []; $rc = null;
exec('sudo -n /usr/local/sbin/mailwolt-clamav ' . escapeshellarg($action) . ' 2>&1', $out, $rc);
\Log::info('ClamAV ' . $action, ['rc' => $rc, 'out' => $out]);
$msg = implode(' ', $out) ?: 'Unbekannter Fehler (rc=' . $rc . ')';
return [$rc === 0, $msg];
}
private function isInstalled(): bool
{
return file_exists('/usr/sbin/clamd')
|| file_exists('/lib/systemd/system/clamav-daemon.service')
|| file_exists('/usr/lib/systemd/system/clamav-daemon.service');
}
private function serviceActive(): bool
{
$exit = null;
@exec('systemctl is-active --quiet clamav-daemon 2>/dev/null', $_, $exit);
return $exit === 0;
}
private function serviceEnabled(): bool
{
$exit = null;
@exec('systemctl is-enabled --quiet clamav-daemon 2>/dev/null', $_, $exit);
return $exit === 0;
}
private function readRamMb(): ?int
{
$out = [];
@exec("ps -C clamd -o rss= 2>/dev/null", $out);
$kb = array_sum(array_map('intval', array_filter($out)));
return $kb > 0 ? (int) round($kb / 1024) : null;
}
private function readDbInfo(): array
{
$paths = [
'/var/lib/clamav/main.cvd',
'/var/lib/clamav/main.cld',
'/var/lib/clamav/daily.cvd',
'/var/lib/clamav/daily.cld',
];
$latest = 0;
foreach ($paths as $p) {
if (@file_exists($p)) {
$mtime = @filemtime($p);
if ($mtime > $latest) $latest = $mtime;
}
}
if ($latest === 0) return ['—', '—'];
$date = date('d.m.Y H:i', $latest);
$ver = '—';
$out = [];
@exec('sigtool --info /var/lib/clamav/daily.cld 2>/dev/null | grep "^Version:" | head -1', $out);
if (empty($out)) {
@exec('sigtool --info /var/lib/clamav/daily.cvd 2>/dev/null | grep "^Version:" | head -1', $out);
}
if (!empty($out[0])) {
$ver = trim(str_replace('Version:', '', $out[0]));
}
return [$date, $ver];
}
public function render()
{
return view('livewire.ui.security.clamav-manager');
}
}