koalvi.blogspot.com - другой мой блог "сойдет на троечку"

понедельник, 1 сентября 2025 г.

простая реализация бэкапа небольшой папки в системе Linux на bash

Реализуется:

 1. Получение бэкапа по почте, для отправки настроен msmtp

2. Создание, с исключением файлов по маске, исключения хранятся в отдельном файле

3. Создание архива с архивированием и шифрованием имен, один из серверов отказался пересылать вложение в котом был архив с запакованным js файлом :(

4. Если общий размер папки не изменился, то архивирования и отправки по почте не будет.

скажем так, информация что отправляется очень критичная!!!!

================ backup.bash

#!/bin/bash

# --- Настройки ---
BACKUP_DIR="/opt/datas" #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
FINAL_ARCHIVE_NAME="backup_$(date +%Y-%m-%d).7z"
TAR_ARCHIVE_NAME="temp_backup.tar.gz"
EXCLUDE_FILE="./exclude.txt"
ARCHIVE_PASSWORD="pass"
RECIPIENT_EMAIL="email@mailserver"
EMAIL_SUBJECT="Резервная копия от $(date +%Y-%m-%d)"
EMAIL_BODY="Здравствуйте!
Прикрепляю к письму резервную копию Tralalal.
Архив защищен паролем.
С уважением,
Ваш скрипт бэкапа."

# Файл для хранения размера последнего бэкапа
LAST_SIZE_FILE="/tmp/backup_size.txt"

# --- Логика ---

# 1. Создание tar.gz архива
if [ -f "$EXCLUDE_FILE" ]; then
    echo "Файл исключений $EXCLUDE_FILE найден. Создаем tar.gz архив с исключениями."
    tar -czvf "$TAR_ARCHIVE_NAME" -C "$BACKUP_DIR" --exclude-from="$EXCLUDE_FILE" .
else
    echo "Файл исключений $EXCLUDE_FILE не найден. Создаем полный tar.gz бэкап."
    tar -czvf "$TAR_ARCHIVE_NAME" -C "$BACKUP_DIR" .
fi

if [ $? -ne 0 ]; then
    echo "Ошибка: Не удалось создать tar.gz архив. Процесс прерван."
    exit 1
fi

# 2. Создание 7z архива
echo "Сжимаем tar.gz архив в 7z с паролем и шифрованием имён файлов..."
7z a -p"$ARCHIVE_PASSWORD" -mhe=on "$FINAL_ARCHIVE_NAME" "$TAR_ARCHIVE_NAME"

if [ $? -ne 0 ]; then
    echo "Ошибка: Не удалось создать 7z архив. Процесс прерван."
    rm "$TAR_ARCHIVE_NAME"
    exit 1
fi

# 3. Проверка размера
# Получаем размер нового 7z-архива в байтах
CURRENT_SIZE=$(stat -c%s "$FINAL_ARCHIVE_NAME")

if [ -f "$LAST_SIZE_FILE" ]; then
    LAST_SIZE=$(cat "$LAST_SIZE_FILE")
else
    LAST_SIZE=0
fi

if [ "$CURRENT_SIZE" == "$LAST_SIZE" ]; then
    echo "Размер архива не изменился. Отправка письма не требуется."
    rm "$TAR_ARCHIVE_NAME"
    rm "$FINAL_ARCHIVE_NAME"
    exit 0
fi

echo "Размер архива изменился. Отправляем письмо."
echo "$CURRENT_SIZE" > "$LAST_SIZE_FILE"

# 4. Отправка по почте
echo "Отправка архива по электронной почте на адрес $RECIPIENT_EMAIL..."

(
    echo -e "Subject: =?UTF-8?B?$(echo -n "$EMAIL_SUBJECT" | base64)?=\nMIME-Version: 1.0\nContent-Type: multipart/mixed; boundary=\"BOUNDARY\"\n\n--BOUNDARY\nContent-Type: text/plain; charset=utf-8\n\n$EMAIL_BODY\n\n--BOUNDARY\nContent-Type: application/x-7z-compressed; name=\"$FINAL_ARCHIVE_NAME\"\nContent-Disposition: attachment; filename=\"$FINAL_ARCHIVE_NAME\"\nContent-Transfer-Encoding: base64\n\n$(base64 "$FINAL_ARCHIVE_NAME")\n--BOUNDARY--\n"
) | msmtp -a default -t "$RECIPIENT_EMAIL"

if [ $? -ne 0 ]; then
    echo "Ошибка: Не удалось отправить письмо."
    exit 1
fi

# 5. Очистка
rm "$TAR_ARCHIVE_NAME"
rm "$FINAL_ARCHIVE_NAME"

echo "Резервное копирование завершено."

=======================

======= exclude.txt
*.tmp
*.log
*.log.*
*sessions*
*token*
===============
на github


четверг, 28 августа 2025 г.

сортировка фото, полученных с камер с использованием yolov8

 Задача/необходимость:
для отслеживания движения на определенной зоне ставится детектор движения либо пересечения на изображении, НО коты-собаки, движения тени от листьев и просто ветер дает очень много лишнего "мусора", а нас волнуют авто и люди.
Вот и настало время компьютерного зрения с распознаванием объектов на фото.
Есть еще серьезный недостаток у камер, вендора Hikvision (возможно это мое устаревшее оборудование, НО мы еще поборемся), присылается на почту 3 фото, в зависимости от скорости пересечения некоторые из них не содержат интересующую информацию.
и большой +  , получение полезной информации в чат Telegram, а позже реализация для Signal.

1. Данные получаем с почтового ящика.

2. Сравниваем фото из одного письма, если они отличаются на 2% то считаем их уникальными. (2% может и больше) и копируем в папку для распознавания.

3. Обрабатываем фото, поиском на нем объектов (перечень возможных), указанных в конфиге для конкретной камеры, если объекты найдены, копируем фото в отдельную папку и отсылаем его на почту/мессенджер, в ином случае переносим фото в папку ошибок1.

4. Вручную просмотрщиком обрабатываем папку ошибок1 удаляя фото без объектов и копируем фото с объектами для обучения модели в папку обучение1. (работа на ошибками)

5. Отдельным редактором делаем обозначения с описанием на фото обучение1.

6. Обучаем модель.

28.08.2025 - Пункты 1-2-3-4 реализованы, чатик с фото читаем, проводим тестирование и доработки. Сейчас версия4.

среда, 20 августа 2025 г.

контроль атрибутов SMART диска в Linux, с передачей их изменения в телеграм

 идея контролировать атрибуты винчестера в автоматическом порядке, при этом не создавая лишней нагрузки на файловую систему, значит: запись только если поменялись критические значения, некритические есть в файле для конкретного винчестера, учитывая что производители могут быть разными как и атрибуты у моделей

помогали несколько движков ИИ, не с первого далеко раза
периоды запуска реализованы через cron
заморачиваться с передачей названия диска в строке не стал
вероятно этот велосипед умрет с этой системой
реализация дополнится отсылкой почты админу

в телеграмме ньюанс насчет перевода на новые строки, меня не раздражает, даже некий юни- шарм

=========== smart_sda.bash

DISK="/dev/sda"
NON_CRITICAL_FILE="/opt/smart/smart_non_critical_sda.txt"
# Load configuration from external file
CONFIG_FILE="/opt/smart/smart_v2.conf"
if [ ! -f "$CONFIG_FILE" ]; then
    echo "Configuration file not found at $CONFIG_FILE. Please create it." >&2
    exit 1
fi
source "$CONFIG_FILE"

# Generate file paths based on disk name
DISK_NAME=$(basename "$DISK")  # e.g., "sda" from "/dev/sda"
LOG_FILE="/dev/null"  # Override log file to /dev/null
STATE_FILE="$BASE_DATA_DIR/smart_state_${DISK_NAME}.txt"
DISK_INFO_FILE="$BASE_DATA_DIR/smart_disk_info_${DISK_NAME}.txt"

# Ensure base directories exist
mkdir -p "$BASE_DATA_DIR" 2>/dev/null

# Ensure smartctl is installed
if ! command -v "$SMARTCTL" &> /dev/null; then
    echo "smartctl not found. Please install smartmontools." >> "$LOG_FILE"
    exit 1
fi

# Ensure non-critical attributes file exists
if [ ! -f "$NON_CRITICAL_FILE" ]; then
    echo "Non-critical attributes file not found at $NON_CRITICAL_FILE" >> "$LOG_FILE"
    exit 1
fi

# Read non-critical attributes into an array, extracting only the first field (number)
mapfile -t NON_CRITICAL < <(grep -v '^\s*#' "$NON_CRITICAL_FILE" | grep -v '^\s*$' | awk '{print $1}')

# Function to check if attribute is non-critical
is_non_critical() {
    local attr_id=$1
    for nc in "${NON_CRITICAL[@]}"; do
        if [ "$nc" == "$attr_id" ]; then
            return 0
        fi
    done
    return 1
}

# Function to get SMART attributes
get_smart_attributes() {
    local output
    output=$("$SMARTCTL" -n standby -A "$DISK" 2>&1)
    if [ $? -ne 0 ]; then
        echo "Error executing smartctl -A on $DISK: $output" >> "$LOG_FILE"
        exit 1
    fi
    echo "$output" | awk '/^[ 0-9]/ { print $1, $2, $10 }'
}

# Function to get disk info (name and serial number) - handles multi-word models
get_disk_info() {
    local output
    output=$("$SMARTCTL" -i "$DISK" 2>&1)
    if [ $? -ne 0 ]; then
        echo "Error executing smartctl -i on $DISK: $output" >> "$LOG_FILE"
        exit 1
    fi
    echo "$output" | awk '
        /Device Model/ {
            out = "Device Model: ";
            for (i=3; i<=NF; i++) out = out $i (i<NF ? " " : "");
            print out
        }
        /Serial Number/ {
            out = "Serial Number: ";
            for (i=3; i<=NF; i++) out = out $i (i<NF ? " " : "");
            print out
        }
    '
}

# Save disk info if file doesn't exist
if [ ! -f "$DISK_INFO_FILE" ]; then
    get_disk_info > "$DISK_INFO_FILE"
fi

# Main logic
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] Checking SMART attributes for $DISK" >> "$LOG_FILE"

