Цель и что даёт эта схема
Эта схема позволяет:
- Автоматически делать бэкап важных данных на внешний диск.
- Иметь понятный лог и статус последнего бэкапа.
- Иметь автозапуск:
- каждые N минут/часов по таймеру;
- с задержкой после подключения диска (чтобы он успел смонтироваться).
- Полностью восстановить систему после переустановки, просто выполнив шаги из этого файла.
1. Команды для повседневной работы
Эти команды считаем уже настроенными (после установки по шагам из части 3 и 4).
Основные команды бэкапа
bckp startЗапустить резервное копирование прямо сейчас (oneshot‑запуск user‑сервиса).bckp statusПоказать состояние user‑сервиса бэкапа (успешно / ошибка / когда в прошлый раз запускался).bckp last-logПоказать лог последнего запуска бэкапа (одна сессия, по разделителю).bckp logПоказать полный лог (все запуски, с ротацией старых успешных запусков).bckp now-logОнлайн‑просмотр лога, чтобы наблюдать прогресс.
Проверка автоматического запуска
- По таймеру (каждый час, от пользователя):
systemctl --user list-timers | grep axelbux-backupУбедиться, что есть таймер axelbux-backup.timer.
- По подключению диска (root‑триггер):
systemctl status axelbux-backup-on-plug.serviceПроверить, что сервис существует и последнее срабатывание было примерно во время подключения диска.
- Состояние user‑сервиса:
systemctl --user status axelbux-backup.serviceПроверить, как отработал последний запуск (код завершения, ошибки).
2. Как всё устроено и что можно менять
2.1. Что делает система в целом
- Копирует папку с документами (по умолчанию
/home/axelbux/Документы) на внешний диск/run/media/axelbux/AxelBux_4TB/BackUP. - Поддерживает зеркало текущего состояния: удалённые файлы перемещаются в
Trash_version. - Хранит историю изменений и ошибок в
backup.log, с мягкой ротацией (старые успешные сессии подчищаются после серии успешных запусков). - Показывает уведомления:
- при старте бэкапа:
BackUp - AxelBux_4TB / Резервное копирование начато; - при завершении:
BackUp - AxelBux_4TB / ...(успешно / с ошибками / прервано).
- при старте бэкапа:
- Не считает отсутствие подключённого диска ошибкой: такой запуск помечается как «ПРОПУЩЕНО» и завершает работу с кодом 0, без уведомлений.
2.2. Главные сущности схемы
- Основной скрипт бэкапа (логика rsync, логирование, статус):
~/.local/bin/run-backup.sh - User‑сервис и таймер (работают в контексте текущего пользователя, через
systemd --user):~/.config/systemd/user/axelbux-backup.service~/.config/systemd/user/axelbux-backup.timer - Утилита управления
bckp(человеческие команды):/usr/local/bin/bckp+ alias в~/.bashrc. - Root‑сервис для автозапуска по подключению диска:
/etc/systemd/system/axelbux-backup-on-plug.service - Правило udev‑триггера по событию «диск подключён»:
/etc/udev/rules.d/99-axelbux-backup.rules
2.3. Настройки в run-backup.sh
Все основные параметры меняются вверху скрипта:
SRC="/home/axelbux/Документы" # откуда копируем
MOUNT_POINT="/run/media/axelbux/AxelBux_4TB" # где смонтирован диск
DST="$MOUNT_POINT/BackUP" # куда копируем
TRASH="$DST/Trash_version" # куда складывать старые версии
LOG="$DST/backup.log" # основной лог
META="$DST/backup.meta" # счётчик успешных запусков
STATUS="$DST/backup.status" # статус последнего запуска
DISK_NAME="AxelBux_4TB" # имя диска для логов/уведомленийЧто можно менять:
- Поменять пользователя
- Заменить
/home/axelbuxна/home/НОВЫЙ_ЮЗЕР. - Выполнять установку и все
systemctl --userуже под этим пользователем. - Везде в этом документе мысленно подставить нового пользователя.
- Заменить
- Поменять диск и точку монтирования
Например, диск монтируется как /run/media/ivan/MyBackupDisk:
MOUNT_POINT="/run/media/ivan/MyBackupDisk"
DISK_NAME="MyBackupDisk"В udev‑правиле нужно также заменить ID_FS_LABEL на новую метку диска.
- Расширить / сузить объём данных
- Изменить
SRC, например:SRC="/home/axelbux"— будет копироваться весь домашний каталог. - Добавить
--excludeв команду rsync (ниже в коде) для игнорирования отдельных каталогов, например:
rsync -r -t -v --delete -u --modify-window=1 -s \
--backup --backup-dir="$TRASH" \
--exclude=".cache" \
--exclude="Загрузки" \
"$SRC" "$DST" >"$RSYNC_OUTPUT_FILE" 2>&1- Изменить частоту бэкапа по таймеру
В ~/.config/systemd/user/axelbux-backup.timer:
[Timer]
OnBootSec=10min
OnUnitActiveSec=60min # интервал между запусками
Unit=axelbux-backup.serviceМожно поставить, например, OnUnitActiveSec=30min или 2h.
2.4. Важная логика устойчивости
В run-backup.sh заложено:
- Проверка:
if [ ! -d "$MOUNT_POINT" ] || [ ! -w "$MOUNT_POINT" ]; then
exit 0
fiЕсли точка монтирования ещё не существует или недоступна для записи (udev сработал раньше автомонта) — скрипт тихо выходит с кодом 0.
- Дополнительная проверка
mountpoint -q "$MOUNT_POINT"— если диск не смонтирован, запуск помечается как «ПРОПУЩЕНО (диск не смонтирован)», пишется в лог иbackup.status, уведомлений нет. - Учёт количества подряд успешных запусков через
META. После 4 подряд успехов лог ротируется: старые сессии удаляются, остаётся только текущая. - Обработка кодов rsync:
0— успех;20— прервано (например, Ctrl+C или отключение диска);23— частичная передача (что‑то не скопировалось);- остальные — общая ошибка.
- Уведомления через
notify-send:- при старте:
"BackUp - ${DISK_NAME}" / "Резервное копирование начато"; - при завершении — разные тексты в зависимости от результата.
- при старте:
3. Полная установка с нуля (включая триггер по подключению диска)
Шаг 0. Предпосылки и зависимости
- Система: ALT Linux (или любая система с systemd, user‑сервисами и udev).
- Root‑доступ через
su. - Уведомления: нужен
notify-send(в ALT можно черезapt-get):
su
apt-get update
apt-get install notify-send
exitПроверка:
notify-send "Тест" "Если видишь это — уведомления работают"Шаг 1. Проверяем диск и создаём каталог для скриптов
- Подключить внешний диск и убедиться, что он смонтирован:
ls /run/media/$USERОжидаем увидеть AxelBux_4TB (или своё имя диска).
- Убедиться в метке диска и UUID (полезно для udev):
lsblk -o NAME,LABEL,UUIDУ меня, например:
sda1 AxelBux_4TB C80EE9B90EE9A124- Создать папку для пользовательских скриптов:
mkdir -p ~/.local/binШаг 2. Пишем основной скрипт run-backup.sh
Открыть файл:
nano ~/.local/bin/run-backup.shВставить полный текст актуального скрипта:
#!/bin/bash
# Настройки
SRC="/home/axelbux/Документы"
MOUNT_POINT="/run/media/axelbux/AxelBux_4TB"
DST="$MOUNT_POINT/BackUP"
TRASH="$DST/Trash_version"
LOG="$DST/backup.log"
META="$DST/backup.meta"
STATUS="$DST/backup.status"
DISK_NAME="AxelBux_4TB"
# Если точка монтирования ещё не существует или недоступна для записи,
# выходим без ошибки (udev может сработать раньше, чем диск смонтируется)
if [ ! -d "$MOUNT_POINT" ]; then
exit 0
fi
if [ ! -w "$MOUNT_POINT" ]; then
exit 0
fi
# --------------------------------------------------
# Вспомогательные функции
# --------------------------------------------------
# Чтение счётчика успешных подряд бэкапов
read_success_counter() {
if [ -f "$META" ]; then
SUCCESS_COUNT=$(cat "$META" 2>/dev/null)
else
SUCCESS_COUNT=0
fi
if ! [[ "$SUCCESS_COUNT" =~ ^[0-9]+$ ]]; then
SUCCESS_COUNT=0
fi
}
# Запись счётчика успешных подряд бэкапов
write_success_counter() {
echo "$SUCCESS_COUNT" > "$META"
}
# Запуск новой сессии логирования
start_new_session() {
# если лог существует и не пустой – добавить 3 пустых строки
if [ -f "$LOG" ] && [ -s "$LOG" ]; then
printf "\n\n\n" >> "$LOG"
fi
LINE_NO=0
SESSION_ERRORS=()
}
# Логирование строки с авто‑нумерацией
log_line() {
LINE_NO=$((LINE_NO + 1))
local now msg
now=$(date +"%Y-%m-%d %H:%M:%S")
msg="$1"
printf "[%04d] [%s] %s\n" "$LINE_NO" "$now" "$msg" >> "$LOG"
}
# Регистрация ошибки (для итогового блока)
register_error() {
local line_no="$1"
local text="$2"
SESSION_ERRORS+=("строка ${line_no}: ${text}")
}
# Отправка уведомления (попытка в пользовательскую сессию axelbux)
send_notify() {
local title="$1"
local body="$2"
notify-send "$title" "$body"
}
# --------------------------------------------------
# Основная логика
# --------------------------------------------------
START_TIME=$(date +"%Y-%m-%d %H:%M:%S")
RESULT="УСПЕШНО"
RSYNC_EXIT=0
mkdir -p "$DST" "$TRASH"
# Подготовка логирования и счётчика
start_new_session
read_success_counter
log_line "START: Начато резервное копирование на ${DISK_NAME}"
send_notify "BackUp - ${DISK_NAME}" "Резервное копирование начато"
# Проверка точки монтирования
if ! mountpoint -q "$MOUNT_POINT"; then
log_line "INFO: Диск ${DISK_NAME} не смонтирован. Бэкап пропущен (это не ошибка)."
END_TIME=$(date +"%Y-%m-%d %H:%M:%S")
log_line "SUMMARY: Дата/время начала: ${START_TIME}"
log_line "SUMMARY: Дата/время завершения: ${END_TIME}"
log_line "SUMMARY: Результат: ПРОПУЩЕНО (диск не смонтирован)"
log_line "SUMMARY: Ошибок: 0"
echo "${END_TIME}|ПРОПУЩЕНО|Диск не смонтирован (съёмный, это норм)" > "$STATUS"
# Никаких уведомлений и код завершения 0
exit 0
fi
log_line "INFO: Точка монтирования ${MOUNT_POINT} доступна"
log_line "INFO: Каталог назначения: ${DST}"
log_line "INFO: Каталог Trash_version: ${TRASH}"
log_line "INFO: Запуск rsync"
# Запуск rsync и захват вывода
RSYNC_OUTPUT_FILE="$(mktemp)"
rsync -r -t -v --delete -u --modify-window=1 -s \
--backup --backup-dir="$TRASH" \
"$SRC" "$DST" >"$RSYNC_OUTPUT_FILE" 2>&1
RSYNC_EXIT=$?
# Разбор вывода rsync построчно и логирование
while IFS= read -r line; do
log_line "RSYNC: ${line}"
done < "$RSYNC_OUTPUT_FILE"
rm -f "$RSYNC_OUTPUT_FILE"
END_TIME=$(date +"%Y-%m-%d %H:%M:%S")
# Определение результата по коду rsync
case "$RSYNC_EXIT" in
0)
RESULT="УСПЕШНО"
;;
20)
RESULT="ПРЕРВАНО"
register_error "$LINE_NO" "rsync прерван сигналом (код 20)"
;;
23)
RESULT="С ОШИБКАМИ"
register_error "$LINE_NO" "rsync завершился с частичной передачей (код 23). См. строки RSYNC: выше."
;;
*)
RESULT="С ОШИБКАМИ"
register_error "$LINE_NO" "rsync завершился с кодом ${RSYNC_EXIT}"
;;
esac
# Записываем статус для пользовательского сервиса
# Формат: ВРЕМЯ_ЗАВЕРШЕНИЯ|РЕЗУЛЬТАТ|ТЕКСТ
STATUS_TEXT="OK"
case "$RESULT" in
"УСПЕШНО")
STATUS_TEXT="Резервное копирование завершено!"
;;
"С ОШИБКАМИ")
STATUS_TEXT="Резервное копирование завершено с ОШИБКАМИ (код ${RSYNC_EXIT})"
;;
"ПРЕРВАНО")
STATUS_TEXT="Резервное копирование прервано (код ${RSYNC_EXIT})"
;;
esac
echo "${END_TIME}|${RESULT}|${STATUS_TEXT}" > "$STATUS"
# Обновление счётчика успешных сессий и ротация лога
if [ "$RESULT" = "УСПЕШНО" ]; then
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
if [ "$SUCCESS_COUNT" -ge 4 ]; then
TMP_LOG="$(mktemp)"
tac "$LOG" | awk 'BEGIN{empty=0} {if($0==""){empty++} else empty=0} {print} (empty==3){exit}' | tac > "$TMP_LOG"
mv "$TMP_LOG" "$LOG"
fi
else
SUCCESS_COUNT=0
fi
write_success_counter
# Итоговый блок
log_line "SUMMARY: Дата/время начала: ${START_TIME}"
log_line "SUMMARY: Дата/время завершения: ${END_TIME}"
log_line "SUMMARY: Результат: ${RESULT}"
log_line "SUMMARY: Код rsync: ${RSYNC_EXIT}"
log_line "SUMMARY: Ошибок: ${#SESSION_ERRORS[@]}"
if [ "${#SESSION_ERRORS[@]}" -gt 0 ]; then
log_line "SUMMARY: Ошибка(и):"
for err in "${SESSION_ERRORS[@]}"; do
log_line "SUMMARY: - ${err}"
done
else
log_line "SUMMARY: Ошибок не обнаружено"
fi
# Уведомления по результату
case "$RESULT" in
"УСПЕШНО")
send_notify "BackUp - ${DISK_NAME}" "Резервное копирование завершено!"
;;
"С ОШИБКАМИ")
send_notify "BackUp - ${DISK_NAME}" "Резервное копирование завершено с ОШИБКАМИ"
;;
"ПРЕРВАНО")
send_notify "BackUp - ${DISK_NAME}" "Резервное копирование прервано"
;;
esac
exit "$RSYNC_EXIT"Сохранить (Ctrl+O, Enter, Ctrl+X) и сделать исполняемым:
chmod +x ~/.local/bin/run-backup.shШаг 3. User‑service и timer (автозапуск каждый N минут)
Создать каталог для user‑юнитов:
mkdir -p ~/.config/systemd/user3.1. User‑service axelbux-backup.service
nano ~/.config/systemd/user/axelbux-backup.serviceСодержимое:
[Unit]
Description=AxelBux backup to 4TB disk (user)
After=graphical-session.target
[Service]
Type=oneshot
ExecStart=%h/.local/bin/run-backup.sh3.2. Таймер axelbux-backup.timer
nano ~/.config/systemd/user/axelbux-backup.timerСодержимое:
[Unit]
Description=Run AxelBux backup every hour (user)
[Timer]
OnBootSec=10min
OnUnitActiveSec=60min
Unit=axelbux-backup.service
[Install]
WantedBy=timers.targetАктивировать:
systemctl --user daemon-reload
systemctl --user enable --now axelbux-backup.timerПроверка:
systemctl --user list-timers | grep axelbux-backupШаг 4. Команда bckp
От root:
su
nano /usr/local/bin/bckpСодержимое:
#!/bin/bash
LOG="/run/media/axelbux/AxelBux_4TB/BackUP/backup.log"
SERVICE="axelbux-backup.service"
usage() {
echo "Использование: bckp {start|status|log|now-log|last-log}"
exit 1
}
cmd="$1"
case "$cmd" in
start)
systemctl --user start "$SERVICE"
;;
status)
systemctl --user status "$SERVICE"
;;
log)
[ -f "$LOG" ] && cat "$LOG" || echo "Лог-файл не найден: $LOG"
;;
now-log)
[ -f "$LOG" ] && tail -f "$LOG" || echo "Лог-файл не найден: $LOG"
;;
last-log)
if [ -f "$LOG" ]; then
tac "$LOG" | awk 'BEGIN{empty=0;printed=0} {
if($0==""){empty++} else empty=0
print
printed=1
if(empty==3){exit}
}' | tac
else
echo "Лог-файл не найден: $LOG"
fi
;;
*)
usage
;;
esacСделать исполняемым и выйти:
chmod +x /usr/local/bin/bckp
exitДобавить alias:
echo "alias bckp='/usr/local/bin/bckp'" >> ~/.bashrc
source ~/.bashrcШаг 5. Root‑сервис для автозапуска по подключению диска (с задержкой и уведомлениями)
- Узнать свои
DISPLAYиDBUS_SESSION_BUS_ADDRESSв графической сессии:
echo "$DISPLAY"
echo "$DBUS_SESSION_BUS_ADDRESS"Пример:
:0
unix:path=/run/user/1000/bus- Создать/обновить сервис:
su
nano /etc/systemd/system/axelbux-backup-on-plug.serviceСодержимое (подставь свои DISPLAY и DBUS_SESSION_BUS_ADDRESS, если отличаются):
[Unit]
Description=AxelBux backup to 4TB disk on plug
After=dev-sda1.device
[Service]
Type=oneshot
User=axelbux
Group=axelbux
Environment=DISPLAY=:0
Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
ExecStartPre=/bin/sleep 30
ExecStart=/home/axelbux/.local/bin/run-backup.shExecStartPre=/bin/sleep 30— задержка 30 секунд после появления устройства, чтобы диск успел смонтироваться.- Можно менять на
sleep 60, если нужно больше времени.
Применить:
systemctl daemon-reload
exitПроверка вручную:
su
systemctl start axelbux-backup-on-plug.service
exit
bckp last-logДолжен появиться свежий запуск, а также два уведомления: «начато» и «завершено».
Шаг 6. Udev‑правило: триггер по подключению диска
Создать правило:
su
nano /etc/udev/rules.d/99-axelbux-backup.rulesСодержимое (по метке):
ACTION=="add", KERNEL=="sd?1", ENV{ID_FS_LABEL}=="AxelBux_4TB", TAG+="systemd", ENV{SYSTEMD_WANTS}="axelbux-backup-on-plug.service"Если удобнее по UUID, можно вместо ID_FS_LABEL:
ACTION=="add", KERNEL=="sd?1", ENV{ID_FS_UUID}=="C80EE9B90EE9A124", TAG+="systemd", ENV{SYSTEMD_WANTS}="axelbux-backup-on-plug.service"Сохранить и обновить:
udevadm control --reload
exitПроверка:
- Отключить диск.
- Подключить диск.
- Подождать чуть больше 30 секунд.
- Убедиться:
bckp last-logВремя START должно быть примерно «время подключения + 30 секунд», а на рабочем столе должны появиться уведомления «BackUp - AxelBux_4TB / Резервное копирование начато» и затем «… завершено!».
4. Как полностью восстановить схему после переустановки системы
- Установить систему и пользователя (например,
axelbux). - Поставить
notify-sendи проверить уведомления. - Подключить диск с существующим
BackUPи логами, убедиться, что он монтируется как/run/media/axelbux/AxelBux_4TB. - Выполнить шаги:
- Шаг 1 (проверка диска, создание
~/.local/bin). - Шаг 2 (создать
run-backup.sh, сделать исполняемым). - Шаг 3 (user‑service и timer).
- Шаг 4 (
bckp). - Шаг 5 (root‑сервис с задержкой и окружением для уведомлений).
- Шаг 6 (udev‑правило).
- Шаг 1 (проверка диска, создание
После этого схема будет вести себя так же, как сейчас: бэкап запускается:
- по таймеру (каждый час, пока диск подключён);
- при подключении диска (через 30 секунд задержки), с уведомлениями о начале и завершении.