Инструменты пользователя

Инструменты сайта


wiki:kompjutery:raspberry_pi_3:raspberry_zoneminder_webmin

Raspberry, ZoneMinder, WebMin

В 2017-ом году возникла задача создания видео-наблюдения для одной камеры.
Решена задача на основе микро-компьютера «Raspberry Pi 3», usb-диска для ноутбука и системы ZoneMinder.
Но разговор далее пойдет не столько про ZoneMinder, сколько про «организацию» всего этого с максимально возможной надежностью.
Подобная «организация» была применена и на других малинках для решения других задач.

В интернете есть достаточно много информации про Raspberry, ZoneMinder и WebMin. Все, что здесь написано, получено из разных источников в интернете. Детально расписывать команды я не буду.

Что использовалось:

  • Raspberry Pi 3
  • microSD на 4 Гига
  • usb-диск на 640 Гиг
  • блок питания на 5В

microSD

Делаем загрузочную microSD на 4 Гига. На самом деле на ней нужен только ее первый раздел «boot», а все остальное будет «жить» на usb-диске. Но оставляем возможноть грузиться и с microSD в минимальном виде без ZoneMinder-а и WebMin-на. Т.е. после загрузки с microSD нужно доустановить пакеты типа «mc», через «raspi-config» настроить сеть, локаль, расширить файловую систему и т.д.
Разделы на microSD:

Device         Boot  Start     End Sectors  Size Id Type
/dev/mmcblk0p1        8192  532479  524288  256M  c W95 FAT32 (LBA)
/dev/mmcblk0p2      532480 7661567 7129088  3.4G 83 Linux

Так как microSD одного объема реально имеют немного разные объемы, для «переносимости» на другие microSD нужно немного уменьшить второй раздел примерно на 20-30 Мег. Это можно сделать с помощью SystemRescueCd.

Подключаем usb-диск и грузимся с microSD.

Разделы на usb-диске

Создаем разделы на usb-диске.

Device     Boot     Start        End   Sectors   Size Id Type
/dev/sda1            2048   67110911  67108864    32G  7 HPFS/NTFS/exFAT
/dev/sda2        67110912  943218687 876107776 417.8G 83 Linux
/dev/sda3       976773120  993550335  16777216     8G 83 Linux
/dev/sda4       993550336 1010327551  16777216     8G 83 Linux
  • sda1 - ntfs, для записи backup-ов microSD и восстановление microSD из dd-образов
  • sda2 - ext4, для данных (mysql, zoneminder, BackUp)
  • sda3 и sda4 - ext4, основной и резервные разделы для Raspbian (с установленными ZoneMinder и WebMin)

Небольшое пояснение. Собственно раздел sda1 (ntfs) нужен для обеспечения возможности «ручного» восстановления microSD на компьютере с ОС Windows, например с помощью Rufus.

С таким разбиением usb-диска имеем три варианта загрузки: с microSD и два с sda.

Немого про загрузку.
При подаче напряжения на малинку загрузчик читает всякую всячину из первого раздела microSD (mmcblk0p1). На последнем этапе чтения первого раздела считывается файл «cmdline.txt». В зависимости от его содержания и происходит дальнейшая загрузка ОС:

console=tty1 root=PARTUUID=6c586e13-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait

На данный момент «6c586e13-02» - второй раздел microSD и он будет рутовым.
Содержимое именно этого раздела позже скопируем на раздел sda3 usb-диска!

Создаем варианты загрузки

# ls -l /dev/disk/by-partuuid/
total 0
lrwxrwxrwx 1 root root 15 Oct 21 11:51 6c586e13-01 -> ../../mmcblk0p1
lrwxrwxrwx 1 root root 15 Oct 21 22:16 6c586e13-02 -> ../../mmcblk0p2
lrwxrwxrwx 1 root root 10 Oct 21 22:16 a3469a5d-01 -> ../../sda1
lrwxrwxrwx 1 root root 10 Oct 21 22:16 a3469a5d-02 -> ../../sda2
lrwxrwxrwx 1 root root 10 Oct 21 22:16 a3469a5d-03 -> ../../sda3
lrwxrwxrwx 1 root root 10 Oct 21 22:16 a3469a5d-04 -> ../../sda4

