Compare commits

...

6 Commits

Author SHA1 Message Date
root
7ca246db3f Генерация корневого сертификата и новых сертификатов подписанных им 2026-03-18 09:43:48 +03:00
root
a5ee241e15 Установка исполнимого флага для директории расшаренной в Виндовс 2026-03-18 09:29:16 +03:00
root
810e6ec436 Добвлен вывод объёмов:
* свбодного места на резервном хранилище
* объёма резервируемого пространства по каждому датасету (+итоговая сумма)
2025-10-24 10:51:38 +03:00
6af17fcb8e Merge pull request 'pankovea-patch-1' (#1) from pankovea-patch-1 into main
Reviewed-on: https://192.168.0.137:3000/pankovea/nas_scripts/pulls/1
2025-07-14 15:22:40 +00:00
root
909b5bbb49 fix: Форматирование вывода при инкрементном копировании
* убран вывод ошибки отсутвующих датасетов
2025-07-14 17:58:04 +03:00
root
1738c35abb fix:
* Сервер по умолчанию 120
* Не выходил из скрипта после вывода справки -h, а продолжал запуск
* Устранены ошибки вывода статистики (invalid date ‘@954.)
* Выход из бесконечного цикла проверки (теперь читает i_task на каждой итеррации)
* Очищает временные переменные по завершении скрипта
2025-07-14 16:28:06 +03:00
5 changed files with 405 additions and 33 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
log*

56
generate_root_ca.sh Executable file
View File

@@ -0,0 +1,56 @@
#!/bin/bash
# Настройки
CERT_DIR="$HOME/certs"
CA_NAME="myrootca"
DAYS=3650 # 10 лет
# Цвета
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'
echo "Генерация корневого сертификата (Root CA)..."
# 1. Создание директории
mkdir -p "$CERT_DIR"
cd "$CERT_DIR" || exit 1
# Проверка: если CA уже существует, предупреждаем
if [ -f "${CA_NAME}.crt" ] && [ -f "${CA_NAME}.key" ]; then
echo -e "${RED}Внимание!${NC} Файлы ${CA_NAME}.crt и ${CA_NAME}.key уже существуют."
read -p "Перезаписать их? (это аннулирует все ранее выданные сертификаты) [y/N]: " confirm
if [[ ! $confirm =~ ^[Yy]$ ]]; then
echo "Отменено."
exit 0
fi
fi
# 2. Генерация приватного ключа (4096 бит)
echo "Генерация приватного ключа..."
openssl genrsa -out "${CA_NAME}.key" 4096 2>/dev/null
chmod 600 "${CA_NAME}.key" # Защита ключа
# 3. Генерация самоподписанного сертификата
echo "Генерация корневого сертификата..."
openssl req -x509 -new -nodes \
-key "${CA_NAME}.key" \
-sha256 \
-days ${DAYS} \
-out "${CA_NAME}.crt" \
-subj "/C=RU/ST=State/L=City/O=MyOrg/CN=TrueNAS Root CA"
# Проверка результата
if [ -f "${CA_NAME}.crt" ]; then
echo -e "${GREEN}Успешно!${NC}"
echo "Файлы созданы в: $CERT_DIR"
echo " Ключ: ${CA_NAME}.key (храните в секрете!)"
echo " Серт: ${CA_NAME}.crt (установите этот файл на все клиентские устройства)"
echo ""
echo "Информация о сертификате:"
openssl x509 -in "${CA_NAME}.crt" -noout -subject -dates
else
echo -e "${RED}Ошибка при создании сертификата.${NC}"
exit 1
fi

93
generate_signed_cert.sh Executable file
View File

@@ -0,0 +1,93 @@
#!/bin/bash
# Настройки
CERT_DIR="$HOME/certs"
CA_KEY="$CERT_DIR/truenasrootca.key"
CA_CRT="$CERT_DIR/truenasrootca.crt"
# Цвета для вывода
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Функция для вывода ошибок
error() {
echo -e "${RED}[ERROR]${NC} $1" >&2
exit 1
}
# Функция для вывода успеха
success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
# Проверка аргументов
if [ "$#" -ne 3 ]; then
echo "Использование: $0 <имя_сертификата> <DNS_имя> <IP_адрес>"
echo "Пример: $0 truenas truenas.local 192.168.0.137"
exit 1
fi
CERT_NAME="$1"
DNS_NAME="$2"
IP_ADDR="$3"
# Переход в директорию сертификатов
mkdir -p "$CERT_DIR"
cd "$CERT_DIR" || error "Не удалось перейти в директорию $CERT_DIR"
# 1. Проверка наличия корневого CA
if [ ! -f "$CA_KEY" ] || [ ! -f "$CA_CRT" ]; then
error "Корневой сертификат не найден! ($CA_CRT / $CA_KEY)"
echo "Сначала создайте Root CA или убедитесь, что файлы лежат в $CERT_DIR"
exit 1
fi
echo -e "${YELLOW}Генерация сертификата для: ${CERT_NAME}${NC}"
echo "DNS: $DNS_NAME"
echo "IP: $IP_ADDR"
# 2. Генерация приватного ключа сервера
# (Пересоздаем ключ, если он уже есть, чтобы не было конфликтов)
openssl genrsa -out "${CERT_NAME}.key" 4096 2>/dev/null || error "Не удалось создать ключ"
chmod 600 "${CERT_NAME}.key" # Защита ключа
# 3. Создание файла расширений (v3.ext) динамически
cat > "${CERT_NAME}.ext" << EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = ${DNS_NAME}
IP.1 = ${IP_ADDR}
EOF
# 4. Создание CSR (Certificate Signing Request)
openssl req -new -key "${CERT_NAME}.key" -out "${CERT_NAME}.csr" \
-subj "/CN=${DNS_NAME}" 2>/dev/null || error "Не удалось создать CSR"
# 5. Подпись сертификата корневым CA
openssl x509 -req -in "${CERT_NAME}.csr" \
-CA "$CA_CRT" -CAkey "$CA_KEY" -CAcreateserial \
-out "${CERT_NAME}.crt" -days 3650 -sha256 \
-extfile "${CERT_NAME}.ext" 2>/dev/null || error "Не удалось подписать сертификат"
# 6. Очистка временных файлов (CSR и ext)
# Если вам нужен CSR для других целей, закомментируйте следующую строку
rm -f "${CERT_NAME}.csr" "${CERT_NAME}.ext"
# 7. Финальный вывод
success "Сертификат успешно создан!"
echo "Файлы:"
echo " Ключ: $CERT_DIR/${CERT_NAME}.key"
echo " Серт: $CERT_DIR/${CERT_NAME}.crt"
echo "Подписан с помощью: (использовать для установки доверия на клиентах)"
# Проверка содержимого
echo ""
echo "Информация о сертификате:"
openssl x509 -in "${CERT_NAME}.crt" -noout -text | grep -A1 "Subject Alternative Name"

142
scan_to_exe_and_set_x_flag.sh Executable file
View File

@@ -0,0 +1,142 @@
#!/usr/bin/env bash
# Совместим с TrueNAS CORE (FreeBSD) и SCALE (Linux)
# Безопасен при перенаправленном stdin
# Проверка наличия терминала для интерактивного режима
if [[ -t 0 ]]; then
INTERACTIVE=1
else
INTERACTIVE=0
echo "Внимание: stdin не подключён к терминалу. Режим подтверждения отключён."
echo "Используйте флаг --force для автоматического выполнения."
if [[ "$*" != *"--force"* ]]; then
exit 1
fi
fi
# Цвета (только если вывод в терминал)
if [[ -t 1 ]]; then
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
else
RED=''; GREEN=''; YELLOW=''; BLUE=''; NC=''
fi
# Проверка аргумента
if [[ $# -lt 1 ]] || [[ "$1" == "--help" ]] || [[ "$1" == "-h" ]]; then
echo "Использование: $0 [--force] <путь_к_директории>"
echo " --force Пропустить подтверждение (для автоматизации)"
exit 1
fi
# Обработка флага --force
FORCE=0
if [[ "$1" == "--force" ]]; then
FORCE=1
shift
fi
TARGET_DIR="$1"
# Проверка директории
if [[ ! -d "$TARGET_DIR" ]]; then
echo -e "${RED}Ошибка:${NC} Директория не существует: $TARGET_DIR" >&2
exit 1
fi
# Нормализация пути
TARGET_DIR="$(cd "$TARGET_DIR" 2>/dev/null && pwd || echo "$TARGET_DIR")"
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}Подготовка к обработке Windows-файлов${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
echo -e "Директория: ${YELLOW}${TARGET_DIR}${NC}"
echo -e "Расширения: ${YELLOW}.exe .bat .cmd .com${NC} (любой регистр)"
echo -e "Права: ${YELLOW}ug+x${NC} (владелец + группа)"
echo ""
# Подсчёт файлов (безопасный метод для BSD)
FILE_COUNT=0
while IFS= read -r -d '' file 2>/dev/null; do
((FILE_COUNT++)) 2>/dev/null || FILE_COUNT=$((FILE_COUNT + 1))
done < <(find "$TARGET_DIR" -type f \( -iname "*.exe" -o -iname "*.bat" -o -iname "*.cmd" -o -iname "*.com" \) -print0 2>/dev/null || true)
echo -e "Найдено файлов: ${GREEN}${FILE_COUNT}${NC}"
echo ""
if [[ $FILE_COUNT -eq 0 ]]; then
echo -e "${YELLOW}Не найдено подходящих файлов. Обработка не требуется.${NC}"
exit 0
fi
# Предпросмотр (только если интерактивно)
if [[ $INTERACTIVE -eq 1 ]]; then
echo -e "${BLUE}Примеры найденных файлов (первые 10):${NC}"
COUNT_PREVIEW=0
while IFS= read -r -d '' file 2>/dev/null; do
echo "$file"
COUNT_PREVIEW=$((COUNT_PREVIEW + 1))
[[ $COUNT_PREVIEW -ge 10 ]] && break
done < <(find "$TARGET_DIR" -type f \( -iname "*.exe" -o -iname "*.bat" -o -iname "*.cmd" -o -iname "*.com" \) -print0 2>/dev/null || true)
if [[ $FILE_COUNT -gt 10 ]]; then
echo " ... и ещё $((FILE_COUNT - 10)) файлов"
fi
echo ""
fi
# Подтверждение (только если интерактивно и не --force)
if [[ $INTERACTIVE -eq 1 && $FORCE -eq 0 ]]; then
echo -e "${YELLOW}Внимание:${NC} Будут изменены права ${GREEN}ТОЛЬКО${NC} для владельца и группы (ug+x)"
echo -n "Продолжить? (y/n) [n]: "
read -r CONFIRM || CONFIRM="n"
case "${CONFIRM,,}" in
y|yes|д|да)
echo -e "\n${GREEN}Подтверждено. Начинаем обработку...${NC}"
;;
*)
echo -e "\n${YELLOW}Операция отменена.${NC}"
exit 0
;;
esac
echo ""
elif [[ $FORCE -eq 1 ]]; then
echo -e "${YELLOW}Режим --force: подтверждение пропущено.${NC}"
echo ""
fi
# Обработка файлов
SUCCESS=0
FAILED=0
TOTAL=0
while IFS= read -r -d '' file 2>/dev/null; do
TOTAL=$((TOTAL + 1))
if chmod ug+x "$file" 2>/dev/null; then
BASENAME="${file##*/}"
echo -e "[${GREEN}OK${NC}] $BASENAME"
SUCCESS=$((SUCCESS + 1))
else
BASENAME="${file##*/}"
echo -e "[${RED}FAIL${NC}] $BASENAME (нет прав?)"
FAILED=$((FAILED + 1))
fi
done < <(find "$TARGET_DIR" -type f \( -iname "*.exe" -o -iname "*.bat" -o -iname "*.cmd" -o -iname "*.com" \) -print0 2>/dev/null || true)
# Итог
echo ""
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}Итог обработки${NC}"
echo -e "${BLUE}========================================${NC}"
echo -e "Успешно: ${GREEN}${SUCCESS}${NC}"
echo -e "Ошибок: ${RED}${FAILED}${NC}"
echo -e "Всего: ${BLUE}${TOTAL}${NC}"
echo ""
if [[ $FAILED -gt 0 ]]; then
echo -e "${YELLOW}Внимание:${NC} Некоторые файлы не обработаны (возможно, нет прав на запись)."
echo "Проверьте: mount -v | grep '${TARGET_DIR%/*}' — возможно, ФС смонтирована как read-only."
fi
echo -e "${GREEN}Готово.${NC}"