# Get current SMART data
current_data=$(get_smart_attributes)
if [ -z "$current_data" ]; then
    echo "[$timestamp] No SMART data retrieved for $DISK." >> "$LOG_FILE"
    exit 0
fi

# Flag to track if changes were found
changes_detected=false
telegram_message=""

# Check if previous data exists
if [ -f "$STATE_FILE" ]; then
    previous_data=$(cat "$STATE_FILE")

    # Compare with previous data
    while IFS= read -r prev_line; do
        prev_id=$(echo "$prev_line" | awk '{print $1}')
        prev_name=$(echo "$prev_line" | awk '{print $2}')
        prev_value=$(echo "$prev_line" | awk '{print $3}')

        # Find matching line in current data
        current_line=$(echo "$current_data" | grep "^$prev_id ")
        if [ -n "$current_line" ]; then
            current_value=$(echo "$current_line" | awk '{print $3}')

            # Check if value changed and attribute is not non-critical
            if [ "$prev_value" != "$current_value" ] && ! is_non_critical "$prev_id"; then
                echo "[$timestamp] Attribute $prev_id ($prev_name) changed: $prev_value -> $current_value" >> "$LOG_FILE"
                changes_detected=true
                telegram_message+="$prev_id ($prev_name): $prev_value -> $current_value\n"
            fi
        fi
    done <<< "$previous_data"