Создаем в «/boot» (первый раздел microSD) три файла:

  • cmdline.txt.sd:
    console=tty1 root=PARTUUID=6c586e13-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
  • cmdline.txt.sda3:
    console=tty1 root=PARTUUID=a3469a5d-03 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait rootdelay=5
  • cmdline.txt.sda4:
    console=tty1 root=PARTUUID=a3469a5d-04 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait rootdelay=5

В последних двух файлах рутовыми разделами становятся разделы 3 и 4 usb-диска и включена задержка загрузки на 5 секунд.

В дальнейшем командой, подобной этой:

root@zm:/boot
# cp cmdline.txt.sda3 cmdline.txt

будем переключать варианты загрузки.

Копирование ОС на sda3

Здесь приведу просто команды (они понятны):

root@zm:/
# mkdir /mnt/ntfs
root@zm:/
# mkdir /mnt/data
root@zm:/
# mkdir /mnt/sda3
root@zm:/
# mkdir /mnt/sda4
root@zm:/
# mount /dev/sda3 /mnt/sda3
root@zm:/
# cp -dpRx / /mnt/sda3/ 

Копирование может занять достаточно много времени, ждемс-с.

Готовим загрузку с раздела sda3

/mnt/sda3/etc/fstab:

/mnt/sda3/etc/fstab:

proc                   /proc       proc      defaults              0       0
#PARTUUID=6c586e13-01   /boot       vfat      defaults              0       2
PARTUUID=6c586e13-01   /boot       vfat      ro,noatime            0       0

PARTUUID=a3469a5d-03   /           ext4      defaults,noatime      0       1
PARTUUID=a3469a5d-04   /mnt/sda4   ext4      noatime,noauto        0       0

PARTUUID=a3469a5d-02   /mnt/data   ext4      defaults,noexec,acl   0       2

#/mnt/data/mysql        /var/lib/mysql          none    bind
#/mnt/data/zoneminder   /var/cache/zoneminder   none    bind

PARTUUID=a3469a5d-01   /mnt/ntfs   ntfs-3g   uid=1000,gid=1000,locale=ru_RU.UTF-8,umask=007

Внимание!
«/boot» монтируется «только по чтению»!
Рутовый раздел здесь - третий раздел usb-диска (sda3).

Грузимся с sda3

root@zm:/boot
# cp cmdline.txt.sda3 cmdline.txt
root@zm:/boot
# sync
root@zm:/boot
# reboot

На данном этапе установка пакетов, их настройка и т.д. будут записываться только на третий раздел usb-диска (sda3)!

MySQL

Написанное ниже применимо и к MariaDB.
После установки MySQL надо остановить сервер MySQL (например так):

root@zm:/
# /etc/init.d/mysql stop

После этого переносим любым удобным способом (например, «mc») каталог «mysql» из «/var/lib/» в «/mnt/data/» с сохранением атрибутов!
В «mc» это «[x] Preserve attributes».
Таким образом, данные сервера MySQL будут писаться в каталог «/mnt/data/mysql» (раздел sda2 usb-диска).

Меняем «/etc/fstab»:

Меняем «/etc/fstab»:

proc                   /proc       proc      defaults              0       0
#PARTUUID=6c586e13-01   /boot       vfat      defaults              0       2
PARTUUID=6c586e13-01   /boot       vfat      ro,noatime            0       0

PARTUUID=a3469a5d-03   /           ext4      defaults,noatime      0       1
PARTUUID=a3469a5d-04   /mnt/sda4   ext4      noatime,noauto        0       0

PARTUUID=a3469a5d-02   /mnt/data   ext4      defaults,noexec,acl   0       2

/mnt/data/mysql        /var/lib/mysql          none    bind
#/mnt/data/zoneminder   /var/cache/zoneminder   none    bind

PARTUUID=a3469a5d-01   /mnt/ntfs   ntfs-3g   uid=1000,gid=1000,locale=ru_RU.UTF-8,umask=007

