PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : find -printf für BusyBox



Seiten : [1] 2

BlackPanther
07.01.16, 12:45
Hallo ihr Lieben,

ich versuche gerade das rsync-snapshot.sh-Skript für eine Synology-Box anzupassen, da das dortige BusyBox einige Befehle nicht beherrscht.

Ich habe so weit alles gerade gebogen, hänge nun aber an dieser Zeile:

LNKDST=$(find "${SNAPSHOT_DST}/" -maxdepth 2 -type d -name "${NAME}.001" -printf " --link-dest=%p")
Er reklamiert das printf, das die BusyBox-Version laut Doku ja auch nicht unterstützt.

Hat mir jemand einen Rat, was ich tun kann? Bzw. wie lässt sich dieser Befehl evtl. umschreiben?

Danke schonmal!
BP

Aqualung
07.01.16, 14:47
Schau mal hier

https://busybox.net/downloads/BusyBox.html
(https://busybox.net/downloads/BusyBox.html)
ob Du mit den vorhandenen Optionen was basteln kannst.

BlackPanther
07.01.16, 15:05
Die Liste hatte ich bereits "studiert" - Fehlanzeige.
Allerdings muss ich leider zugeben, dass ich nicht gerade der Guru in Sachen Linux bin ...

Hättest du mir da also evtl. eine Lösung?
Das angesprochene Skript ist das hier: http://www.pointsoftware.ch/howto-local-and-remote-snapshot-backup-using-rsync-with-hard-links/

BP

BetterWorld
07.01.16, 15:07
Busybox is POSIX konform.
POSIX spezifiziert das Kommando printf.

Also einfach den Krempel in einer Pipe weiterleiten zu printf

BlackPanther
07.01.16, 15:17
OK!?

Hättest du mir da einen Tipp, wie das geht?
Ich versteh, was da abläuft - nur selber schreiben ist so ein Ding :/

Danke dir vielmals!!!!! :)

BP

BetterWorld
07.01.16, 15:28
Wenn ich das richtig sehe genügt

LNKDST=$(find "${SNAPSHOT_DST}/" -maxdepth 2 -type d -name "${NAME}.001" | read dest; printf " --link-dest=%s" $(basename $dest)

BlackPanther
07.01.16, 15:32
Alles klar, teste ich heute abend ...

Danke! :)
BP

BetterWorld
07.01.16, 15:36
ooos.
Ausgerechnet ich, der so viel Wert auf korrektes Quoting legt, mache solche Fehler!!!!

Korrekt ist:

LNKDST=$(find "${SNAPSHOT_DST}/" -maxdepth 2 -type d -name "${NAME}.001" | read dest; printf " --link-dest=%s" "$(basename "$dest")"

BlackPanther
07.01.16, 19:44
Scheint zu funktionieren, zumindest kommt kein Fehler.

Allerdings hängt er nun an einer anderen Stelle:

shopt -s extglob
Scheinbar kennt ash (oder erneut Busybox???) shopt nicht.

BP

BetterWorld
07.01.16, 20:15
Busybox verwendet die ash, die kein shopt kennt.

Versuche
set -o +<option_name> zum Abschalten (Einschalten -<name>)
oder
set +<option_name> zum Abschalten (Einschalten -<name>)
oder

BlackPanther
07.01.16, 21:16
Versuche
set -o +<option_name> zum Abschalten (Einschalten -<name>)
oder
set +<option_name> zum Abschalten (Einschalten -<name>)
Allerdings kennt set -o kein extglob :-/

BP

BetterWorld
07.01.16, 22:07
tja. Dann wirst du -wohl oder übel- alle Stellen des Scriptes, an denen extglobs zum Einsatz kommen, umschreiben müssen.

"Globs" gibt es bei der Bash einige.
Die extglobs sind weitergehende RegExe, die meist bei der Auswahl von Dateinamen eingesetzt werden.
(Finden auch noch in anderen Zusammenhang Anwendung, dürfte aber bei einem rsync Script weniger von Belang sein.)

Alternativ könntest du dir auf dem Zielsystem aber auch schlicht eine bash installieren.

BlackPanther
08.01.16, 15:22
So, bash installiert ... und was soll ich sagen? Läuft!

Nun noch ein paar Schönheitsschliffe:

ooos.
Ausgerechnet ich, der so viel Wert auf korrektes Quoting legt, mache solche Fehler!!!!

Korrekt ist:

LNKDST=$(find "${SNAPSHOT_DST}/" -maxdepth 2 -type d -name "${NAME}.001" | read dest; printf " --link-dest=%s" "$(basename "$dest")"

Nicht ganz! Irgendwo fehlt noch eine Klammer zu. Ich tippe auf den Schluss:

LNKDST=$(find "${SNAPSHOT_DST}/" -maxdepth 2 -type d -name "${NAME}.001" | read dest; printf " --link-dest=%s" "$(basename "$dest")")
Allerdings kommt dann eine Fehlermeldung:
--link-dest arg does not exist:

Irgendein Rat?

BP

EDIT: Ich vermute, beim ersten Durchlauf kommt die Meldung daher, dass es kein altes Backup gibt. Aber bei jedem folgenden????

BetterWorld
08.01.16, 15:51
Korrekt. Die letzte Klammer fehlt(e).


LNKDST=$(find "${SNAPSHOT_DST}/" -maxdepth 2 -type d -name "${NAME}.001" | read dest; printf " --link-dest=%s" "$(basename "$dest")" )

BlackPanther
08.01.16, 18:02
Korrekt. Die letzte Klammer fehlt(e).


LNKDST=$(find "${SNAPSHOT_DST}/" -maxdepth 2 -type d -name "${NAME}.001" | read dest; printf " --link-dest=%s" "$(basename "$dest")" )
Alles klar.

Eine Idee zur Fehlermeldung?

BP

BetterWorld
08.01.16, 18:37
Ja.
Es ist syntaktisch nicht korrekt. Eigentlich soll LINKDST=$( something ) "something" ausführen und das Resultat einsetzen.
Also würde die Zuweisung an LINDST="result_of_something" lauten.
Aber dazu kommt es nicht.

Die bash will ja zuerst das hier ausführen:
find "${SNAPSHOT_DST}/" -maxdepth 2 -type d -name "${NAME}.001" | read dest; printf " --link-dest=%s" "$(basename "$dest")"
Der erste Teil bis zum Kommandotrenner Strichpunkt funktioniert und setzt die temporäre Variable "dest":
Bis dahin parst die bash noch korrekt.
Ausgeführt ist noch gar nix. "Parsen" meint erst mal lesen&verstehen.

find "${SNAPSHOT_DST}/" -maxdepth 2 -type d -name "${NAME}.001" | read dest
Im zweitern Befehlsteil printf " --link-dest=%s" "$(basename "$dest")" geht auch noch einiges gut.
Das $(basename "$dest") liefert den nackten Dateinamen als Sring (egal ob mit oder Leerzeichen drin; es ist EIN String)
Und damit "druckt" printf --link-dest="ein Dateiname.irgendwas" (ohne die Quotes; die bash behandelt es als einen STring, er ist intern NICHT von solchen Quotes umgeben.
Also besser: Es würde drucken wollen.
Noch immer wird nur geparst und nichts ist ausgeführt.

Und nun?
Alles könnte bis hierher ausgeführt und zufriedenstellend erledigt werden.
Jetzt müsste noch die Zuweisungsklammer schliessen. Tut sie aber nicht.
Also "denkt" sich der Parser: Da muss jetzt noch was kommen. Entweder geht der printf - Befehl weiter, oder es kommt ein Befehlstrenner oder die Zuweisung mittels $() ProzessausführungUndErsetzung endet.

Da gar nichts mehr kommt, der Befehl also in jedem Falle fehlschlagen wird, bricht die bash ab und gibt schlicht
--link-dest arg does not exist: aus.
Das war ja das letzte, von wo ab die bash keinen Sinn mehr reinparsen konnte.

Willst du das wirklich ganz genau wissen, wirst du den Quellcode lesen müssen.
Good luck! :)



Das ist ein interner Parsingfehler der Bash.

Den zu interpretieren

BlackPanther
08.01.16, 21:03
Also was du mir sagen willst, hab ich verstanden - die für die Funktion des Skripts letztlich interessante Folgerung jedoch nicht.
Ist das nun lediglich eine Warnung der Syntax-Prüfung (die ich ignorieren kann) oder schlägt die Zeile fehl (und da muss eine andere Lösung her)?

Gruß
BP

BetterWorld
08.01.16, 21:14
Poste das aktuelle Script samt Originaldaten.

BlackPanther
08.01.16, 21:32
#!/bin/bash
# ----------------------------------------------------------------------
# created by francois scheurer on 20070323
# derivate from mikes handy rotating-filesystem-snapshot utility
# see http://www.mikerubel.org/computers/rsync_snapshots
# ----------------------------------------------------------------------
#rsync note:
# 1) rsync -avz /src/foo /dest => ok, creates /dest/foo, like cp -a /src/foo /dest
# 2) rsync -avz /src/foo/ /dest/foo => ok, creates /dest/foo, like cp -a /src/foo/. /dest/foo (or like cp -a /src/foo /dest)
# 3) rsync -avz /src/foo/ /dest/foo/ => ok, same as 2)
# 4) rsync -avz /src/foo/ /dest => dangerous!!! overwrite dest content, like cp -a /src/foo/. /dest
# solution: remove trailing / at /src/foo/ => 1)
# minor problem: rsync -avz /src/foo /dest/foo => creates /dest/foo/foo, like mkdir /dest/foo && cp -a /src/foo /dest/foo
# main options:
# -H --hard-links
# -a equals -rlptgoD (no -H,-A,-X)
# -r --recursive
# -l --links
# -p --perms
# -t --times
# -g --group
# -o --owner
# -D --devices --specials
# -x --one-file-system
# -S --sparse
# --numeric-ids
# useful options:
# -n --dry-run
# -z --compress
# -y --fuzzy
# --bwlimit=X limit disk IO to X kB/s
# -c --checksum
# -I --ignore-times
# --size-only
# other options:
# -v --verbose
# -P equals --progress --partial
# -h --human-readable
# --stats
# -e'ssh -o ServerAliveInterval=60'
# --delete
# --delete-delay
# --delete-excluded
# --ignore-existing
# -i --itemize-changes
# --stop-at
# --time-limit
# --rsh=\"ssh -p ${HOST_PORT} -i /root/.ssh/rsync_rsa -l root\"
# --rsync-path=\"/usr/bin/rsync\""
# quickcheck options:
# the default behavior is to skip files with same size & mtime on destination
# mtime = last data write access
# atime = last data read access (can be ignored with noatime mount option or with chattr +A)
# ctime = last inode change (write access, change of permission or ownership)
# note that a checksum is always done after a file synchronization/transfer
# --modify-window=X ignore mtime differences less or equal to X sec
# --size-only skip files with same size on destination (ignore mtime)
# -c --checksum skip files with same MD5 checksum on destination (ignore size & mtime, all files are read once, then the list of files to be resynchronized is read a second time, there is a lot of disk IO but network trafic is minimal if many files are identical; log includes only different files)
# -I --ignore-times never skip files (all files are resynchronized, all files are read once, there is more network trafic than with --checksum but less disk IO and hence is faster than --checksum if net is fast or if most files are different; log includes all files)
# --link-dest does the quickcheck on another reference-directory and makes hardlinks if quickcheck succeeds
# (however, if mtime is different and --perms is used, the reference file is copied in a new inode)
# see also this link for a rsync tutorial: http://www.thegeekstuff.com/2010/09/rsync-command-examples/
#todo:
# 'du' slow on many snapshot.X..done
# autokill after n minutes.
# if disk full, its better to replace the snapshot.001 than to cancel and have a very old backup (even if it may fail to create the snapshot and ends with 0 backups)..done
# rsync-snapshot for oracle redo logs..old
# 'find'-list with md5 signatures -> .gz file stored aside rsync.log.gz inside the snapshot.X folder; this file will be move to parent dir /backup/snapshot/localhost/ before deletion of a snapshot; this file will also be used to extract an incremental backup with tape-arch.sh..done (md5sum calculation with rsync-list.sh for acm14=18m58 and only 5m27 with a reference file. speedup is ~250-300%)
# realtime freedisk display with echo $(($(stat -f -c "%f" /backup/snapshot/) * 4096 / 1024))
# use authorized_keys with restriction of bash (command=) and set sshd_config with PermitRootLogin=forced-commands-only, see http://troy.jdmz.net/rsync/index.html http://www.snailbook.com/faq/restricted-scp.auto.html
# note: rsync lists all files in snapshot.X disregarding inclusion patterns, this is slow.




# ------------- the help page ------------------------------------------
if [ "$1" == "-h" ] || [ "$1" == "--help" ]; then
cat << "EOF"
Version 2.01 2013-01-16

USAGE: rsync-snapshot.sh HOST [--recheck]

PURPOSE: create a snapshot backup of the whole filesystem into the folder
'/backup/snapshot/HOST/snapshot.001'.
If HOST is 'localhost' it is replaced with the local hostname.
If HOST is a remote host then rsync over ssh is used to transfer the files
with a delta-transfer algorithm to transfer only minimal parts of the files
and improve speed; rsync uses for this the previous backup as reference.
This reference is also used to create hard links instead of files when
possible and thus save disk space. If original and reference file have
identical content but different timestamps or permissions then no hard link
is created.
A rotation of all backups renames snapshot.X into snapshot.X+1 and removes
backups with X>512. About 10 backups with non-linear distribution are kept
in rotation; for example with X=1,2,3,4,8,16,32,64,128,256,512.
The snapshots folders are protected read-only against all users including
root using 'chattr'.
The --recheck option forces a sync of all files even if they have same mtime
& size; it is can verify a backup and fix corrupted files;
--recheck recalculates also the MD5 integrity signatures without using the
last signature-file as precalculation.
Some features like filter rules, MD5, chattr, bwlimit and per server retention
policy can be configured by modifying the scripts directly.

FILES:
/backup/snapshot/rsync/rsync-snapshot.sh the backup script
/backup/snapshot/rsync/rsync-list.sh the md5 signature script
/backup/snapshot/rsync/rsync-include.txt the filter rules

Examples:
(nice -5 ./rsync-snapshot.sh >log &) ; tail -f log
cd /backup/snapshot; for i in $(ls -A); do nice -10 /backup/snapshot/rsync/rsync-snapshot.sh $i; done
EOF
exit 1
fi




# ------------- tuning options, file locations and constants -----------
SRC="$1" #name of backup source, may be a remote or local hostname
OPT="$2" #options (--recheck)
HOST_PORT=22 #port of source of backup
SCRIPT_PATH="/backup/snapshot/rsync"
SNAPSHOT_DST="/backup/snapshot" #destination folder
NAME="snapshot" #backup name
LOG="rsync.log"
MIN_MIBSIZE=100000 # older snapshots (except snapshot.001) are removed if free disk <= MIN_MIBSIZE. the script may exit without performing a backup if free disk is still short.
OVERWRITE_LAST=0 # if free disk space is too small, then this option let us remove snapshot.001 as well and retry once
MAX_MIBSIZE=3500000 # older snapshots (except snapshot.001) are removed if their size >= MAX_MIBSIZE. the script performs a backup even if their size is too big.
#old: SPEED=5 # 1 is slow, 100 is fast, 100000 faster and 0 does not use slow-down. this allows to avoid rsync consuming too much system performance
BWLIMIT=100000 # bandwidth limit in KiB/s. 0 does not use slow-down. this allows to avoid rsync consuming too much system performance
BACKUPSERVER="rembk" # this server connects to all other to download filesystems and create remote snapshot backups
MD5LIST=1 #to compute a list of md5 integrity signatures of all backuped files, need 'rsync-list.sh'
CHATTR=1 # to use 'chattr' command and protect the backups again modification and deletion
DU=1 # to use 'du' command and calculate the size of existing backups, disable it if you have many backups and it is getting too slow (for example on BACKUPSERVER)
SOURCE="/" #source folder to backup

HOST_LOCAL="$(hostname -s)" #local hostname
#HOST_SRC="${SRC:-${HOST_LOCAL}}" #explicit source hostname, default is local hostname
if [ -z "${SRC}" ] || [ "${SRC}" == "localhost" ]; then
HOST_SRC="${HOST_LOCAL}" #explicit source hostname, default is local hostname
else
HOST_SRC="${SRC}" #explicit source hostname
fi

if [ "${HOST_LOCAL}" == "${BACKUPSERVER}" ]; then #if we are on BACKUPSERVER then do some fine tuning
MD5LIST=1
MIN_MIBSIZE=35000 #needed free space for chunk-file tape-arch.sh
MAX_MIBSIZE=12000
DU=0 # NB: 'du' is currently disabled on BACKUPSERVER for performance reasons
elif [ "${HOST_LOCAL}" == "${HOST_SRC}" ]; then #else if we are on a generic server then do other some fine tuning
if [ "${HOST_SRC}" == "ZRHSV-TST01" ]; then
MIN_MIBSIZE=500; CHATTR=0; DU=0; MD5LIST=0
fi
fi




# ------------- initialization -----------------------------------------
shopt -s extglob #enable extended pattern matching operators

OPTION="--stats \
--recursive \
--links \
--perms \
--times \
--group \
--owner \
--devices \
--hard-links \
--numeric-ids \
--delete \
--delete-excluded \
--bwlimit=${BWLIMIT}"
# --progress
# --size-only
# --stop-at
# --time-limit
# --sparse

if [ "${HOST_SRC}" != "${HOST_LOCAL}" ]; then #option for a remote server
SOURCE="${HOST_SRC}:${SOURCE}"
OPTION="${OPTION} \
--compress \
--rsh=\"ssh -p ${HOST_PORT} -i /root/.ssh/rsync_rsa -l root\" \
--rsync-path=\"/usr/bin/rsync\""
fi
if [ "${OPT}" == "--recheck" ]; then
OPTION="${OPTION} \
--ignore-times"
elif [ -n "${OPT}" ]; then
echo "Try rsync-snapshot.sh --help ."
exit 2
fi




# ------------- check conditions ---------------------------------------
echo "$(date +%Y-%m-%d_%H:%M:%S) ${HOST_SRC}: === Snapshot backup is created into ${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.001 ==="
STARTDATE=$(date +%s)

# make sure we're running as root
if (($(id -u) != 0)); then
echo "Sorry, must be root. Exiting..."
echo "$(date +%Y-%m-%d_%H:%M:%S) ${HOST_SRC}: === Snapshot failed. ==="
exit 2
fi

# make sure we have a correct snapshot folder
if [ ! -d "${SNAPSHOT_DST}/${HOST_SRC}" ]; then
echo "Sorry, folder ${SNAPSHOT_DST}/${HOST_SRC} is missing. Exiting..."
echo "$(date +%Y-%m-%d_%H:%M:%S) ${HOST_SRC}: === Snapshot failed. ==="
exit 2
fi

# make sure we do not have started already rsync-snapshot.sh or rsync process (started by rsync-cp.sh or by a remote rsync-snapshot.sh) in the background.
if [ "${HOST_LOCAL}" != "${BACKUPSERVER}" ]; then #because BACKUPSERVER need sometimes to perform an rsync-cp.sh it must disable the check of "already started".
#RSYNCPID=$(pgrep -f "/bin/bash .*rsync-snapshot.sh")
#if ([ -n "${RSYNCPID}" ] && [ "${RSYNCPID}" != "$$" ]) #|| pgrep -x "rsync"
if pgrep -f "/bin/\w*sh \w*rsync-snapshot\.sh" | grep -qv "$$"; then
echo "Sorry, rsync is already running in the background. Exiting..."
echo "$(date +%Y-%m-%d_%H:%M:%S) ${HOST_SRC}: === Snapshot failed. ==="
exit 2
fi
fi




# ------------- remove some old backups --------------------------------
# remove certain snapshots to achieve an exponential distribution in time of the backups (1,2,4,8,...)
for b in 512 256 128 64 32 16 8 4; do
let a=b/2+1
let f=0 #this flag is set to 1 when we find the 1st snapshot in the range b..a
for i in $(seq -w "${b}" -1 "${a}"); do
# for i in $(seq -f'%03g' "${b}" -1 "${a}"); do
if [ -d "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${i}" ]; then
if [ "${f}" -eq 0 ]; then
let f=1
else
echo "$(date +%Y-%m-%d_%H:%M:%S) Removing ${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${i} ..."
[ "${CHATTR}" -eq 1 ] && chattr -R -i "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${i}" &>/dev/null
rm -rf "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${i}"
fi
fi
done
done

# remove additional backups if free disk space is short
remove_snapshot() {
local MIN_MIBSIZE2=$1
local MAX_MIBSIZE2=$2
for i in $(seq -w 512 -1 001); do
# for i in $(seq -f'%03g' 512 -1 001); do
if [ -d "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${i}" ] || [ ${i} -eq 1 ]; then
[ ! -h "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.last" ] && [ -d "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${i}" ] && ln -s "${NAME}.${i}" "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.last"
let d=0 #disk space used by snapshots and free disk space are ok
echo -n "$(date +%Y-%m-%d_%H:%M:%S) Checking free disk space... "
FREEDISK=$(df -m ${SNAPSHOT_DST} | tail -1 | sed -e 's/ */ /g' | cut -d" " -f4 | sed -e 's/M*//g')
echo -n "${FREEDISK} MiB free. "
if [ ${FREEDISK} -ge ${MIN_MIBSIZE2} ]; then
echo "Ok, bigger than ${MIN_MIBSIZE2} MiB."
if [ "${DU}" -eq 0 ]; then #avoid slow 'du'
break
else
echo -n "$(date +%Y-%m-%d_%H:%M:%S) Checking disk space used by ${SNAPSHOT_DST}/${HOST_SRC} ... "
USEDDISK=$(du -ms "${SNAPSHOT_DST}/${HOST_SRC}/" | cut -f1)
echo -n "${USEDDISK} MiB used. "
if [ ${USEDDISK} -le ${MAX_MIBSIZE2} ]; then
echo "Ok, smaller than ${MAX_MIBSIZE2} MiB."
break
else
let d=2 #disk space used by snapshots is too big
fi
fi
else
let d=1 #free disk space is too small
fi
if [ ${d} -ne 0 ]; then #we need to remove snapshots
if [ ${i} -ne 1 ]; then
echo "Removing ${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${i} ..."
[ "${CHATTR}" -eq 1 ] && chattr -R -i "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${i}" &>/dev/null
rm -rf "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${i}"
[ -h "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.last" ] && rm -f "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.last"
else #all snapshots except snapshot.001 are removed
if [ ${d} -eq 1 ]; then #snapshot.001 causes that free space is too small
if [ "${OVERWRITE_LAST}" -eq 1 ]; then #last chance: remove snapshot.001 and retry once
OVERWRITE_LAST=0
echo "Warning, free disk space will be smaller than ${MIN_MIBSIZE} MiB."
echo "$(date +%Y-%m-%d_%H:%M:%S) OVERWRITE_LAST enabled. Removing ${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.001 ..."
rm -rf "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.001"
[ -h "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.last" ] && rm -f "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.last"
else
for j in ${LNKDST//--link-dest=/}; do
if [ -d "${j}" ] && [ "${CHATTR}" -eq 1 ] && [ $(lsattr -d "${j}" | cut -b5) != "i" ]; then
chattr -R +i "${j}" &>/dev/null #undo unprotection that was needed to use hardlinks
fi
done
[ ! -h "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.last" ] && ln -s "${NAME}.${j}" "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.last"
echo "Sorry, free disk space will be smaller than ${MIN_MIBSIZE} MiB. Exiting..."
echo "$(date +%Y-%m-%d_%H:%M:%S) ${HOST_SRC}: === Snapshot failed. ==="
exit 2
fi
elif [ ${d} -eq 2 ]; then #snapshot.001 causes that disk space used by snapshots is too big
echo "Warning, disk space used by ${SNAPSHOT_DST}/${HOST_SRC} will be bigger than ${MAX_MIBSIZE} MiB. Continuing anyway..."
fi
fi
fi
fi
done
}

# perform an estimation of required disk space for the new backup
while :; do #this loop is executed a 2nd time if OVERWRITE_LAST was ==1 and snapshot.001 got removed
OOVERWRITE_LAST="${OVERWRITE_LAST}"
echo -n "$(date +%Y-%m-%d_%H:%M:%S) Testing needed free disk space ..."
mkdir -p "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.test-free-disk-space"
chmod -R 775 "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.test-free-disk-space"
cat /dev/null >"${SNAPSHOT_DST}/${HOST_SRC}/${LOG}"
LNKDST=$(find "${SNAPSHOT_DST}/" -maxdepth 2 -type d -name "${NAME}.001" | read dest; printf " --link-dest=%s" "$(basename "$dest")")
# LNKDST=$(find "${SNAPSHOT_DST}/" -maxdepth 2 -type d -name "${NAME}.001" -printf " --link-dest=%p")
for i in ${LNKDST//--link-dest=/}; do
if [ -d "${i}" ] && [ "${CHATTR}" -eq 1 ] && [ $(lsattr -d "${i}" | cut -b5) == "i" ]; then
chattr -R -i "${i}" &>/dev/null #unprotect last snapshots to use hardlinks
fi
done
eval rsync \
--dry-run \
${OPTION} \
--include-from="${SCRIPT_PATH}/rsync-include.txt" \
${LNKDST} \
"${SOURCE}" "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.test-free-disk-space" >>"${SNAPSHOT_DST}/${HOST_SRC}/${LOG}"
RES=$?
if [ "${RES}" -ne 0 ] && [ "${RES}" -ne 23 ] && [ "${RES}" -ne 24 ]; then
echo "Sorry, error in rsync execution (value ${RES}). Exiting..."
echo "$(date +%Y-%m-%d_%H:%M:%S) ${HOST_SRC}: === Snapshot failed. ==="
exit 2
fi
let i=$(tail -100 "${SNAPSHOT_DST}/${HOST_SRC}/${LOG}" | grep 'Total transferred file size:' | cut -d " " -f5)/1048576
echo " ${i} MiB needed."
rm -rf "${SNAPSHOT_DST}/${HOST_SRC}/${LOG}" "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.test-free-disk-space"
remove_snapshot $((${MIN_MIBSIZE} + ${i})) $((${MAX_MIBSIZE} - ${i}))
if [ "${OOVERWRITE_LAST}" == "${OVERWRITE_LAST}" ]; then #no need to retry
break
fi
done




# ------------- create the snapshot backup -----------------------------
# perform the filesystem backup using rsync and hard-links to the latest snapshot
# Note:
# -rsync behaves like cp --remove-destination by default, so the destination
# is unlinked first. If it were not so, this would copy over the other
# snapshot(s) too!
# -use --link-dest to hard-link when possible with previous snapshot,
# timestamps, permissions and ownerships are preserved
echo "$(date +%Y-%m-%d_%H:%M:%S) Creating folder ${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.000 ..."
mkdir -p "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.000"
chmod 775 "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.000"
cat /dev/null >"${SNAPSHOT_DST}/${HOST_SRC}/${LOG}"
echo -n "$(date +%Y-%m-%d_%H:%M:%S) Creating backup of ${HOST_SRC} into ${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.000"
if [ -n "${LNKDST}" ]; then
echo " hardlinked with${LNKDST//--link-dest=/} ..."
else
echo " not hardlinked ..."
fi
eval rsync \
-vv \
${OPTION} \
--include-from="${SCRIPT_PATH}/rsync-include.txt" \
${LNKDST} \
"${SOURCE}" "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.000" >>"${SNAPSHOT_DST}/${HOST_SRC}/${LOG}"
RES=$?
if [ "${RES}" -ne 0 ] && [ "${RES}" -ne 23 ] && [ "${RES}" -ne 24 ]; then
echo "Sorry, error in rsync execution (value ${RES}). Exiting..."
echo "$(date +%Y-%m-%d_%H:%M:%S) ${HOST_SRC}: === Snapshot failed. ==="
exit 2
fi
for i in ${LNKDST//--link-dest=/}; do
if [ -d "${i}" ] && [ "${CHATTR}" -eq 1 ] && [ $(lsattr -d "${i}" | cut -b5) != "i" ]; then
chattr -R +i "${i}" &>/dev/null #undo unprotection that was needed to use hardlinks
fi
done
mv "${SNAPSHOT_DST}/${HOST_SRC}/${LOG}" "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.000/${LOG}"
gzip -f "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.000/${LOG}"




# ------------- create the MD5 integrity signature ---------------------
# create a gziped 'find'-list of all snapshot files (including md5 signatures)
if [ "${MD5LIST}" -eq 1 ]; then
echo "$(date +%Y-%m-%d_%H:%M:%S) Computing filelist with md5 signatures of ${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.000 ..."
OWD="$(pwd)"
cd "${SNAPSHOT_DST}"
# NOW=$(date "+%s")
# MYTZ=$(date "+%z")
# let NOW${MYTZ:0:1}=3600*${MYTZ:1:2}+60*${MYTZ:3:2} # convert localtime to UTC
# DATESTR=$(date -d "1970-01-01 $((${NOW} - 1)) sec" "+%Y-%m-%d_%H:%M:%S") # 'now - 1s' to avoid missing files
DATESTR=$(date -d "1970-01-01 UTC $(($(date +%s) - 1)) seconds" "+%Y-%m-%d_%H:%M:%S") # 'now - 1s' to avoid missing files
REF_LIST="$(find ${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.001/ -maxdepth 1 -type f -name 'snapshot.*.list.gz' 2>/dev/null)"
if [ -n "${REF_LIST}" ] && [ "${OPT}" != "--recheck" ]; then
REF_LIST2="/tmp/rsync-reflist.tmp"
gzip -dc "${REF_LIST}" >"${REF_LIST2}"
touch -r "${REF_LIST}" "${REF_LIST2}"
${SCRIPT_PATH}/rsync-list.sh "${HOST_SRC}/${NAME}.000" 0 "${REF_LIST2}" | sort -u | gzip -c >"${HOST_SRC}/${NAME}.${DATESTR}.list.gz"
rm -f "${REF_LIST2}"
else
${SCRIPT_PATH}/rsync-list.sh "${HOST_SRC}/${NAME}.000" 0 | sort -u | gzip -c >"${HOST_SRC}/${NAME}.${DATESTR}.list.gz"
fi
touch -d "${DATESTR/_/ }" "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${DATESTR}.list.gz"
cd "${OWD}"
[ ! -d "${SNAPSHOT_DST}/${HOST_SRC}/md5-log" ] && mkdir -p "${SNAPSHOT_DST}/${HOST_SRC}/md5-log"
cp -al "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${DATESTR}.list.gz" "${SNAPSHOT_DST}/${HOST_SRC}/md5-log/${NAME}.${DATESTR}.list.gz"
mv "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${DATESTR}.list.gz" "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.000/${NAME}.${DATESTR}.list.gz"
touch -d "${DATESTR/_/ }" "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.000"
fi




# ------------- finish and clean up ------------------------------------
# protect the backup against modification with chattr +immutable
if [ "${CHATTR}" -eq 1 ]; then
echo "$(date +%Y-%m-%d_%H:%M:%S) Setting recursively immutable flag of ${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.000 ..."
chattr -R +i "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.000" &>/dev/null
fi

# rotate the backups
if [ -d "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.512" ]; then #remove snapshot.512
echo "Removing ${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.512 ..."
[ "${CHATTR}" -eq 1 ] && chattr -R -i "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.512" &>/dev/null
rm -rf "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.512"
fi
[ -h "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.last" ] && rm -f "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.last"
for i in $(seq -w 511 -1 000); do
#for i in $(seq -f'%03g' 511 -1 000); do
if [ -d "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${i}" ]; then
let j=${i##+(0)}+1
j=$(printf "%.3d" "${j}")
echo "Renaming ${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${i} into ${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${j} ..."
[ "${CHATTR}" -eq 1 ] && chattr -i "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${i}" &>/dev/null
mv "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${i}" "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${j}"
[ "${CHATTR}" -eq 1 ] && chattr +i "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.${j}" &>/dev/null
[ ! -h "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.last" ] && ln -s "${NAME}.${j}" "${SNAPSHOT_DST}/${HOST_SRC}/${NAME}.last"
fi
done

# remove additional backups if free disk space is short
OVERWRITE_LAST=0 #next call of remove_snapshot() will not remove snapshot.001
remove_snapshot ${MIN_MIBSIZE} ${MAX_MIBSIZE}
echo "$(date +%Y-%m-%d_%H:%M:%S) ${HOST_SRC}: === Snapshot backup successfully done in $(($(date +%s) - ${STARTDATE})) sec. ==="
exit 0
#eof

BP

BlackPanther
11.01.16, 12:28
*push*

BP

BetterWorld
11.01.16, 14:33
Ein hirnkrankes Script.

Ist zwar explizit ein bash Script, verwendet aber Posixkonstrukte.
Und verwendet völlig ohne Grund eval.
Lösche den Käse besser.

Und die Zeile muss richtig lauten:

LNKDST=$(find "${SNAPSHOT_DST}/" -maxdepth 2 -type d -name "${NAME}.001" | while read dest; do
printf " --link-dest=%s" "$(basename "$dest")")
done
Da hab ich das while vergessen. Mea culpa.

BlackPanther
13.01.16, 08:39
Ein hirnkrankes Script.

Ist zwar explizit ein bash Script, verwendet aber Posixkonstrukte.
Und verwendet völlig ohne Grund eval.
Lösche den Käse besser.

Und die Zeile muss richtig lauten:

LNKDST=$(find "${SNAPSHOT_DST}/" -maxdepth 2 -type d -name "${NAME}.001" | while read dest; do
printf " --link-dest=%s" "$(basename "$dest")")
done
Da hab ich das while vergessen. Mea culpa.

Die Verwendung dieses Skripts ist leider nicht ganz auf meiner alleinigen Miste gewachsen - daher ist der Weg da wieder raus etwas schwer zu verkaufen. ;-)
Wenn es also irgendwie vertretbar ist, dann sollte ich es irgendwie zum Laufen bringen ...

Bei Ausführung wirft er nun 100.000fach diese Fehlermeldung aus:
Testing needed free disk space .../backup/snapshot/rsync/rsync-snapshot.sh: command substitution: line 326: syntax error: unexpected end of file
Ich vermute mal, für jede Datei ein Mal. Oder in Dauerschleife?

Sorry, dass ich dir mit diesem Sch**ß derart auf die Nerven geh - bin total verzweifelt!

BP

BetterWorld
13.01.16, 13:00
Klammer verrutscht.
LNKDST=$(find "${SNAPSHOT_DST}/" -maxdepth 2 -type d -name "${NAME}.001" | while read dest; do
printf " --link-dest=%s" "$(basename "$dest")" done)
Irgendwelche irgendwoher kopierte Scripte, die man nicht versteht, "verkaufen", ist keine "Geschäftsgrundlage".
Ziemlich übel.

Wieviel zahlst du mir?

BlackPanther
13.01.16, 13:35
Irgendwelche irgendwoher kopierte Scripte, die man nicht versteht, "verkaufen", ist keine "Geschäftsgrundlage".
Ziemlich übel.

Wieviel zahlst du mir?

Mit "verkaufen" war auch nicht verkaufen im Sinne von "Geld nehmen" gemeint - sondern "für gut heißen" ... ;)
Es geht da mehr um persönliche Glaubwürdigkeit ...

Niemand sagte, dass ich das Skript nicht verstehe. Ich weiß sehr wohl was es tut, ich verstehe welche Zeile was verantwortet.
Dennoch bin ich zwar mit etlichen Skript- und Programmiersprachen groß geworden - aber eben nicht unbedingt mit Linux.

Versteh mich bitte nicht falsch: Je mehr ich mich mit Linux beschäftige, desto toller gefallen mir auf Shell-Ebene diese ganzen Pipe-Geschichten - dennoch sind mir die ganzen Syntax-Regeln teilweise ein wenig gruselig, weil immer wieder anders als ich es nun erwarten würde ...
Einfaches Beispiel: dieses Semikolon in der while-Abfrage. Zwar logisch, dass das "read dest" ein bool zurück gibt, aber (für mich) nicht nachvollziehbar, weshalb das ein "Befehlsschlusszeichen" mitten in die noch nicht abgeschlossene Abfrage soll.

Daher meine Eingangs-Aussage: Lesen geht bei mir, verstehen auch - nur selber syntaktisch korrekt schreiben ist so ein Ding ...

Nun scheint es jedenfalls zu tun (freue mich hoffentlich nicht zu früh) und ich danke dir ganz recht herzlich! :)
Und wenn du noch ein kleines Fünkchen Geduld mit mir haben solltest, wäre ich dir dankbar, wenn du mir diese Meldungen noch auf "Ignorierbarkeit" prüfen könntest:

2016-01-13_14:13:31 Testing needed free disk space .../backup/snapshot/rsync/rsync-snapshot.sh: line 328: lsattr: command not found
/backup/snapshot/rsync/rsync-snapshot.sh: line 328: [: ==: unary operator expected
/backup/snapshot/rsync/rsync-snapshot.sh: line 328: lsattr: command not found
/backup/snapshot/rsync/rsync-snapshot.sh: line 328: [: ==: unary operator expected
/backup/snapshot/rsync/rsync-snapshot.sh: line 328: lsattr: command not found
/backup/snapshot/rsync/rsync-snapshot.sh: line 328: [: ==: unary operator expected
Warning: Identity file /root/.ssh/rsync_rsa not accessible: No such file or directory.
--link-dest arg does not exist: snapshot.001
--link-dest arg does not exist: snapshot.001
--link-dest arg does not exist: snapshot.001
[...]


EDIT:
Ich habe mittlerweile e2fslibs installiert - allerdings wirft lsattr nun die Fehlermeldung
lsattr: error while loading shared libraries: libe2p.so.2: cannot open shared object file: No such file or directory

Untertänigsten Dank nochmals! :)
BP

Ps: Ja, auf die verrutschte Klammer hätte ich auch selbst kommen können - bin ich nur leider irgendwie nicht :rolleyes:

BetterWorld
13.01.16, 14:30
Tja, Shellscripting ist eben NICHT Programmierung.
Eine Shell ist in erster Linie ein Kommandointerpreter.
Dem man sehr mächtige Konstrukte angeflanscht hat, um auch sehr komplexe Aufgaben zu erledigen.
Und deshalb folgt es eben NICHT dem normalen Programmierdenken.

Ein help while

looser@computer:~ $ help while
while: while Kommandos; do Kommandos; done
Execute commands as long as a test succeeds.

Expand and execute COMMANDS as long as the final command in the
`while' COMMANDS has an exit status of zero.

Exit Status:
Returns the status of the last command executed.
Bei normaler Programmierung erwartet man für das while eine Bedingung. In der Shellprogrammierung ist das eine ganze Reihe von Kommandos, wobei (meist) der Exitcode des zuletzt ausgeführten Kommandos als boolscher Wert INTERPRETIERT wird. ( 0 == OK; != Nicht OK )
Und damit lassen sich nette Sachen machen:

while \
command1
command2
command3 ; do
:
done In der eigentlichen Schleife wird nur die NOP Anweisung ausgeführt. Ich lasse also ein ganzes Teilscript laufen, bis command3 einen Fehler zurückgibt. Mit normaler Programmierung geht das nicht so einfach.
Und das gilt für so ziemlich alle "compound commands".

Das, die Kommandoliste abschließende ";" ist nötig. Man kann ja mehrzeilig schreiben, oder alles in eine Zeile packen. Als Befehlstrenner ist das Semikolon ODER ein Newline definiert. Um es eindeutig zu machen (auch um das Parsing der Shell zu vereinfachen) ist das Semikolon nötig.

Guck mal help if Das ist auch ein zusammengesetztes Kommando.
help gibt Hilfe zu internen bash Kommandos. man builtin listet alle Shell- internen Kommandos (Stichwort bash im POSIX- Mode)
Und man bash oder info bash bietet alle Bash- internen Kommandos.

Und das da:

[ $(lsattr -d "${i}" | cut -b5) == "i" ];
ist genau das, was ich meinte: Der Shebang identifiziert das Script als Bashscript, aber die Anweisung ist eine reine Shellanweisung.
Und dort ist halt einmal kein "==" zulässig. Schlicht ein Fehler.
Siehe help "[" und help "[["
Und dann gibt es noch man "[" (nicht auf allen Systemen) und man test.
Und damit das alles nicht gar so einfach ist, kann das Kommando "[" ein echtes Binary sein (ein file $(which "$") ergibt ELF-Loader...),
ODER ein Link auf test. Und die Bash hat ihr internes "[".
Wenn man test meint, sollte man auch test schreiben. (was alle schlechten Scripte im ganzen schlechten Netz nicht machen).
Das führt immer zu solchen Fehlern. Gerade bei schlechten Scripten, die nicht einmal alle Ausführungszweige testen. Die Zeile ist schlicht syntaktisch falsch.
Die Meldung [: ==: unary operator expected sagt auch genau das: = ist ein Zeichen, also unär. == sind zwei Zeichen, also binär.

line 328: lsattr: command not found Tja. Ein Kommando das nicht gefunden wird, ist entweder nicht da, oder woanders.
Finde es auf der Kiste, oder installiere es.
Wenn nicht, schreibe das Script entsprechend um.

Warning: Identity file /root/.ssh/rsync_rsa not accessible: No such file or directory. Da ein "Identity file" ja nur was mit Verschlüsselung zu tun hat, kann man das in diesen NSA-Zeiten getrost ignorieren. Das Script selbst hat wohl soviele Löcher, dass fehlende Verschlüsselung wohl ein eher geringes Problem sein wird.

Da es ja nicht um Geld sondern nur um Glauben geht,
erwarte ich noch viel mehr Kohle in meinem Klingelbeutel.

Und rate dringend vom Einsatz dieses Scriptes ab.

BlackPanther
13.01.16, 14:50
"Alles" klar.

Dann mache ich aus "==" ein "=", korrekt?

Und lsattr ist mittlerweile installiert und produziert nun eben ein
lsattr: error while loading shared libraries: libe2p.so.2: cannot open shared object file: No such file or directory
Ich vermute, das ist wieder so eine "Kastrier-Geschichte" von Synology.
Eine Idee, wie ich das zum Laufen bekomme?

Ich bete für dich! ;):D
BP

BetterWorld
13.01.16, 15:26
Meinst du nicht, dass du lieber Messdienern solltest?
Rechner sind nicht so wirklich dein Metier.

Wenn eine Lib fehlt, kann man die nachinstallieren, oder sich die Quellen besorgen und selbst kompilieren.
libe2p.so.2 lib== Library so==SharedObject 2==Hauptversionsnummer

Wie lange willst du an deinem Irrglauben noch festhalten?

BlackPanther
13.01.16, 15:45
Meinst du nicht, dass du lieber Messdienern solltest?
Rechner sind nicht so wirklich dein Metier.

Wenn eine Lib fehlt, kann man die nachinstallieren, oder sich die Quellen besorgen und selbst kompilieren.
libe2p.so.2 lib== Library so==SharedObject 2==Hauptversionsnummer

Wie lange willst du an deinem Irrglauben noch festhalten?
Ich will nicht - ich muss ;)

Rechner sind mein Metier, doch! Nur Linux nicht! ;)

hmmmm, und wie installiere ich libe2p? ipkg behauptet, es kenne das Paket nicht ... :(

BP

BetterWorld
13.01.16, 15:57
Sourcen besorgen.
Compilierumgebung installieren, falls noch nicht installiert.

Nach beigefügter INSTALL UND README vorgehen.
Im einfachsten Falle dann ein cd /Pfad/zu/source && ./configure && make && su -c "make install"

BlackPanther
14.01.16, 03:17
So.
Alles installiert - aber leider noch nicht im richtigen Verzeichnis.

Der aktuelle Ort ist /opt/lib/libe2p.so.2 und müsste /lib/libe2p.so.2 sein.

Bekomme ich die Kiste irgendwie dazu, diesen Suchpfad einfach mit in Betracht zu ziehen - ohne alles verschieben zu müssen oder mit Links zu schaffen?
Also so, dass nach einem evtl.en Upgrade des OS alles schnell rekonstruiert ist?

Beste nächtliche Grüße
BP