fi

# Save current data as new state only if changes in critical attributes were detected or no previous state exists
if [ "$changes_detected" = true ] || [ ! -f "$STATE_FILE" ]; then
    echo "$current_data" > "$STATE_FILE"
fi

# Send Telegram notification if changes were detected
if [ "$changes_detected" = true ]; then
    disk_info=$(cat "$DISK_INFO_FILE")
    full_message="SMART Changes detected on $DISK at $timestamp\n$disk_info\nChanges:\n$telegram_message"
    curl_response=$(curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" \
         -d chat_id="$TELEGRAM_CHAT_ID" \
         -d text="$full_message" 2>&1)
    if [ $? -ne 0 ]; then
        echo "[$timestamp] Error sending Telegram notification for $DISK: $curl_response" >> "$LOG_FILE"
    else
        echo "[$timestamp] Telegram notification sent for $DISK" >> "$LOG_FILE"
    fi
fi
======== smart_v2.conf ====
SMARTCTL="/usr/sbin/smartctl"
BASE_LOG_DIR="/opt/smart/out"
BASE_DATA_DIR="/opt/smart"
TELEGRAM_BOT_TOKEN="xxxxxx:*************************"
TELEGRAM_CHAT_ID="-xxxxxxxxxxx"
=== smart_non_critical_sda.txt=====
16
9
=====================

среда, 16 июля 2025 г.

Mikrotik. ROS. Мониторинг состояния хостов по icmp.

 задача по мониторингу состояния хостов в сети древняя, у меня было несколько реализаций, какую-то реализацию делал некий Влад (судя по коду может и не один), но собравший с остатками здравомыслия пришел я к следующему проекту:

1. Перечень хостов и их названий хранится в файле.

2. Мы отслеживаем отключение либо включение хоста в сети (по протоколу icmp)

3. Сообщения "уходят" в телеграмм, так как задач несколько, с ним мы работаем на модульном уровне, как в результат сложилось - используем глобальную переменную для сообщения, а в теле вызываем скрипт.

4. Периодически проверяем состояние и работоспособность самого скрипта, например в начале рабочего времени и в конце.

Программу решил писать не я, а стать архитектором, поручив Искусственному интеллекту реализацию идей, и был я довольно раздосадован....с ROS неважно работает ИИ, а сколько раз он указывал что мой скрипт работать не будет и мне нужно обновиться на 7ю версию ROS и без этого вообще никак.....но результат положительный есть, и опыт у ИИ есть, получал я седые волосы с Gemni и Grok, кстати второй очень неплохо пишет короткий код, а вот в многострочный проектах его дерзость в принятии спорных решений просто убивает желание с ним работать, он тот самый неуч:) который может всё, а вот  Gemni почти наоборот,  и плохо что я не почувствовал явного прогресса от ИИ, иногда приходилось возвращаться по коду, так они не могли воспроизвести уже отработанный рабочий код. Да скрипт в ROS это специфическое, но не Perl и т.п. в конце концов...