Запускаем:

root@zm:/
# mount /mnt/data/mysql
root@zm:/
# /etc/init.d/mysql start

ZoneMinder

ZoneMinder:

ZoneMinder:

ZoneMinder

Аналогичную операцию по переносу производим с данными ZoneMinder после его установки:

  1. останавливаем zoneminder
  2. переносим «zoneminder» из «/var/cache/» в «/mnt/data/»
  3. убираем комментарий у строчки «#/mnt/data/zoneminder…» в «/etc/fstab»
  4. монтируем «/mnt/data/zoneminder»
  5. запускаем zoneminder

WebMin

А вот теперь самое интересное.

Стартовая страница:

Стартовая страница:

Стартовая страница

Страница «Custom Commands»:

Страница «Custom Commands»:

Страница "Custom Commands"


Некоторые пункты страницы «Custom Commands» работают самостоятельно, а некоторые через вызов bash-скриптов. Сктрипты находятся в «/home/pi/.scripts».

Пункты страницы «Custom Commands» харянтся в каталоге «/etc/webmin/custom» с менами подобными этим «1510403022.cmd» и «1510403022.html» (два файла на один пункт). Формат их достаточно простой. Приведу содержимое файлов «*.cmd», но в названии буду использовать названия пунктов из «Custom Commands»:

Backup SD (~ 5 dd + 8 gzip min.)

Backup SD (~ 5 dd + 8 gzip min.)

/home/pi/.scripts/BackupSD $gzip
Backup SD (~ 5 dd + 8 gzip min.)
root 0 0 0 1 0 0 0 -
gzip:9:echo yes; echo no|:0,0:create gzip

Вызывает скрипт «BackupSD» с передачей параметра.
Создает dd-образ microSD в каталоге «/mnt/ntfs/BackUp/sd» с именами подобными этому «zm_2019-10-19.sd» и, если передан парамеир «yes» сжимает этот образ в «/mnt/data/BackUp/sd» с именем «zm_2019-10-19.sd.gz».

Check SD Or dd-image SD

Check SD Or dd-image SD

/home/pi/.scripts/CheckSdOrImageSD $param
Check SD Or dd-image SD
root 0 0 0 1 0 0 0 -
param:9:echo "sd";find "/mnt/ntfs/BackUp/sd" -name *.sd -print | awk -F '/' '{print $NF;}' | sort -r|:0,0:sd or file

Вызывает скрипт «CheckSdOrImageSD»
и в зависимости от параметра проверяет саму microSD либо выбранный из списка ее dd-образ (список формируется автоматом).

CPUinfo

CPUinfo

cat /proc/cpuinfo
CPUinfo
* 0 0 0 1 0 0 0 -

Работает самостоятельно, ну и понятно для чего.

Firmware

Firmware

vcgencmd version
Firmware
* 0 0 0 1 0 0 0 -

Работает самостоятельно, ну и понятно для чего.

Installed to file

Installed to file

dpkg -l > /mnt/data/BackUp/Installed/zm_$(date +%F_%H.%M).installed
Installed to file
root 0 0 0 0 0 0 0 -

Работает самостоятельно, ну и понятно для чего.

Remount boot (rw)

Remount boot (rw)

mount | grep boot; mount -o remount,rw /dev/mmcblk0p1 /boot; mount | grep boot
Remount boot (rw)
root 0 0 0 1 0 0 0 -

Работает самостоятельно.
Внимание! Ранее я писал что первый раздел microSD («/boot») монтируется в режиме «только читать». Делается это для увеличения надежности работы. Но при обновлении пакетов ОС может возникнуть необходимость записи в этот раздел. Для этого и есть этот пункт. Перед обновлением пакетов ОС его надо вызвать и он перемонтирует первый раздел для чтения/записи.

Rsync Root To Sda (~ 20 min.)

Rsync Root To Sda (~ 20 min.)

/home/pi/.scripts/RsyncRootToSda
Rsync Root To Sda (~ 20 min.)
root 0 0 0 1 0 0 0 -