View File

@@ -2,16 +2,12 @@
#TODO list #TODO list
# 1. Скрипт не определяет, что нужно остановиться # 1. Скрипт не определяет, что нужно остановиться
# 2. Возникают ошибки: # 2. Нужно автоматически определить, если копирование уже запущено то предложить остановить и вывести процесс резервирования
# sending main_pool/3D_Design@manual-20250327 (99%: 21.9G/21.9G) 25.6M/s date: invalid date @0. # Нет проверки на верность введённых параметров
# date: invalid date @954.
# sending main_pool/3D_Design@manual-20250327 (100%: 21.9G/21.9G) 25.6M/s date: invalid date @-1.
# date: invalid date @954.
# 3. Нужно автоматически определить, если копирование уже запущено то предложить остановить и вывести процесс резервирования
# смотреть нужно сразу на всей системе. Не будем запускать несколько параллельных резервирований. # смотреть нужно сразу на всей системе. Не будем запускать несколько параллельных резервирований.
backup_server="192.168.0.162" # Сервер для резервирования backup_server="192.168.0.120" # Сервер для резервирования
backup_user="root" # Пользователь на сервере backup_user="root" # Пользователь на сервере
inc_snapshot="manual-$(date +%Y%m%d)" # Новый создаваемый снисмок inc_snapshot="manual-$(date +%Y%m%d)" # Новый создаваемый снисмок
incremental=false incremental=false
@@ -51,7 +47,7 @@ while [[ "$#" -gt 0 ]]; do
echo " --no-check"; echo " --no-check";
echo " --stop"; echo " --stop";
echo "-i | --incremental" ; echo "-i | --incremental" ;
echo "-p | --progress" ;; exit 0;;
*) echo "Неизвестные параметры: $1"; exit 1 ;; *) echo "Неизвестные параметры: $1"; exit 1 ;;
esac esac
shift shift
@@ -90,16 +86,20 @@ find_ssh_key() {
done done
# Если ключей нет, создаем новый # Если ключей нет, создаем новый
# echo "Создаём новый ключ..." if [ ! -f ~/.ssh/id_ed25519 ]; then
ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519 >&2 ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519
echo "~/.ssh/id_ed25519" # echo "Создаём новый ключ..."
ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519 >&2
echo "~/.ssh/id_ed25519"
else
echo "Ключей для этого сервера не найдено. Ключ по умолчанию ~/.ssh/id_ed25519 уже существует. Создание нового ключа прервано"
fi
} }
# Определение ключа # Определение ключа
key_path=$(find_ssh_key) key_path=$(find_ssh_key)
key_pub_path="${key_path}.pub" key_pub_path="${key_path}.pub"
check_ssh_connection() { check_ssh_connection() {
ssh -o "BatchMode=yes" -o "ConnectTimeout=5" "$backup_user@$backup_server" exit 2>/dev/null ssh -o "BatchMode=yes" -o "ConnectTimeout=5" "$backup_user@$backup_server" exit 2>/dev/null
return $? return $?
@@ -210,6 +210,19 @@ local_datasets=(${=local_datasets}) # Конвертирование в масс
backup_datasets=$(ssh $backup_user@$backup_server zfs list -o name | grep -v -e ix-app -e boot-pool -e system-data -e jails -e NAME) backup_datasets=$(ssh $backup_user@$backup_server zfs list -o name | grep -v -e ix-app -e boot-pool -e system-data -e jails -e NAME)
backup_datasets=(${=backup_datasets}) # Конвертирование в массив backup_datasets=(${=backup_datasets}) # Конвертирование в массив
# Свободное место на бэкап-сервере
declare -A backup_pool_free # Словарь: ключ = пул, значение = свободное место
ignored_pools=(boot-pool)
# Запрашиваем данные о свободном пространстве и парсим
while read -r pool free; do
if (( ${ignored_pools[(Ie)$pool]} )); then
continue
fi
backup_pool_free[$pool]="$free"
done < <(ssh "$backup_user@$backup_server" "zpool list -H -o name,free" 2>/dev/null)
declare -A work_datasets # Словарь ключ = резервируемый локальный датасет, значение = имя датасета на удалённом сервере declare -A work_datasets # Словарь ключ = резервируемый локальный датасет, значение = имя датасета на удалённом сервере
# Если заданы local_dataset & remote_dataset # Если заданы local_dataset & remote_dataset
@@ -282,12 +295,13 @@ fi # Конец блока наличия переданных параметр
# Словарь со списком последких снапшотов для датасетов # Словарь со списком последких снапшотов для датасетов
declare -A last_loc_snaps declare -A last_loc_snaps
echo -ne "Проверяю на наличие $inc_snapshot...\033[2K\r" echo -ne "Проверяю на наличие $inc_snapshot...\033[2K\r"
column_widths="%-45s * %-45s\n"
for loc_ds bak_ds in ${(kv)work_datasets}; do for loc_ds bak_ds in ${(kv)work_datasets}; do
# Запросить последние снимки # Запросить последние снимки
loc_snap="$(zfs list -t snapshot -o name ${loc_ds} | grep 'manual' | tail -n1 | egrep -o '@.+' )" loc_snap="$(zfs list -t snapshot -o name ${loc_ds} 2>/dev/null | grep 'manual' | tail -n1 | egrep -o '@.+' )"
loc_snap=${loc_snap:1} # удалить @ в начале строки loc_snap=${loc_snap:1} # удалить @ в начале строки
if [[ $incremental = true ]]; then if [[ $incremental = true ]]; then
bak_snap=$(ssh $backup_user@$backup_server zfs list -t snapshot -o name ${bak_ds} | grep 'manual' | tail -n1 | egrep -o '@.+') bak_snap=$(ssh $backup_user@$backup_server zfs list -t snapshot -o name ${bak_ds} 2>/dev/null | grep 'manual' | tail -n1 | egrep -o '@.+')
bak_snap=${bak_snap:1} # удалить @ в начале строки bak_snap=${bak_snap:1} # удалить @ в начале строки
fi fi
# echo "$loc_ds @ $loc_snap -> $bak_ds @ $bak_snap" # echo "$loc_ds @ $loc_snap -> $bak_ds @ $bak_snap"
@@ -295,12 +309,12 @@ for loc_ds bak_ds in ${(kv)work_datasets}; do
if [[ "$loc_snap" = "$inc_snapshot" ]]; then if [[ "$loc_snap" = "$inc_snapshot" ]]; then
if [[ $incremental = true ]] && [[ "$loc_snap" = "$bak_snap" ]]; then if [[ $incremental = true ]] && [[ "$loc_snap" = "$bak_snap" ]]; then
# Если последние снимки на обеих машинах совпадают в инкрементном режиме # Если последние снимки на обеих машинах совпадают в инкрементном режиме
echo "${loc_ds}@${loc_snap} * существует на обеих машинах. Исключено из списка резервируемых" printf $column_widths "${loc_ds}@${loc_snap}" "существует на обеих машинах. Исключено из списка резервируемых"
unset "work_datasets[$loc_ds]" unset "work_datasets[$loc_ds]"
else else
# Если на удалённой машине нет такого снимка, то предложить удалить. # Если на удалённой машине нет такого снимка, то предложить удалить.
echo "Датасет $loc_ds уже имеет последний снимок $inc_snapshot" printf $column_widths "${loc_ds}@${loc_snap}" "существует на исходной машине, но отсутвует на $backup_server"
read "act?ok: резервировать | re: пересоздать (удаляет сразу!) | исключить из работы по умолчанию : " read "act?ok: резервировать | re: пересоздать (удаляет сразу!!!) | по умолчанию исключить из работы : "
case $act in case $act in
re) zfs destroy "${loc_ds}@${loc_snap}"; re) zfs destroy "${loc_ds}@${loc_snap}";
loc_snap="$(zfs list -t snapshot -o name ${loc_ds} | grep 'manual' | tail -n1 | egrep -o '@.+' )"; loc_snap="$(zfs list -t snapshot -o name ${loc_ds} | grep 'manual' | tail -n1 | egrep -o '@.+' )";
@@ -317,6 +331,7 @@ for loc_ds bak_ds in ${(kv)work_datasets}; do
last_loc_snaps[$loc_ds]=$loc_snap last_loc_snaps[$loc_ds]=$loc_snap
else else
if [[ $incremental = true ]]; then if [[ $incremental = true ]]; then
printf $column_widths "${loc_ds}" "Не имеет снимков. Не возможно сделать инкрементный резерв."
unset "work_datasets[$loc_ds]" unset "work_datasets[$loc_ds]"
fi fi
fi fi
@@ -325,25 +340,74 @@ done
# --------- Запрашиваем подтверждение пользователя ---------- # --------- Запрашиваем подтверждение пользователя ----------
echo "" estimate_changes_bytes() { # Рассчитать объём резервируемого пространства
echo "Список резервируемых датасетов:" local loc_ds="$1"
column_widths="%-4s %-35s %-20s -> %-35s\n" local base_snap="$2"
local value
if [[ -z "$base_snap" ]]; then
# Полный бэкап → used в байтах
value=$(zfs get -H -p -o value used "$loc_ds" 2>/dev/null)
else
# Инкремент → written@ в байтах
value=$(zfs get -H -p -o value "written@$base_snap" "$loc_ds" 2>/dev/null)
fi
# Если значение получено — выводим его, иначе — ничего (пусто)
if [[ -n "$value" && "$value" != "-" ]]; then
echo "$value"
fi
}
human_size() { # Перевести байты в читаемый вид
bytes="$1"
if [[ -z "$bytes" ]]; then
echo "N/A"
return
fi
local one_gib=1073741824
if (( bytes >= one_gib )); then
numfmt --to=iec --format="%.1f" "$bytes" 2>/dev/null || echo "${bytes}B"
else
numfmt --to=iec --format="%.0f" "$bytes" 2>/dev/null || echo "${bytes}B"
fi
}
declare -A est_changes # Словарь хранения необходимого объёма для резервирования. Ключ - датасет, значнеие - объём в байтах
echo
echo "Список резервируемых датасетов:"
column_widths="%-4s %-35s %-20s %-10s -> %-35s\n"
printf "$column_widths" "#" "Локальный датасет" "Базовый снапшот" "Изменения" "Бэкап-датасет"
total_bytes=0
if [[ $incremental = true ]]; then if [[ $incremental = true ]]; then
i=0; for loc_ds bak_ds in ${(kv)work_datasets}; do i=0; for loc_ds bak_ds in ${(kv)work_datasets}; do
(( i++ )) (( i++ ))
printf $column_widths $i $loc_ds "${last_loc_snaps[$loc_ds]}" $bak_ds est_changes[$loc_ds]=$(estimate_changes_bytes "$loc_ds" "${last_loc_snaps[$loc_ds]}")
(( total_bytes += est_changes[$loc_ds] ))
printf $column_widths $i $loc_ds "${last_loc_snaps[$loc_ds]}" $(human_size "${est_changes[$loc_ds]}") $bak_ds
done done
printf "%-4s %-35s %-20s\n" "" "Инкрементный снимок:" "$inc_snapshot (*)" printf "%-4s %-35s %-20s\n" "" "Инкрементный снимок:" "$inc_snapshot (*)"
else else
i=0; for loc_ds bak_ds in ${(kv)work_datasets}; do i=0; for loc_ds bak_ds in ${(kv)work_datasets}; do
(( i++ )) (( i++ ))
printf $column_widths $i $loc_ds "$inc_snapshot (*)" "$bak_ds (*)" est_changes[$loc_ds]=$(estimate_changes_bytes "$loc_ds" "")
(( total_bytes += est_changes[$loc_ds] ))
printf $column_widths $i $loc_ds "$inc_snapshot (*)" $(human_size "${est_changes[$loc_ds]}") "$bak_ds (*)"
done done
fi fi
# --------- Выбор датасетов ---------- # Итоговая сумма
echo
echo "Общий объём изменений: $(human_size $total_bytes)"
echo
echo "Свободное место на пулах бэкап-сервера:"
# Сортируем по имени пула (опционально)
for pool in ${(ko)backup_pool_free}; do
printf " %-20s : %s\n" "$pool" "${backup_pool_free[$pool]}"
done
echo
# --------- Выбор датасетов ----------
total_bytes=0
if [[ $check = true ]]; then if [[ $check = true ]]; then
read 'work?Утвердите данные. "y" в работу | "1 2.." выбрать через пробел | НЕТ по умолчанию : ' read 'work?Утвердите данные. "y" в работу | "1 2.." выбрать через пробел | НЕТ по умолчанию : '
if [[ $work = *[[:digit:]]* ]]; then if [[ $work = *[[:digit:]]* ]]; then
@@ -354,13 +418,19 @@ if [[ $check = true ]]; then
if [[ ${indx_list[(r)$i]} != $i ]]; then # Если нет $i в списке $indx_list if [[ ${indx_list[(r)$i]} != $i ]]; then # Если нет $i в списке $indx_list
unset "work_datasets[$loc_ds]" unset "work_datasets[$loc_ds]"
else else
echo "$i - $loc_ds \t ${last_loc_snaps[$loc_ds]} \t -> \t${bak_ds}" (( total_bytes = est_changes[$loc_ds]))
printf $column_widths $i $loc_ds "${last_loc_snaps[$loc_ds]}" $(human_size "${est_changes[$loc_ds]}") $bak_ds
fi fi
done done
fi fi
# Если был выведен список, то запросить запуск в работу нового списка # Если был выведен список, то запросить запуск в работу нового списка
if [[ ${#indx_list} > 0 ]]; then if [[ ${#indx_list} > 0 ]]; then
# Итоговая сумма
total_human=$(numfmt --to=iec --format="%.2f" "$total_bytes" 2>/dev/null || echo "${total_bytes}B")
echo
echo "Общий объём изменений: $total_human"
echo
work="N" work="N"
read "work?В работу (y/N) " read "work?В работу (y/N) "
fi fi
@@ -419,7 +489,7 @@ fi
# Вывод прогресса # Вывод прогресса
# Это отдельный блок программы, который не имеет прямого доступа к переменным процесса в фоне. # Это отдельный блок программы, который не имеет прямого доступа к переменным процесса в фоне.
# Обмен переменными проивится ерез временный файл # Обмен переменными проивится ерез временный файл
if [[ $progress == true ]]; then if [[ $progress == true ]] && { (( ! ${+work} )) || [[ $work =~ ^[yY]$ ]]; }; then
echo "" echo ""
pids=$(ps -t $(tty | sed 's|/dev/||') -o pid,args | grep -e zfs_send.zsh -e "zfs send" | grep -v -- 'grep' | grep -v -- 'progress-only' ) # Список PIDs с командой запуска pids=$(ps -t $(tty | sed 's|/dev/||') -o pid,args | grep -e zfs_send.zsh -e "zfs send" | grep -v -- 'grep' | grep -v -- 'progress-only' ) # Список PIDs с командой запуска
@@ -431,7 +501,8 @@ if [[ $progress == true ]]; then
pids=$(ps a -o pid,args | grep -e zfs_send.zsh -e "zfs send" | grep -v -- 'grep' | grep -v -- 'progress-only' ) pids=$(ps a -o pid,args | grep -e zfs_send.zsh -e "zfs send" | grep -v -- 'grep' | grep -v -- 'progress-only' )
fi fi
fi fi
echo $pids
echo $pids
# Выводить прогресс запрашивая список запущенных процессов # Выводить прогресс запрашивая список запущенных процессов
if [[ $pids = "" ]]; then if [[ $pids = "" ]]; then
echo "Нет запущенного процесса резервирования" echo "Нет запущенного процесса резервирования"
@@ -451,8 +522,8 @@ echo $pids
time_changed_percent_value=$TS1 time_changed_percent_value=$TS1
n_tasks=$(< /dev/shm/backup_n_tasks) n_tasks=$(< /dev/shm/backup_n_tasks)
i_task=$(< /dev/shm/backup_i_task) while [[ $i_task -lt $n_tasks ]]; do # выводим статус пока не завершены все задачи
while [[ $i_task != $n_tasks ]]; do # выводим статус пока не завершены все задачи i_task=$(< /dev/shm/backup_i_task)
progress=$(ps -u | grep "sending" | grep -v "grep" | sed -r "s/(.*) zfs: (.*)/\2/") progress=$(ps -u | grep "sending" | grep -v "grep" | sed -r "s/(.*) zfs: (.*)/\2/")
# Извлекаем объем и процент # Извлекаем объем и процент
@@ -539,8 +610,15 @@ echo $pids
elapsed_total=$(( $(date +%s) - TS1 )) elapsed_total=$(( $(date +%s) - TS1 ))
fi fi
time_part_of_percent=$(( now - time_changed_percent_value )) # Время с последнего изменения процента time_part_of_percent=$(( now - time_changed_percent_value )) # Время с последнего изменения процента
estimated_total=$(( 100 / percent * elapsed_total )) # Всего времени на задачу # Используем awk для более точных расчётов
estimated_remain=$(( estimated_total - elapsed_total - time_part_of_percent )) # Осталось времени на задачу # estimated_total - Всего времени на задачу
# estimated_remain - Осталось времени на задачу
read estimated_total estimated_remain <<< $(awk -v p="$percent" -v t="$elapsed_total" -v tp="$time_part_of_percent" '
BEGIN {
total = (p > 0 ? (100 / p * t) : 0);
remain = (total > 0 ? (total - t - tp) : 0);
printf("%d %d", total, remain)
}')
if (( estimated_remain >= 86400 )) || (( estimated_total >= 86400 )); then # Если осталось более чем сутки, то отобразить дни if (( estimated_remain >= 86400 )) || (( estimated_total >= 86400 )); then # Если осталось более чем сутки, то отобразить дни
estimated_remain_days=$(( estimated_remain / 86400 )); estimated_remain_time=$(( estimated_remain % 86400 )) estimated_remain_days=$(( estimated_remain / 86400 )); estimated_remain_time=$(( estimated_remain % 86400 ))
estimated_total_days=$(( estimated_total / 86400 )); estimated_total_time=$(( estimated_total % 86400 )) estimated_total_days=$(( estimated_total / 86400 )); estimated_total_time=$(( estimated_total % 86400 ))
@@ -564,9 +642,11 @@ echo $pids
fi fi
# Удалить временные файлы состояний, если они существуют # Удалить временные файлы состояний, если они существуют
[ -f /dev/shm/backup_i_task ] && rm /dev/shm/backup_i_task [ -f /dev/shm/backup_i_task ] && rm /dev/shm/backup_i_task
[ -f /dev/shm//dev/shm/backup_time_start ] && rm /dev/shm/backup_time_start [ -f /dev/shm//dev/shm/backup_time_start ] && rm /dev/shm/backup_time_start
[ -f /dev/shm//dev/shm/backup_time_circle ] && rm /dev/shm/backup_time_circle [ -f /dev/shm//dev/shm/backup_time_circle ] && rm /dev/shm/backup_time_circle
# Обнуляем переменные после завершения задачи, чтобы избежать ошибок при повторном запуске
unset last_volume last_volume_time last_update_time last_percent time_changed_percent_value
fi fi