начнем:

файл в котором хранятся  данные
=================== host_data.txt ========================
192.168.0.2;comp1
192.168.0.3;comp10

====================================================

файл самого скрипта...
===================skript1

:local filePath "flash/host_data.txt"
:local fileCheckResult [/file find name=$filePath]
:global telegramMessage
:global hostStatus
:if ([:typeof $hostStatus] = "nothing") do={
    :set hostStatus [:toarray ""]
    :local i
    :for i from=0 to=129 do={
        :set ($hostStatus->$i) -1
    }
}
#:log info "MONITORING_START: Skript zapushen dlya monitoringa uzlov i otpravki Telegram."
:if ($fileCheckResult = "") do={
    :set telegramMessage "MONITORING_ERROR: FAIL flash/host_ping.txt NE NAIDEN!"
    :log error $telegramMessage
    :log info "MONITORING_TELEGRAM_FILE_ALERT: Vyzyvayu telegram_api_get dlya faila."
    /system script run telegram_api_get
    :log info "MONITORING_TELEGRAM_FILE_ALERT: telegram_api_get vyzvan dlya faila."
    :return
}
:local fileContent [/file get $filePath contents]
:local remainingContent $fileContent
:local lineCount 0
:local currentLine ""
:local newlinePos 0
:while ([:len $remainingContent] > 0) do={
    :set newlinePos [:find $remainingContent "\n"]
    :if ($newlinePos != -1) do={
        :set currentLine [:pick $remainingContent 0 $newlinePos]
        :set remainingContent [:pick $remainingContent ($newlinePos + 1) [:len $remainingContent]]
#    :log info $newlinePos
    } else={
        :set currentLine $remainingContent
        :set remainingContent ""
    }

    :set lineCount ($lineCount + 1)
    
    # Ïðîïóñêàåì ïóñòûå ñòðîêè
    :if ([:len $currentLine] = 0) do={:continue}

    # Ïðîâåðêà íà ïðåâûøåíèå ìàêñèìàëüíîãî êîëè÷åñòâà ýëåìåíòîâ
    :if ($lineCount > 130) do={
        :set telegramMessage ("MONITORING_ERROR: Prevyøåí limit v 130 uzlov v faile " . $filePath . "!")
        :log error $telegramMessage
        :log info "MONITORING_TELEGRAM_LIMIT_ALERT: Vyzyvayu telegram_api_get dlya oshibki limita."
        /system script run telegram_api_get
        :log info "MONITORING_TELEGRAM_LIMIT_ALERT: telegram_api_get vyzvan dlya oshibki limita."
        :return
    }
    :local ipAddress ""
    :local nodeName ""
    :local semicolonPos [:find $currentLine ";"]
    :if ($semicolonPos != -1) do={
        :set ipAddress [:pick $currentLine 0 $semicolonPos]
        :set nodeName [:pick $currentLine ($semicolonPos + 1) [:len $currentLine]]
#       :log info ("MONITORING_INFO: Obrabotka uzla: " . $nodeName . " (" . $ipAddress . ")")
    } else={
        :set telegramMessage ("MONITORING_WARNING: Nepravilniy format stroki #" . $lineCount . ": '" . $currentLine . "'. Propuskaem.")
#        :log warning $telegramMessage
#        :log info "MONITORING_TELEGRAM_FORMAT_ALERT: Vyzyvayu telegram_api_get dlya oshibki formata."
        /system script run telegram_api_get
#        :log info "MONITORING_TELEGRAM_FORMAT_ALERT: telegram_api_get vyzvan dlya oshibki formata."
        :continue
    }
    :local pingCommandResult [/ping $ipAddress count=3 interface=bridge]
    :local currentStatus
    :if ($pingCommandResult = 0) do={
        :set currentStatus 0
    } else={
        :set currentStatus 1
    }
    :local arrayIndex ($lineCount - 1)
    :local previousStatus ($hostStatus->$arrayIndex)
    :if ($currentStatus != $previousStatus) do={
        :if ($currentStatus = 0) do={
            :set telegramMessage ("MONITORING_ALERT: UZEL NE OTVETCHAET: " . $nodeName . " (" . $ipAddress . ")!")
            /system script run telegram_api_get
        } else={
            :set telegramMessage ("MONITORING_STATUS: UZEL " . $nodeName . " (" . $ipAddress . ") - VOSSTANOVLEN!")
            /system script run telegram_api_get
         }
    } else={
        :if ($currentStatus = 0) do={
#            :log info ("MONITORING_STATUS: UZEL " . $nodeName . " (" . $ipAddress . ") - OSTAYETSYA NEDOSTUPNYM.")
        } else={
#            :log info ("MONITORING_STATUS: UZEL " . $nodeName . " (" . $ipAddress . ") - OSTAYETSYA DOSTUPNYM.")
        }
    }
    :set ($hostStatus->$arrayIndex) $currentStatus
}
#:log info "MONITORING_COMPLETE: Skript zavershen."