Вызывает скрипт «RsyncRootToSda» без параметров.
Важный скрипт! Работает только с разделами usb-диска!
Автоматичекси определяет рутовый раздел и синхронизирует с ним другой раздел usb-диска. Т.е. если рутовым разделом является sda3, то sda4 бодет синхронизирован с ним. И наоборот, sda3 синхронизируется если рутовый - sda4. В скрипте жетско «зашита» работа с sda3 и sda4 (править ручками при другой организации)!
Некоторые файлы и каталоги НЕ синхронизируются специально (например файл «fstab»)! Перед какими-либо действиями, да и просто регулярно лучше делать синхронизацию!

S.M.A.R.T sda to file

S.M.A.R.T sda to file

smartctl -a /dev/sda > /mnt/data/BackUp/SMART/zm_$(date +%F_%H.%M).smart
S.M.A.R.T sda to file
root 0 0 0 0 0 0 0 -

Работает самостоятельно, ну и понятно для чего.

Switch Root Partition And Reboot

Switch Root Partition And Reboot

if [ "$partition" = "show" ]; then mount | grep " / " | awk '{print $1}'; else /home/pi/.scripts/SwitchRootPartition $partition ; fi
Switch Root Partition And Reboot
root 0 0 0 1 0 0 0 -
partition:9:echo "show";echo "sda3";echo "sda4";echo "sd"|:0,0:root partition

Вызывает скрипт «SwitchRootPartition» с параметрами.
А это и есть «переключалка» загрузки.

Temperature CPU

Temperature CPU

vcgencmd measure_temp
Temperature CPU
* 0 0 0 0 0 0 0 -

Работает самостоятельно, ну и понятно для чего.

Volts

Volts

for id in core sdram_c sdram_i sdram_p; do echo "$id: $(vcgencmd measure_volts $id)"; done
Volts
* 0 0 0 1 0 0 0 -

Работает самостоятельно, ну и понятно для чего.

Скрипты

Теперь сами скрипты.

BackupSD

BackupSD

#!/bin/bash

# подключение дополнительных файлов, порядок важен
includeFiles=("Settings" "Functions")
# определяю откуда запущен скрипт
dir_scripts=`dirname $0`
# подключаю файл
for includeFile in "${includeFiles[@]}"; do
   if ! [ -f "${dir_scripts}/${includeFile}" ]; then
      echo "Не найден подключаемый файл \"${dir_scripts}/${includeFile}\""
      exit 0
   fi
   . "${dir_scripts}/${includeFile}"
done

# переменные
path_backup="/mnt/ntfs/BackUp/sd"
path_zip="/mnt/data/BackUp/sd"
backup_file="${prefix}_$(date +%F).sd"

# основной код
isUserRoot
is_runing

verifyDir "$path_backup"

if [ -f "${path_backup}/${backup_file}" ]; then
   rm ${path_backup}/${backup_file}
fi

# проверяю свободное место для образа
size_free=$(df -k ${path_backup} | grep '/' | awk '{print $4}')
# перевожу в мбайты
let "size_free /= 1000"
size_sd=$(fdisk -l | grep "/dev/mmcblk0:" | awk '{print $5}')
# перевожу в мбайты
let "size_sd /= 1000000"
# добавляю 10%
let "size_sd *= 11"
let "size_sd /= 10"
if [ "$size_sd" -gt "$size_free" ]; then
   echoExit "Мало свободного места для образа SD: $size_free мБ"
fi

checkPartition "/dev/mmcblk0p1"
checkPartition "/dev/mmcblk0p2"

echoTime "Создание dd-образа SD: ${path_backup}/${backup_file}..."
dd if=/dev/mmcblk0 of=${path_backup}/${backup_file} bs=1M
if [ $? -eq 0 ]; then
  echoTime "Создание образа SD прошло успешно."
  if [ "$1" = "yes" ]; then
    if [ -d "$path_zip" ]; then
      echoTime "Сжатие файла ${backup_file} в каталог: ${path_zip}..."
      gzip -ck --fast ${path_backup}/${backup_file} > ${path_zip}/${backup_file}.gz
      if [ $? ]; then
        echoTime "Файл ${backup_file} сжат успешно."
      else
        echoError "Ошибка сжатия файла ${backup_file}: $?!"
