Document iscsi in the man page.
[debian-live-boot/hramrach.git] / bin / live-snapshot
blob08e5fd654adf08609dc9d7a411da83fc58b3f3a1
1 #!/bin/sh
3 # live-snapshot - utility to manage Debian Live systems snapshots
5 # This program mounts a device (fallback to /tmpfs under $MOUNTP
6 # and saves the /live/cow (or a different directory) filesystem in it
7 # for reuse in another live-boot session.
8 # Look at the manpage for more informations.
10 # Copyright (C) 2006-2008 Marco Amadori <marco.amadori@gmail.com>
11 # Copyright (C) 2008 Chris Lamb <chris@chris-lamb.co.uk>
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 # On Debian systems, the complete text of the GNU General Public License
27 # can be found in /usr/share/common-licenses/GPL-3 file.
29 # declare here two vars from /etc/live.conf because of "set -u"
30 ROOTSNAP=""
31 HOMESNAP=""
33 if [ -n "${LIVE_SNAPSHOT_CHECK_UNBOUND}" ]
34 then
35 set -eu
36 else
37 set -e
40 . /usr/share/initramfs-tools/scripts/live-helpers
42 LIVE_CONF="/etc/live.conf"
44 if [ -r "${LIVE_CONF}" ]
45 then
46 . "${LIVE_CONF}"
49 export USERNAME USERFULLNAME HOSTNAME
51 EXECUTABLE="${0}"
52 PROGRAM=$(basename "${EXECUTABLE}")
54 # Needs to be available at run and reboot time
55 SAFE_TMPDIR="/live"
57 # Permits multiple runs
58 MOUNTP="$(mktemp -d -p ${SAFE_TMPDIR} live-snapshot-mnt.XXXXXX)"
59 DEST="${MOUNTP}/live-sn.cpio.gz"
60 DEF_SNAP_COW="/live/cow"
61 TMP_FILELIST="${PROGRAM}.list"
63 # Command line defaults and declarations
64 SNAP_COW="${DEF_SNAP_COW}"
65 SNAP_DEV=""
66 SNAP_MNT=""
67 SNAP_OUTPUT=""
68 SNAP_RESYNC_STRING=""
69 SNAP_TYPE="cpio"
70 SNAP_LIST="/etc/live-snapshot.list"
71 EXCLUDE_LIST="/etc/live-snapshot.exclude_list"
73 Error ()
75 echo "${PROGRAM}: error:" ${@}
76 exit 1
79 panic ()
81 Error ${@}
84 Header ()
86 echo "${PROGRAM} - utility to perform snapshots of Debian Live systems"
87 echo
88 echo "usage: ${PROGRAM} [-c|--cow DIRECTORY] [-d|--device DEVICE] [-o|--output FILE] [-t|--type TYPE]"
89 echo " ${PROGRAM} [-r|--resync-string STRING]"
90 echo " ${PROGRAM} [-f|--refresh]"
91 echo " ${PROGRAM} [-h|--help]"
92 echo " ${PROGRAM} [-u|--usage]"
93 echo " ${PROGRAM} [-v|--version]"
96 Help ()
98 Header
100 echo
101 echo "Options:"
102 echo " -c, --cow: copy on write directory (default: ${SNAP_COW})."
103 echo " -d, --device: output snapshot device (default: ${SNAP_DEV:-auto})."
104 echo " -o, --output: output image file (default: ${DEST})."
105 echo " -r, --resync-string: internally used to resync previous made snapshots."
106 echo " -f, --refresh: try to sync a running snapshot."
107 echo " -t, --type: snapshot filesystem type. Options: \"squashfs\", \"ext2\", \"ext3\", \"ext4\", \"jffs2\" or \"cpio\".gz archive (default: ${SNAP_TYPE})"
108 echo
109 echo "Look at live-snapshot(1) man page for more information."
111 exit 0
114 Usage ()
116 Header
118 echo
119 echo "Try \"${PROGRAM} --help\" for more information."
121 exit 0
124 Version ()
126 echo "${PROGRAM}"
127 echo
128 echo "Copyright (C) 2006 Marco Amadori <marco.amadori@gmail.com>"
129 echo "Copyright (C) 2008 Chris Lamb <chris@chris-lamb.co.uk>"
130 echo
131 echo "This program is free software; you can redistribute it and/or modify"
132 echo "it under the terms of the GNU General Public License as published by"
133 echo "the Free Software Foundation; either version 2 of the License, or"
134 echo "(at your option) any later version."
135 echo
136 echo "This program is distributed in the hope that it will be useful,"
137 echo "but WITHOUT ANY WARRANTY; without even the implied warranty of"
138 echo "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"
139 echo "GNU General Public License for more details."
140 echo
141 echo "You should have received a copy of the GNU General Public License"
142 echo "along with this program; if not, write to the Free Software"
143 echo "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA"
144 echo
145 echo "On Debian systems, the complete text of the GNU General Public License"
146 echo "can be found in /usr/share/common-licenses/GPL-2 file."
147 echo
148 echo "Homepage: <http://debian-live.alioth.debian.org/>"
150 exit 0
153 Try_refresh ()
155 FOUND=""
156 if [ -n "${ROOTSNAP}" ]; then
157 "${EXECUTABLE}" --resync-string="${ROOTSNAP}"
158 FOUND="Yes"
161 if [ -n "${HOMESNAP}" ]; then
162 "${EXECUTABLE}" --resync-string="${HOMESNAP}"
163 FOUND="Yes"
166 if [ -z "${FOUND}" ]
167 then
168 echo "No autoconfigured snapshots found at boot;" > /dev/null 1>&2
169 echo "(no resync string in ${LIVE_CONF})." > /dev/null 1>&2
170 exit 1
174 Parse_args ()
176 # Parse command line
177 ARGS="${*}"
178 ARGUMENTS="$(getopt --longoptions cow:,device:,output,resync-string:,refresh,type:,help,usage,version --name=${PROGRAM} --options c:d:o:t:r:fhuv --shell sh -- ${ARGS})"
180 eval set -- "${ARGUMENTS}"
182 while true
184 case "${1}" in
185 -c|--cow)
186 SNAP_COW="${2}"
187 shift 2
190 -d|--device)
191 SNAP_DEV="${2}"
192 shift 2
195 -o|--output)
196 SNAP_OUTPUT="${2}"
197 shift 2
200 -t|--type)
201 SNAP_TYPE="${2}"
202 shift 2
205 -r|--resync-string)
206 SNAP_RESYNC_STRING="${2}"
207 break
210 -f|--refresh)
211 Try_refresh
212 exit 0
215 -h|--help)
216 Help
219 -u|--usage)
220 Usage
223 -v|--version)
224 Version
228 shift
229 break
233 Error "internal error."
236 esac
237 done
240 Defaults ()
242 # Parse resync string
243 if [ -n "${SNAP_RESYNC_STRING}" ]
244 then
245 SNAP_COW=$(echo "${SNAP_RESYNC_STRING}" | sed -e 's|^/root\([^:.]*\).*$|'"${DEF_SNAP_COW}"'\1|')
246 SNAP_DEV=$(echo "${SNAP_RESYNC_STRING}" | cut -f2 -d ':')
247 SNAP_MNT=$(echo "${SNAP_RESYNC_STRING}" | cut -f3 -d ':')
248 DEST="${MOUNTP}/${SNAP_MNT}"
250 case "${SNAP_MNT}" in
251 *.cpio.gz)
252 SNAP_TYPE="cpio"
255 *.squashfs)
256 SNAP_TYPE="squashfs"
259 *.jffs2)
260 SNAP_TYPE="jffs2"
263 *.ext2|*.ext3)
264 SNAP_TYPE="ext2"
268 SNAP_TYPE="whole_partition"
271 *.ext4)
272 SNAP_TYPE="ext4"
276 Error "unrecognized resync string"
278 esac
279 elif [ -z "${SNAP_OUTPUT}" ]
280 then
281 # Set target file based on image
282 case "${SNAP_TYPE}" in
283 cpio)
284 DEST="${MOUNTP}/live-sn.cpio.gz"
287 squashfs|jffs2|ext2)
288 DEST="${MOUNTP}/live-sn.${SNAP_TYPE}"
291 ext3)
292 DEST="${MOUNTP}/live-sn.ext2"
295 ext4)
296 DEST="${MOUNTP}/live-sn.ext4"
298 esac
299 else
300 DEST="${SNAP_OUTPUT}"
304 Validate_input ()
306 case "${SNAP_TYPE}" in
307 cpio|squashfs|jffs2|ext2|ext3|ext4|whole_partition)
311 Error "invalid filesystem type \"${SNAP_TYPE}\""
313 esac
315 if [ ! -d "${SNAP_COW}" ]
316 then
317 Error "${SNAP_COW} is not a directory"
320 if [ "$(id -u)" -ne 0 ]
321 then
322 Error "you are not root"
326 Mount_device ()
328 case "${SNAP_DEV}" in
330 # create a temp
331 mount -t tmpfs -o rw tmpfs "${MOUNTP}"
335 if [ -b "${SNAP_DEV}" ]
336 then
337 try_mount "${SNAP_DEV}" "${MOUNTP}" rw
340 esac
343 Entry_is_modified ()
345 # Returns true if file exists and it is also present in "cow" directory
346 # This means it is modified in respect to read-only media, so it deserve
347 # to be saved
349 entry="${1}"
351 if [ -e "${entry}" ] || [ -L "${entry}" ]
352 then
353 if [ -e "${SNAP_COW}/${entry}" ] || [ -L "${SNAP_COW}/${entry}" ]
354 then
355 return 0
358 return 1
361 Do_filelist ()
363 # BUGS: does not handle deleted files yet
364 TMP_FILELIST=$1
366 if [ -f "${SNAP_LIST}" ]
367 then
368 # if SNAP_COW == /live/cow/home, SNAP_RW = /home
369 SNAP_RW=$(echo "${SNAP_COW}" | sed -e "s|${DEF_SNAP_COW}||g")
370 if [ -z "${SNAP_RW}" ]
371 then
372 SNAP_RW="/"
375 cd "${SNAP_RW}"
376 # Generate include list removing empty and commented lines
377 # and transforming paths to relatives
378 for entry in $(sed -e '/^ *$/d' -e '/^#.*$/d' -e 's#^.*$#./&#' -e 's#/\+#/#g' "${SNAP_LIST}")
380 if [ -d "${entry}" ]
381 then
382 find "${entry}" | while read line
384 if Entry_is_modified "${line}"
385 then
386 printf "%s\000" "${line}" >> "${TMP_FILELIST}"
388 done
389 elif Entry_is_modified "${entry}"
390 then
391 # if file exists and it is modified
392 printf "%s\000" "${entry}" >> "${TMP_FILELIST}"
394 done
395 cd "${OLDPWD}"
397 # echo Working dir
398 echo "${SNAP_RW}"
399 else
400 cd "${SNAP_COW}"
401 # removing whiteouts from list
402 find . -path '*.wh.*' -prune -o -print0 >> "${TMP_FILELIST}"
403 cd "${OLDPWD}"
404 # echo Working dir
405 echo "${SNAP_COW}"
409 Do_snapshot ()
411 TMP_FILELIST=$(mktemp -p "${SAFE_TMPDIR}" "${TMP_FILELIST}.XXXXXX")
413 case "${SNAP_TYPE}" in
414 squashfs)
415 echo ".${TMP_FILELIST}" > "${TMP_FILELIST}"
416 # Removing whiteheads of unionfs
417 cd "${SNAP_COW}"
418 find . -name '*.wh.*' >> "${TMP_FILELIST}"
420 if [ -e "${EXCLUDE_LIST}" ]
421 then
422 # Add explicitly excluded files
423 grep -v '^#.*$' "${EXCLUDE_LIST}" | grep -v '^ *$' >> "${TMP_FILELIST}"
426 cd "${OLDPWD}"
427 mksquashfs "${SNAP_COW}" "${DEST}" -ef "${TMP_FILELIST}"
430 cpio|whole_partition)
431 if [ "${SNAP_TYPE}" = "cpio" ]
432 then
433 COPY_CMD="cpio --quiet -o0 -H newc | gzip -9c > ${DEST}"
434 else
435 COPY_CMD="cpio --quiet -pumd0 ${DEST}/"
438 WORKING_DIR=$(Do_filelist "${TMP_FILELIST}")
439 cd "${WORKING_DIR}"
440 if [ -e "${EXCLUDE_LIST}" ]
441 then
442 # Convert \0 to \n and tag existing (rare but possible) \n in filenames,
443 # this to let grep -F -v do a proper work in filtering out
444 cat "${TMP_FILELIST}" | \
445 tr '\n' '\1' | \
446 tr '\0' '\n' | \
447 grep -F -v -f "${EXCLUDE_LIST}" | \
448 tr '\n' '\0' | \
449 tr '\1' '\n' | \
450 eval $COPY_CMD || exit 1
451 else
452 cat "${TMP_FILELIST}" | \
453 eval $COPY_CMD || exit 1
455 cd "${OLDPWD}"
458 # ext2|ext3|ext4 and jffs2 does not easily support an exclude list; files
459 # should be copied to another directory in order to filter content
460 ext2|ext3|ext4)
461 DU_DIM="$(du -ks ${SNAP_COW} | cut -f1)"
462 REAL_DIM="$(expr ${DU_DIM} + ${DU_DIM} / 20)" # Just 5% more to be sure, need something more sophistcated here...
463 genext2fs --size-in-blocks=${REAL_DIM} --reserved-percentage=0 --root="${SNAP_COW}" "${DEST}"
466 jffs2)
467 mkfs.jffs2 --root="${SNAP_COW}" --output="${DEST}"
469 esac
471 if [ -f "${TMP_FILELIST}" ]
472 then
473 rm -f "${TMP_FILELIST}"
477 Clean ()
479 if [ -z "${SNAP_RESYNC_STRING}" ] && echo "${DEST}" | grep -q "${MOUNTP}"
480 then
481 echo "${DEST} is present on ${MOUNTP}, therefore no automatic unmounting the latter." > /dev/null 1>&2
482 else
483 umount "${MOUNTP}"
484 rmdir "${MOUNTP}"
488 Warn_user ()
490 if [ -z "${SNAP_RESYNC_STRING}" ]
491 then
492 case ${SNAP_TYPE} in
493 cpio|ext2|ext3|ext4)
494 echo "Please move ${DEST} (if is not already in it)" > /dev/null 1>&2
495 echo "in a supported writable partition (e.g ext3, vfat)." > /dev/null 1>&2
498 squashfs)
499 echo "To use ${DEST} you need to rebuild your media or add it" > /dev/null 1>&2
500 echo "to your multisession disc under the \"/live\" directory." > /dev/null 1>&2
503 jffs2)
504 echo "Please cat or flashcp ${DEST} to your partition in order to start using it." > /dev/null 1>&2
506 esac
508 if grep -qv persistent /proc/cmdline
509 then
510 echo "Remember to boot this live system with \"persistent\" specified at boot prompt." > /dev/null 1>&2
515 Main ()
517 Parse_args "${@}"
518 Defaults
519 Validate_input
520 trap 'Clean' EXIT
521 Mount_device
522 Do_snapshot
523 Warn_user
526 Main "${@:-}"