========================


:global hostStatus
:if ([:typeof $hostStatus] != "array") do={
    :log warning "UPDATE_HOST_STATUS: non massiv."
    :return
}
:local arrayLength [:len $hostStatus]
:local i 0
:while ($i < $arrayLength) do={
    :local currentValue ($hostStatus->$i)
    :if ($currentValue = 0) do={
        :do {
            :set ($hostStatus->$i) -1
        } on-error={
            :log error "UPDATE_HOST_STATUS: error update"
        }
        :log info "UPDATE_HOST_STATUS: update"
    } else={
        :log info "UPDATE_HOST_STATUS: non update"
    }
    :set i ($i + 1)
}

====================
Дальше думаем о sheduler

воскресенье, 19 сентября 2021 г.

объединение двух тестовых фалов в Lazarus (Delphi) "по-старому"...

 Просто возмутило осложнение вариантов объединения двух текстовых файлов  в Lazarus (Delphi) во множестве вариантов поэтому поступили так:

var
tfOut,tfIn:textfile;
file_s1,file_s2:string;
...
AssignFile(tfOut, file_s1);
AssignFile(tfIn, file_s2);
reset(tfIn);
 try
append(tfOut);
while not eof(tfIn) do begin
readln(tfIn,body_file);
writeln(tfOut,body_file);
end;
   CloseFile(tfIn);
   CloseFile(tfOut);
 except
   on E: EInOutError do
    writeln('File error. Elaboration: ', E.Message);
 end;
===================================================
если говорить вообще об тестовом способе через консоль -
If ShellExecute(0,nil, PChar('cmd'),PChar('/c copy text1.txt+text2.txt /b text1.txt'),nil,1)=0 then;

 0/1 открывать окно консоли или нет, 
/С /K - закрывать/не закрывать окно консоли после выполнения
но при обработке тысяч файлов этот метод не справляется по скорости обработки, у меня происходит отказ доступа после какого-то времени

вторник, 2 февраля 2021 г.

изменяем в домене дефолтные прежде настройки паролей пользователей

===user.csv=============

SamAccountName

vova

misha

bobik

===============un_pass_neverexp.ps1=======

import-csv user.csv | ForEach-Object { Set-ADUser -Identity $_.SamAccountName -PasswordNeverExpires:$FALSE -CannotChangePassword:$FALSE}

позволяет убрать галочку о том что пароль никогда не состарится + разрешаем менять пароль самому пользователю


================== узнать у кого когда заканчиваются пароли

Get-ADUser -filter * -properties PasswordLastSet, UserPrincipalName, PasswordNeverExpires| ft Name, PasswordLastSet, PasswordNeverExpires | Export-csv -path c:\report\password.csv -Encoding UTF8

пятница, 9 ноября 2018 г.

сжатие sysdba

SET ISC_USER=SYSDBA
SET ISC_PASSWORD=masterke

copy /Y base1.gdb base1_original.gdb
gbak -b base1.gdb base1_pack.gdb
gbak -r 1b52_pack.gdb base1+.gdb


очень вероятно что нужно

gbak -b -g  base1.gdb base1_pack.gdb

и возможно что

gfix.exe -sweep

.....