#        [ -f "${path_zip}/${backup_file}" ]  && rm ${path_zip}/${backup_file}
      fi
    else
      echoError "Нет каталога для сжатия файла ${backup_file}: ${path_zip}!"
    fi
  fi
else
  echoTime "Ошибка backup-а SD: $?!"
fi

mount /dev/mmcblk0p1

CheckSdOrImageSD

CheckSdOrImageSD

#!/bin/bash

# подключение дополнительных файлов, порядок важен
includeFiles=("Functions")
# определяю откуда запущен скрипт
dir_scripts=`dirname $0`
# подключаю файл
for includeFile in "${includeFiles[@]}"; do
   if ! [ -f "${dir_scripts}/${includeFile}" ]; then
      echo "Не найден подключаемый файл \"${dir_scripts}/${includeFile}\""
      exit 0
   fi
   . "${dir_scripts}/${includeFile}"
done

# переменные
path_backup="/mnt/ntfs/BackUp/sd"

# основной код
isUserRoot
is_runing

if [ "$1" == "sd" ]; then
   echo "Проверка SD..."
   checkPartition "/dev/mmcblk0p1"
   mountPartition "/dev/mmcblk0p1"
   checkPartition "/dev/mmcblk0p2"
   exit
fi

if ! [ -f "${path_backup}/${1}" ]; then
   echoExit "Не найден файл \"${path_backup}/${1}\""
fi

echoTime "Проверка dd-образа: $1"...
l_device=$(losetup -all | grep "${1}" | awk '{print $1}')
if [ -z "$l_device" ]; then
   l_device=$(losetup --find --partscan --show "${path_backup}/${1}")
   if ! [ $? -eq 0 ]; then
      echoExit "Ошибка loop-монтирования ${1}: $?!"
   fi
fi

checkPartition ${l_device}p1
checkPartition ${l_device}p2

losetup --detach "$l_device"

Functions

Functions

# Функции для скриптов
# Тело функции должно заканчиваться точкой с запятой или символом новой строки

function echoExit {
   echo "$1"
   echo "Скрипт завершил свою работу!"
   exit 0
}

function echoError {
   echo "$1"
}

function echoTime {
   echo " "
   if [ -n "$1" ]; then
      echo "$(date +%H:%M:%S) $1"
      echo "========"
   fi
}

function is_runing {
   name="${0##*/}"
   if [ $(pgrep -c $name) -gt 1 ]; then
      echo "Скрипт ($name) уже запущен!"
      echo "Повторный запуск этого скрипта возможен только после завершения предыдущего запуска!"
      echo "Дождитесь завершения или прервите работу предыдущего запуска!"
      echo " "
      exit 0
   fi
}

function isUserRoot {
   if [ $(whoami) != "root" ]; then
      echoExit "Запуск только под рутом!"
   fi
}

function verifyDir {
   if ! [ -d "$1" ]; then
      mkdir -p "$1"
      if ! [ $? ]; then
         echoExit "Ошибка создания каталога ${1}: $?"
      fi
   fi
}

function mountPartition {
   if [ -z "$(mount | grep "$1")" ]; then
      mount $1
      if ! [ $? -eq 0 ]; then
         echoExit "Ошибка монтирования $1: $?!"
      fi
   fi
}

function unmountPartition {
   if ! [ -z "$(mount | grep "$1")" ]; then
      umount $1
      if ! [ $? -eq 0 ]; then
         echoExit "Ошибка отмонтирования $1: $?!"
      fi
   fi
}

function checkPartition {
   if [ -n "$1" ]; then
      unmountPartition "$1"
      echoTime "Быстрая проверка файловой системы на $1..."
      fsck  -yf $1
      if ! [ $? -eq 0 ]; then
         echoExit "Ошибки файловой системы на $1: $?!"
      fi
   fi
}

RsyncRootToSda

RsyncRootToSda

