diff --git a/src/main/kotlin/ButtonListener.kt b/src/main/kotlin/ButtonListener.kt index 7fe9da1..4aa9077 100644 --- a/src/main/kotlin/ButtonListener.kt +++ b/src/main/kotlin/ButtonListener.kt @@ -1,7 +1,7 @@ package ru.thiflict import kotlinx.coroutines.* -import ru.thiflict.DebugMode.DEBUG +import ru.thiflict.DebugMode.isDebug import ru.thiflict.PinConfig.BUTTON import java.io.BufferedReader import java.io.InputStreamReader @@ -26,10 +26,6 @@ class ButtonListener( clickListeners.add(listener) } - fun removeClickListener(listener: ButtonClickListener) { - clickListeners.remove(listener) - } - private fun notifyClick() { val event = ButtonClickEvent() clickListeners.forEach { it.invoke(event) } @@ -55,25 +51,25 @@ class ButtonListener( var line: String? while (reader.readLine().also { line = it } != null) { - println(if (DEBUG) "$line" else "") + println(if (isDebug) "$line" else "") when { line?.contains("rising") == true -> { currentButtonState.set(true) onStateChange?.invoke(true) - println(if (DEBUG) "Кнопка нажата" else "") + println(if (isDebug) "Кнопка нажата" else "") // Генерируем событие клика при нажатии notifyClick() } line?.contains("falling") == true -> { currentButtonState.set(false) onStateChange?.invoke(false) - println(if (DEBUG) "Кнопка разжата" else "") + println(if (isDebug) "Кнопка разжата" else "") // Убрана проверка на разжатие для генерации события } } } val exitCode = process!!.waitFor() - println("Процесс завершён ($exitCode)") + println(if (isDebug) "Процесс завершён ($exitCode)" else "") isListening.set(false) } } catch (e: Exception) { @@ -82,10 +78,4 @@ class ButtonListener( } return currentButtonState.get() } - - fun stopListening() { - listenerJob?.cancel() - process?.destroy() - isListening.set(false) - } } \ No newline at end of file diff --git a/src/main/kotlin/GPIOController.kt b/src/main/kotlin/GPIOController.kt index eda64cf..f4278fc 100644 --- a/src/main/kotlin/GPIOController.kt +++ b/src/main/kotlin/GPIOController.kt @@ -1,16 +1,9 @@ package ru.thiflict -import kotlinx.coroutines.delay -import ru.thiflict.PinConfig -import java.io.File -import kotlin.system.exitProcess +import ru.thiflict.DebugMode.isDebug class GPIOController(private val pin: Int) { - fun setDirection(direction: String) { - // Для libgpiod направление задаётся автоматически - } - fun setValue(value: Int) { try { // Пробуем разные варианты синтаксиса gpioset @@ -27,7 +20,7 @@ class GPIOController(private val pin: Int) { } } catch (e: Exception) { - println("Ошибка установки GPIO $pin = $value: ${e.message}") + println(if (isDebug) "Ошибка установки GPIO $pin = $value: ${e.message}" else "") } } @@ -41,7 +34,7 @@ class GPIOController(private val pin: Int) { process.waitFor() output.toIntOrNull() ?: 0 } catch (e: Exception) { - println("Ошибка чтения GPIO $pin: ${e.message}") + println(if (isDebug) "Ошибка чтения GPIO $pin: ${e.message}" else "") 0 } } @@ -51,5 +44,4 @@ class GPIOController(private val pin: Int) { } -} - +} \ No newline at end of file diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 42a8111..ba880e5 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -1,9 +1,13 @@ package ru.thiflict import kotlinx.coroutines.* -import ru.thiflict.DebugMode.DEBUG +import ru.thiflict.DebugMode.isDebug +import java.io.BufferedReader import java.io.File +import java.io.InputStreamReader import kotlin.system.exitProcess +import java.util.* +import java.util.concurrent.* // Конфигурация GPIO (BCM номера) object PinConfig { @@ -14,7 +18,11 @@ object PinConfig { } object DebugMode { - const val DEBUG = true + var isDebug = false + + fun enableDebugging() { + isDebug = true + } } // Состояния системы @@ -32,12 +40,11 @@ class USBVirusScanner { private lateinit var greenLed: GPIOController private lateinit var yellowLed: GPIOController private lateinit var redLed: GPIOController - private lateinit var buttonReader: ButtonListener private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) fun initialize() { - println(if (DEBUG) "Инициализация..." else "") + println(if (isDebug) "Инициализация..." else "") try { // Проверяем доступность gpiod @@ -54,16 +61,18 @@ class USBVirusScanner { // Запускаем индикацию состояния startStateIndication() - currentState = SystemState.IDLE + + antivirusDatabaseUpdater() + Thread.sleep(1000) - println("Инициализация завершена.") + println(if (isDebug) "Инициализация завершена." else "") currentState = SystemState.IDLE } catch (e: Exception) { currentState = SystemState.ERROR - println("❌ Ошибка инициализации: ${e.message}") + println(if (isDebug) "❌ Ошибка инициализации: ${e.message}" else "") e.printStackTrace() throw e } @@ -75,23 +84,23 @@ class USBVirusScanner { val exitCode = process.waitFor() if (exitCode != 0) { - throw Exception("gpiodetect не найден") + throw Exception(if (isDebug) "gpiodetect не найден" else "") } val output = process.inputStream.bufferedReader().readText() - println("✅ libgpiod найден") + println(if (isDebug) "✅ libgpiod найден" else "") if (output.isNotBlank()) { - println("Доступные чипы: ${output.trim()}") + println(if (isDebug) "Доступные чипы: ${output.trim()}" else "") } } catch (e: Exception) { - throw Exception("libgpiod Не установлен!") + throw Exception(if (isDebug) "libgpiod Не установлен!" else "") } } fun buttonPress(defaultButtonState: Boolean): Boolean { var finalButtonState = defaultButtonState - println(if (DEBUG) "Начало finalButtonState: $finalButtonState" else "") + println(if (isDebug) "Начало finalButtonState: $finalButtonState" else "") val buttonListener = ButtonListener() val initialState = buttonListener.startListening { isPressed -> @@ -99,7 +108,7 @@ class USBVirusScanner { finalButtonState = true } } - println(if (DEBUG) "finalButtonState: $finalButtonState" else "") + println(if (isDebug) "finalButtonState: $finalButtonState" else "") return finalButtonState } @@ -164,27 +173,27 @@ class USBVirusScanner { fun startScan() { if (scanJob?.isActive == true) { - println("Сканирование уже выполняется") + println(if (isDebug) "Сканирование уже выполняется" else "") return } scanJob = scope.launch { currentState = SystemState.SCANNING - println("=== Начало сканирования ===") + println(if (isDebug) "=== Начало сканирования ===" else "") try { // Поиск USB-устройств val usbDevices = findUSBDevices() if (usbDevices.isEmpty()) { - println("Устройства не найдены") + println(if (isDebug) "Устройства не найдены" else "") currentState = SystemState.ERROR delay(5000) currentState = SystemState.IDLE return@launch } - println("📀 Найдены USB-устройства:") + println(if (isDebug) "📀 Найдены USB-устройства:" else "") usbDevices.forEach { device -> println(" - ${device.mountPoint}") } @@ -194,23 +203,23 @@ class USBVirusScanner { val scanResults = mutableListOf() for (device in usbDevices) { - println("🔍 Сканирование: ${device.mountPoint}") + println(if (isDebug) "🔍 Сканирование: ${device.mountPoint}" else "") val result = scanWithClamAV(device.mountPoint) scanResults.add(result) if (result.isInfected) { hasVirus = true - println("⚠️ ВНИМАНИЕ! Найдены угрозы!") + println(if (isDebug) "⚠️ ВНИМАНИЕ! Найдены угрозы!" else "") result.infectedFiles.take(10).forEach { file -> println(" 🦠 ${file.take(100)}") } if (result.infectedFiles.size > 10) { - println(" ... и ещё ${result.infectedFiles.size - 10} файлов") + println(if (isDebug) " ... и ещё ${result.infectedFiles.size - 10} файлов" else "") } } else if (result.error != null) { - println("❌ Ошибка сканирования: ${result.error}") + println(if (isDebug) "❌ Ошибка сканирования: ${result.error}" else "") } else { - println("✅ Сканирование завершено: угроз не найдено") + println(if (isDebug) "✅ Сканирование завершено: угроз не найдено" else "") } println() } @@ -225,25 +234,25 @@ class USBVirusScanner { } val message = if (hasVirus) { - "🦠 Найдены вирусы! Красный LED горит" + if (isDebug) "🦠 Найдены вирусы! Красный LED горит" else "" } else { - "✅ Флешка чиста! Зелёный LED горит" + if (isDebug) "✅ Флешка чиста! Зелёный LED горит" else "" } - println(message) + println(if (isDebug) message else "") // Держим состояние 5 секунд delay(5000) currentState = SystemState.IDLE } catch (e: Exception) { - println("❌ Критическая ошибка: ${e.message}") + println(if (isDebug) "❌ Критическая ошибка: ${e.message}" else "") e.printStackTrace() currentState = SystemState.ERROR delay(5000) currentState = SystemState.IDLE } - println("=== Сканирование завершено ===\n") + println(if (isDebug) "=== Сканирование завершено ===\n" else "") } } @@ -295,7 +304,7 @@ class USBVirusScanner { } } catch (e: Exception) { - println("Ошибка при поиске USB-устройств: ${e.message}") + println(if (isDebug) "Ошибка при поиске USB-устройств: ${e.message}" else "") } return devices.distinctBy { it.mountPoint } @@ -316,7 +325,7 @@ class USBVirusScanner { infectedFiles = emptyList(), virusNames = emptyMap(), exitCode = -1, - error = "ClamAV не установлен. Установите: sudo apt install clamav" + error = if (isDebug) "ClamAV не установлен. Установите: sudo apt install clamav" else "" ) } @@ -328,11 +337,11 @@ class USBVirusScanner { infectedFiles = emptyList(), virusNames = emptyMap(), exitCode = -1, - error = "Путь не существует или не является директорией: $path" + error = if (isDebug)"Путь не существует или не является директорией: $path" else "" ) } - println(" Запуск clamscan (это может занять некоторое время)...") + println(if (isDebug) " Запуск clamscan (это может занять некоторое время)..." else "") // Запускаем ClamAV val processBuilder = ProcessBuilder( @@ -340,8 +349,8 @@ class USBVirusScanner { "-r", "--stdout", "--infected", - "--max-filesize=70M", - "--max-scansize=150M", + "--max-filesize=1M", + "--max-scansize=10M", path ) @@ -364,30 +373,22 @@ class USBVirusScanner { } } - val syncProccessBuilder = ProcessBuilder("sync") - syncProccessBuilder.start() - process.waitFor() - - val umountProcessBulder = ProcessBuilder("umount", "/dev/sda", "-f") - umountProcessBulder.start() - process.waitFor() - return ScanResult( isInfected = infectedFiles.isNotEmpty(), infectedFiles = infectedFiles, virusNames = virusNames, exitCode = exitCode, - error = if (exitCode != 0 && exitCode != 1) "ClamAV вернул код ошибки: $exitCode" else null + error = if (isDebug) (if (exitCode != 0 && exitCode != 1) "ClamAV вернул код ошибки: $exitCode" else "") else "" ) } catch (e: Exception) { - println(" Ошибка при выполнении clamscan: ${e.message}") + println(if (isDebug) " Ошибка при выполнении clamscan: ${e.message}" else "") return ScanResult( isInfected = false, infectedFiles = emptyList(), virusNames = emptyMap(), exitCode = -1, - error = e.message + error = if (isDebug) e.message else "" ) } } @@ -427,14 +428,14 @@ class USBVirusScanner { } } - println("📝 Лог сохранён: ${logFile.absolutePath}") + println(if (isDebug) "📝 Лог сохранён: ${logFile.absolutePath}" else "") } catch (e: Exception) { - println("Ошибка сохранения лога: ${e.message}") + println(if (isDebug) "Ошибка сохранения лога: ${e.message}" else "") } } fun shutdown() { - println("\nЗавершение работы...") + println(if (isDebug) "\nЗавершение работы..." else "") isRunning = false indicationJob?.cancel() @@ -453,7 +454,7 @@ class USBVirusScanner { // Игнорируем ошибки при закрытии } - println("✅ Система остановлена") + println(if (isDebug) "✅ Система остановлена" else "") } } @@ -470,14 +471,63 @@ data class ScanResult( val error: String? = null ) +fun scheduleUpdaterAt3AM() { + val scheduler = Executors.newSingleThreadScheduledExecutor() + val now = Calendar.getInstance() + val targetTime = Calendar.getInstance().apply { + set(Calendar.HOUR_OF_DAY, 3) + set(Calendar.MINUTE, 0) + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + } -fun main() { + var initialDelay = targetTime.timeInMillis - now.timeInMillis - println(if (DebugMode.DEBUG) "Зелёный - ${PinConfig.GREEN_LED}" else "") - println(if (DebugMode.DEBUG) "Жёлтый - ${PinConfig.YELLOW_LED}" else "") - println(if (DebugMode.DEBUG) "Красный - ${PinConfig.RED_LED}" else "") - println(if (DebugMode.DEBUG) "Кнопка - ${PinConfig.BUTTON}" else "") + // Если время уже прошло сегодня, планируем на завтра + if (initialDelay <= 0) { + targetTime.add(Calendar.DAY_OF_MONTH, 1) + initialDelay = targetTime.timeInMillis - now.timeInMillis + } + + scheduler.scheduleAtFixedRate( + { antivirusDatabaseUpdater() }, + initialDelay, + TimeUnit.DAYS.toMillis(1), + TimeUnit.MILLISECONDS + ) + + println("Планировщик запущен. Первый запуск через ${initialDelay / 1000 / 60} минут") +} + +fun antivirusDatabaseUpdater() { + println(if (isDebug) "Обновление базы данных антивируса" else "") + val processBuilder = ProcessBuilder("freshclam", "-vvv") + + processBuilder.redirectErrorStream(true) + + val process = processBuilder.start() + val reader = BufferedReader(InputStreamReader(process.inputStream)) + if (isDebug) { + reader.useLines { lines -> + lines.forEach { println("Process output: $it") } + } + } + process.waitFor() +} + +fun main(args: Array) { + + if (args.contains("--debug")) { + DebugMode.enableDebugging() + } + + scheduleUpdaterAt3AM() + + println(if (isDebug) "Зелёный - ${PinConfig.GREEN_LED}" else "") + println(if (isDebug) "Жёлтый - ${PinConfig.YELLOW_LED}" else "") + println(if (isDebug) "Красный - ${PinConfig.RED_LED}" else "") + println(if (isDebug) "Кнопка - ${PinConfig.BUTTON}" else "") val defaultButtonState = false val scanner = USBVirusScanner() @@ -495,21 +545,23 @@ fun main() { } buttonListener.startListening() - println("═══════════════════════════════════════") - println(" USB Virus Scanner готов к работе") - println("═══════════════════════════════════════") - println("📌 Вставьте флешку и нажмите кнопку") - println("📌 Зелёный LED мигает - режим ожидания") - println("📌 Жёлтый LED мигает - идёт проверка") - println("📌 Зелёный LED горит - флешка чиста") - println("📌 Красный LED горит - найдены вирусы") - println("📌 Для выхода нажмите Ctrl+C\n") + println(if (isDebug) """ + ═══════════════════════════════════════ + USB Virus Scanner готов к работе + ═══════════════════════════════════════ + 📌 Вставьте флешку и нажмите кнопку + 📌 Зелёный LED мигает - режим ожидания + 📌 Жёлтый LED мигает - идёт проверка + 📌 Зелёный LED горит - флешка чиста + 📌 Красный LED горит - найдены вирусы + 📌 Для выхода нажмите Ctrl+C + """.trimIndent() else "") while (true) { Thread.sleep(1000) } } catch (e: Exception) { - println("❌ Критическая ошибка: ${e.message}") + println(if (isDebug) "❌ Критическая ошибка: ${e.message}" else "") e.printStackTrace() scanner.shutdown() exitProcess(1)