diff --git a/pi-gen-sources/00-teslausb-tweaks/files/teslausb_setup_variables.conf.sample b/pi-gen-sources/00-teslausb-tweaks/files/teslausb_setup_variables.conf.sample index fb50e3d2..934c1ebe 100644 --- a/pi-gen-sources/00-teslausb-tweaks/files/teslausb_setup_variables.conf.sample +++ b/pi-gen-sources/00-teslausb-tweaks/files/teslausb_setup_variables.conf.sample @@ -17,7 +17,7 @@ export SSID='your_ssid' export WIFIPASS='your_pass' # Variables for CIFS (Windows/Mac file sharing) archiving. -# If you want to use rsync or rclone, delete or comment out this section +# If you want to use rsync, rclone, or NFS, delete or comment out this section # and uncomment the rsync or rclone section below. export ARCHIVE_SYSTEM=cifs export ARCHIVE_SERVER=your_archive_name_or_ip @@ -48,6 +48,12 @@ export SHARE_PASSWORD='password' # The following is optional #export RCLONE_FLAGS=() +# Variables for NFS archiving +# Note: SHARE_NAME must be the exact export path on the NAS (e.g. /volume1/TeslaCam) +#export ARCHIVE_SYSTEM=nfs +#export ARCHIVE_SERVER=hostname_or_ip +#export SHARE_NAME='/volume1/TeslaCam' + # The DATA_DRIVE option (formerly USB_DRIVE) is used to specify that you want # to store recordings on a different drive than the boot drive. If you don't # plan on using an sd card at all (i.e. you are both booting from and storing diff --git a/run/archiveloop b/run/archiveloop index 64575cd7..28e0495a 100755 --- a/run/archiveloop +++ b/run/archiveloop @@ -78,9 +78,9 @@ then exit 1 ;; esac -elif [ "${ARCHIVE_SYSTEM:-none}" = "cifs" ] +elif [ "${ARCHIVE_SYSTEM:-none}" = "cifs" ] || [ "${ARCHIVE_SYSTEM:-none}" = "nfs" ] then - # For CIFS + # For CIFS and NFS [[ -n "${SHARE_NAME:-}" && -f /backingfiles/cam_disk.bin ]] && export ARCHIVE_MOUNT=/mnt/archive [[ -n "${MUSIC_SHARE_NAME:-}" && -f /backingfiles/music_disk.bin ]] && export MUSIC_ARCHIVE_MOUNT=/mnt/musicarchive fi diff --git a/run/nfs_archive/archive-clips.sh b/run/nfs_archive/archive-clips.sh new file mode 100644 index 00000000..3f03200d --- /dev/null +++ b/run/nfs_archive/archive-clips.sh @@ -0,0 +1,46 @@ +#!/bin/bash -eu + +function connectionmonitor { + while true + do + for _ in {1..5} + do + if timeout 6 /root/bin/archive-is-reachable.sh "$ARCHIVE_SERVER" + then + sleep 5 + continue 2 + fi + sleep 1 + done + log "connection dead, killing archive-clips" + killall rsync || true + sleep 2 + killall -9 rsync || true + kill -9 "$1" || true + return + done +} + +connectionmonitor $$ & + +rsynctmp=".teslausbtmp" +rm -rf "$ARCHIVE_MOUNT/${rsynctmp:?}" || true +mkdir -p "$ARCHIVE_MOUNT/$rsynctmp" + +rm -f /tmp/archive-rsync-cmd.log /tmp/archive-error.log + +while [ -n "${1+x}" ] +do + # Using --no-o --no-g to prevent permission errors on NFS root squashed shares + if ! (rsync -avhRL --no-o --no-g --remove-source-files --temp-dir="$rsynctmp" --no-perms --omit-dir-times --stats \ + --log-file=/tmp/archive-rsync-cmd.log --ignore-missing-args \ + --files-from="$2" "$1/" "$ARCHIVE_MOUNT" &> /tmp/rsynclog || [[ "$?" = "24" ]] ) + then + cat /tmp/archive-rsync-cmd.log /tmp/rsynclog > /tmp/archive-error.log + exit 1 + fi + shift 2 +done + +rm -rf "$ARCHIVE_MOUNT/${rsynctmp:?}" || true +kill %1 || true diff --git a/run/nfs_archive/archive-is-reachable.sh b/run/nfs_archive/archive-is-reachable.sh new file mode 100644 index 00000000..02792ad8 --- /dev/null +++ b/run/nfs_archive/archive-is-reachable.sh @@ -0,0 +1,4 @@ +#!/bin/bash -eu + +ARCHIVE_HOST_NAME="$1" +nc -z -w 5 "$ARCHIVE_HOST_NAME" 2049 > /dev/null 2>&1 diff --git a/run/nfs_archive/connect-archive.sh b/run/nfs_archive/connect-archive.sh new file mode 100644 index 00000000..af97c3db --- /dev/null +++ b/run/nfs_archive/connect-archive.sh @@ -0,0 +1,9 @@ +#!/bin/bash -eu + +function mount_if_set() { + local mount_point=$1 + [ -z "$mount_point" ] || ensure_mountpoint_is_mounted_with_retry "$mount_point" +} + +mount_if_set "${ARCHIVE_MOUNT:-}" +mount_if_set "${MUSIC_ARCHIVE_MOUNT:-}" diff --git a/run/nfs_archive/copy-music.sh b/run/nfs_archive/copy-music.sh new file mode 100644 index 00000000..fa2b925d --- /dev/null +++ b/run/nfs_archive/copy-music.sh @@ -0,0 +1,6 @@ +#!/bin/bash -eu + +ensure_music_file_is_mounted +/root/bin/copy-music.sh +trim_free_space "$MUSIC_MOUNT" +unmount_music_file diff --git a/run/nfs_archive/disconnect-archive.sh b/run/nfs_archive/disconnect-archive.sh new file mode 100644 index 00000000..799f1d11 --- /dev/null +++ b/run/nfs_archive/disconnect-archive.sh @@ -0,0 +1,22 @@ +#!/bin/bash -eu + +unmount_if_set() { + local mount_point=$1 + if [ -n "$mount_point" ] + then + if findmnt --mountpoint "$mount_point" > /dev/null + then + if timeout 10 umount -f -l "$mount_point" >> "$LOG_FILE" 2>&1 + then + log "Unmounted $mount_point." + else + log "Failed to unmount $mount_point." + fi + else + log "$mount_point already unmounted." + fi + fi +} + +unmount_if_set "${ARCHIVE_MOUNT:-}" & +unmount_if_set "${MUSIC_ARCHIVE_MOUNT:-}" & diff --git a/run/nfs_archive/verify-and-configure-archive.sh b/run/nfs_archive/verify-and-configure-archive.sh new file mode 100644 index 00000000..2bbb31ad --- /dev/null +++ b/run/nfs_archive/verify-and-configure-archive.sh @@ -0,0 +1,145 @@ +#!/bin/bash -eu + +function log_progress () { + if declare -F setup_progress > /dev/null + then + setup_progress "verify-and-configure-archive: $*" + return + fi + echo "verify-and-configure-archive: $1" +} + +function check_archive_server_reachable () { + log_progress "Verifying that the archive server $ARCHIVE_SERVER is reachable..." + local serverunreachable=false + local default_interface + default_interface=$(route | grep "^default" | awk '{print $NF}') + + # Check NFS Port 2049 + hping3 -c 1 -S -p 2049 "$ARCHIVE_SERVER" 1>/dev/null 2>&1 || + hping3 -c 1 -S -p 2049 -I "$default_interface" "$ARCHIVE_SERVER" 1>/dev/null 2>&1 || + serverunreachable=true + + if [ "$serverunreachable" = true ] + then + log_progress "STOP: The archive server $ARCHIVE_SERVER is unreachable on port 2049. Try specifying its IP address instead." + exit 1 + fi + + log_progress "The archive server is reachable." +} + +function check_archive_mountable () { + local test_mount_location="/tmp/archivetestmount" + local share_path="$1" + local mode="$2" + + log_progress "Verifying that the archive share is mountable..." + + if [ ! -e "$test_mount_location" ] + then + mkdir "$test_mount_location" + fi + + local mounted=false + + # NFS Mount Command + # Forced vers=3 for wider NAS compatibility (Unifi, Synology, etc) + # proto=tcp and nolock help with stability over wifi + local commandline="mount -t nfs '$ARCHIVE_SERVER:$share_path' '$test_mount_location' -o 'rw,noauto,nolock,proto=tcp,vers=3'" + + log_progress "Trying NFS mount command-line:" + log_progress "$commandline" + + if eval "$commandline" + then + mounted=true + fi + + if [ "$mounted" = false ] + then + log_progress "STOP: unable to mount archive share via NFS" + exit 1 + else + log_progress "The archive share is mountable." + if [ "$mode" = "rw" ] + then + if ! touch "$test_mount_location/testfile" + then + log_progress "STOP: archive share is not writeable. Check permissions on NAS." + umount "$test_mount_location" + exit 1 + fi + rm "$test_mount_location/testfile" + fi + fi + + umount "$test_mount_location" +} + +function install_required_packages () { + log_progress "Installing/updating required packages if needed" + apt-get -y --force-yes install hping3 nfs-common + if ! command -v nc > /dev/null + then + apt-get -y --force-yes install netcat || apt-get -y --force-yes install netcat-openbsd + fi + log_progress "Done" +} + +install_required_packages + +check_archive_server_reachable + +if [ -e /backingfiles/cam_disk.bin ] +then + check_archive_mountable "$SHARE_NAME" rw +fi + +if [ -n "${MUSIC_SHARE_NAME:+x}" ] +then + if [ "$MUSIC_SIZE" = "0" ] + then + log_progress "STOP: MUSIC_SHARE_NAME specified but no music drive size specified" + exit 1 + fi + check_archive_mountable "$MUSIC_SHARE_NAME" ro +fi + +function configure_archive () { + log_progress "Configuring the archive..." + + local archive_path="/mnt/archive" + local music_archive_path="/mnt/musicarchive" + + if [ ! -e "$archive_path" ] && [ -e /backingfiles/cam_disk.bin ] + then + mkdir "$archive_path" + fi + + # Remove existing NFS entries to prevent duplicates + sed -i "/^.* nfs .*$/ d" /etc/fstab + + if [ -e /backingfiles/cam_disk.bin ] + then + echo "$ARCHIVE_SERVER:$SHARE_NAME $archive_path nfs rw,noauto,nolock,proto=tcp,vers=3 0 0" >> /etc/fstab + elif [ -d "$archive_path" ] + then + rmdir "$archive_path" || log_progress "failed to remove $archive_path" + fi + + if [ -n "${MUSIC_SHARE_NAME:+x}" ] + then + if [ ! -e "$music_archive_path" ] + then + mkdir "$music_archive_path" + fi + echo "$ARCHIVE_SERVER:$MUSIC_SHARE_NAME $music_archive_path nfs ro,noauto,nolock,proto=tcp,vers=3 0 0" >> /etc/fstab + elif [ -d "$music_archive_path" ] + then + rmdir "$music_archive_path" || log_progress "failed to remove $music_archive_path" + fi + log_progress "Configured the archive." +} + +configure_archive diff --git a/setup/pi/configure.sh b/setup/pi/configure.sh index a046bc4c..2435c367 100644 --- a/setup/pi/configure.sh +++ b/setup/pi/configure.sh @@ -139,6 +139,14 @@ function check_archive_configs () { check_variable "ARCHIVE_SERVER" check_rsync ;; + nfs) + if [ -e /backingfiles/cam_disk.bin ] + then + check_variable "SHARE_NAME" + fi + check_variable "ARCHIVE_SERVER" + check_rsync + ;; none) export ARCHIVE_SERVER=localhost ;; @@ -163,6 +171,9 @@ function get_archive_module () { cifs) echo "run/cifs_archive" ;; + nfs) + echo "run/nfs_archive" + ;; none) echo "run/none_archive" ;; @@ -365,7 +376,7 @@ function install_archive_scripts () { copy_script "$archive_module"/connect-archive.sh "$install_path" copy_script "$archive_module"/disconnect-archive.sh "$install_path" copy_script "$archive_module"/archive-is-reachable.sh "$install_path" - if [ -n "${MUSIC_SHARE_NAME:+x}" ] && grep cifs <<< "$archive_module" + if [ -n "${MUSIC_SHARE_NAME:+x}" ] && grep -E "cifs|nfs" <<< "$archive_module" then copy_script "$archive_module"/copy-music.sh "$install_path" fi diff --git a/setup/pi/setup-teslausb b/setup/pi/setup-teslausb index db8257bd..9c3134a8 100755 --- a/setup/pi/setup-teslausb +++ b/setup/pi/setup-teslausb @@ -615,6 +615,14 @@ function diagnose { else echo "ERROR: CIFS archiving selected, but archive not defined in fstab" fi + elif [ "${ARCHIVE_SYSTEM:-none}" = "nfs" ] + then + if grep -q '/mnt/archive' /etc/fstab + then + echo "NFS archiving selected" + else + echo "ERROR: NFS archiving selected, but archive not defined in fstab" + fi elif [ "${ARCHIVE_SYSTEM:-none}" = "rclone" ] then if [ ! -e "/root/.config/rclone/rclone.conf" ]