#!/bin/bash

# подключение дополнительных файлов, порядок важен
includeFiles=("Functions")
# определяю откуда запущен скрипт
dir_scripts=`dirname $0`
# подключаю файл
for includeFile in "${includeFiles[@]}"; do
   if ! [ -f "${dir_scripts}/${includeFile}" ]; then
      echo "Не найден подключаемый файл \"${dir_scripts}/${includeFile}\""
      exit 0
   fi
   . "${dir_scripts}/${includeFile}"
done

# основной код
isUserRoot
is_runing

rsync_to=""
rsync_from=$(mount | grep " / " | awk '{print $1}')
rsync_exclude=("/lost+found" "/tmp" "fstab" "swap" "/var/tmp")

case "$rsync_from" in
   "/dev/sda3")
      rsync_to="/dev/sda4"
   ;;
   "/dev/sda4")
      rsync_to="/dev/sda3"
   ;;
esac

if [ -z "$rsync_to" ]; then
   echoExit "В настоящий момент малинка загружена с неизвестного раздела: ${rsync_from}!"
else
   rsync_to=$(fdisk -l | grep "${rsync_to}" | awk '{print $1}')
fi

if [ -z "$rsync_to" ]; then
   echoExit "На usb-диске не найден раздел для синхронизации!"
fi

checkPartition "$rsync_to"
mountPartition "$rsync_to"

exclude_str=""
echoTime "Исключения синхронизации:"
for exclude in "${rsync_exclude[@]}"; do
   echo "$exclude"
   exclude_str=${exclude_str}" --exclude "${exclude}
done

sync
echoTime "Синхронизация ${rsync_to} с рутовым разделом..."
rsync_to=$(mount | grep "$rsync_to" | awk '{print $3}')
## -n, --dry-run   perform a trial run with no changes made
rsync -avchx --delete-during ${exclude_str} / ${rsync_to}/
if [ $? -eq 0 ]; then
   sync
   echoTime "Синхронизация прошла успешно."
   unmountPartition "$rsync_to"
else
   echoError "Ошибка синхронизации: $?!"
fi

Settings

Settings

# различные значения, индивидуалные для каждой малинки

prefix="zm"

SwitchRootPartition

SwitchRootPartition

#!/bin/bash

# подключение дополнительных файлов, порядок важен
includeFiles=("Functions")
# определяю откуда запущен скрипт
dir_scripts=`dirname $0`
# подключаю файл
for includeFile in "${includeFiles[@]}"; do
   if ! [ -f "${dir_scripts}/${includeFile}" ]; then
      echo "Не найден подключаемый файл \"${dir_scripts}/${includeFile}\""
      exit 0
   fi
   . "${dir_scripts}/${includeFile}"
done

# основной код
isUserRoot
is_runing

root_partition=$(mount | grep " / " | awk '{print $1}')
if [ "$root_partition" == "/dev/$1" ]; then
   echoExit "Текущий рутовый раздел совпадает с выбранным."
fi

if [ -z "$(mount | grep "/boot")" ]; then
   echoExit "Раздел boot не примонтирован!"
fi

if ! [ -f "/boot/cmdline.txt.$1" ]; then
   echoExit "Не найден файл: /boot/cmdline.txt.$1"
fi

if [ "$1" == "sd" ]; then
   checkPartition "/dev/mmcblk0p2"
else
   checkPartition "/dev/$1"
fi

mount -o remount,rw /dev/mmcblk0p1 /boot
if ! [ $? -eq 0 ]; then
   echoExit "Ошибка перемонтирования раздела /boot!"
fi

cp "/boot/cmdline.txt.$1" "/boot/cmdline.txt"
if ! [ $? -eq 0 ]; then
   echoExit "Ошибка копирования файла /boot/cmdline.txt.$1 в файл /boot/cmdline.txt"
fi

echo " "
sync
echo "Рутовый раздел изменен на /dev/$1."
echo "Перезагрузка через 3 секунды!"
sleep 3
reboot


«Functions» и «Settings» - чисто вспомогалельные фпайлы и атрибут «исполняемый» им не нужен. Остальным нужно присвоить атрибут «исполняемый»!
В скриптах куча всяких проверок и выводом подробных сообщений в окно WebMin-а!

Есть еще один скрипт в каталоге «/root/.scripts»!

RestoreSD

RestoreSD

#!/bin/bash

# функции
function echoExit {
   echo "$1"
   echo "Скрипт завершил свою работу!"
   exit 0
}

function echoTime {
   echo " "
   if [ -n "$1" ]; then
      echo "$(date +%H:%M:%S) $1"
      echo "========"
   fi
}

function is_runing {
   name="${0##*/}"
   if [ $(pgrep -c $name) -gt 1 ]; then
      echo "Скрипт ($name) уже запущен!"
      echo "Повторный запуск этого скрипта возможен только после завершения предыдущего запуска!"
      echo "Дождитесь завершения или прервите работу предыдущего запуска!"
      echo " "
      exit 0
   fi
}

function mountPartition {
   if [ -z "$(mount | grep "$1")" ]; then
      mount $1
      if ! [ $? -eq 0 ]; then
         echoExit "Ошибка монтирования $1: $?!"
      fi
   fi
}

function unmountPartition {
   if ! [ -z "$(mount | grep "$1")" ]; then
      umount $1
      if ! [ $? -eq 0 ]; then
         echoExit "Ошибка отмонтирования $1: $?!"
      fi
   fi
}

function checkPartition {
   if [ -n "$1" ]; then
      unmountPartition "$1"
      echoTime "Быстрая проверка файловой системы на $1..."
      fsck  -yf $1
      if ! [ $? -eq 0 ]; then
         echoExit "Ошибки файловой системы на $1: $?!"
      fi
   fi
}

function restoreSD {
   if [ "$1" != "exit" ]; then
      echo " "
      echoTime "Восстановление SD из файла: $1"
   
      unmountPartition "/dev/mmcblk0p1"
      unmountPartition "/dev/mmcblk0p2"
      
      dd if=${path_backup}/$1 of=/dev/mmcblk0 bs=1M
      if [ $? -eq 0 ]; then
         echoTime "Восстановление SD прошло успешно."
         checkPartition "/dev/mmcblk0p1"
         mountPartition "/dev/mmcblk0p1"
         checkPartition "/dev/mmcblk0p2"
      else
         echoTime "Ошибка восстановления SD: $?!"
      fi
   fi
}

# переменные
path_backup="/mnt/ntfs/BackUp/sd"

# основной код
clear
echo " "
is_runing

echo "Восстановление SD (приблизительно 23 мин.)..."

#files=$(find "$path_backup" -name *.sd -print | awk -F '/' '{print $NF;}')

# в именах файлов *.sd НЕ должно быть пробелов!
# Если в каталоге нет ни одного файла, соответствующего шаблону,
# то за имя файла принимается сам шаблон.
# Чтобы избежать этого, используйте ключ nullglob
shopt -s nullglob
for file_sd in "${path_backup}/"*.sd; do
   files_list=( "${files_list[@]}" "${file_sd##*/}" )
done
# убираю ключ nullglob
shopt -u nullglob

# добавка последним элементом, для возможности выбора выхода
files_list=( "${files_list[@]}" "exit" )

if [ ${#files_list[@]} -le 1 ]; then
    echoExit "Не найдены файлы ${path_backup}/*.sd!"
fi

echo "Список найденных файлов:"
COLUMNS=12
PS3='Номер файла для восстановления:'
select file_restore in "${files_list[@]}"; do
   restoreSD "$file_restore"
   break
done

echoExit


Вот он и предназначен для восстановления microSD из dd-образа!
Работать с ним надо только в терминале и под рутом! И понятно, что малинка должна быть загружена с раздела usb-диска!

Подготовка sda4 для загрузки

Настало время для раздела sda4 usb-диска.
Напомню что малинка на данном этапе должна быть загружена с sda3 usb-диска!
Здесь тоже приведу просто команды (они понятны):

root@zm:/
# mount /dev/sda4 /mnt/sda4
root@zm:/
# cp -dpRx / /mnt/sda4/ 

Файл «/mnt/sda4/etc/fstab»:

Файл «/mnt/sda4/etc/fstab»:

proc                   /proc       proc      defaults              0       0
#PARTUUID=6c586e13-01   /boot       vfat      defaults              0       2
PARTUUID=6c586e13-01   /boot       vfat      ro,noatime            0       0

PARTUUID=a3469a5d-04   /           ext4      defaults,noatime      0       1
PARTUUID=a3469a5d-03   /mnt/sda3   ext4      noatime,noauto        0       0

PARTUUID=a3469a5d-02   /mnt/data   ext4      defaults,noexec,acl   0       2

/mnt/data/mysql        /var/lib/mysql          none    bind
/mnt/data/zoneminder   /var/cache/zoneminder   none    bind

PARTUUID=a3469a5d-01   /mnt/ntfs   ntfs-3g   uid=1000,gid=1000,locale=ru_RU.UTF-8,umask=007


Думаю понятны отличия от файла «/etc/fstab».
После этих действий можно переклюситься на загрузку с sda4 в WebMin-е.

Работа с полученной конфигурацией

Здесь собраны некоторые моменты и тонкости дальнейшей работы.

Обновление пакетов ОС

Это один из «болезненных» моментов работы с малинкой если обновления затрагивают загрузчик, firmware, драйвера и подобное!
Несколько пунктов кратко:

  1. Малинка должна быть загружена с раздела usb-диска (например sda3)!
  2. Перед обновлением нужно синхронизировать разделы sda usb-диска (пункт «Rsync Root To Sda (~ 20 min.)»)!
  3. Перемонтировать первый раздел microSD (пункт «Remount boot (rw)»)!
  4. Обновляем пакеты на usb-диске (рекомендую делать это в консоле).
  5. Переключаемся на загрузку с microSD (пункт «Switch Root Partition And Reboot») и обновляем пакеты уже на ней (консоль).
  6. Переключаемся обратно именно на sda3 (пункт «Switch Root Partition And Reboot»).
  7. Делаем пункт «Backup SD (~ 5 dd + 8 gzip min.)»!
  8. И если все хорошо, то делаем пункт «Rsync Root To Sda (~ 20 min.)»

Если что-то пошло не так, то есть dd-образ(ы) microSD («/mnt/ntfs/BackUp/sd») и предыдущая рабочая ОС на другом разделе usb-диска!
Такой способ «отката» использую даже в случае если пакет «криво» встал. Было такое с обновлением ZoneMinder-а. Тогда просто переключаемся на загрузку с раздел sda4, синхронизируем sda, и возвращаемся на загрузку с sda3.

Восстановление microSD

Здесь тоже есть некоторые тонкости.
Уже писал что одинаковые по объему флешки (microSD - тоже флешка) немного различаются по реальному объему. И писал что нужно немного уменьшить второй раздел флешки (SystemRescueCd).
При восстановлении флешки из dd-образа, dd может выдать что не хватает места. Если второй раздел все же поместился на флешку, то все нормально! А если нет, то тогда придется с помощью SystemRescueCd уменьшить второй раздел в самом dd-образе! И после этого опять «натравить» dd.
Столкнулся еще с одной «засадой» при переходе с «Raspbian Linux 9» на «Raspbian Linux 10»!
Причина «засады» в том, что в предыдущих образах Raspbian из интернета первый раздел в них был объемом около 50 Мег. В последующих образах объем уже был 256 Мег! И для обновление до «Raspbian Linux 10» банально не хватало места на первом разделе! SystemRescueCd и подобное ничем не помогли, увеличить первый раздел флешки (LBA) не получилось! Но это уже другая история ;))

wiki/kompjutery/raspberry_pi_3/raspberry_zoneminder_webmin.txt · Последнее изменение: 2025-02-01 09:39 — nik

Если не указано иное, содержимое этой вики предоставляется на условиях следующей лицензии: GNU Free Documentation License 1.3
GNU Free Documentation License 1.3 Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki