diff --git a/beadm b/beadm index 0b6ab4d..801da39 100755 --- a/beadm +++ b/beadm @@ -35,10 +35,6 @@ fi __usage() { local NAME=${0##*/} echo "usage:" - echo " ${NAME} subcommand cmd_options" - echo - echo " subcommands:" - echo echo " ${NAME} activate beName" echo " ${NAME} create [-e nonActiveBe | -e beName@snapshot] beName" echo " ${NAME} create beName@snapshot" @@ -62,7 +58,12 @@ __be_exist() { # 1=DATASET # check if argument is a snapshot __be_snapshot() { # 1=DATASET/SNAPSHOT - echo "${1}" | grep -q "@" + echo "${1}" | grep -q "@" 2> /dev/null +} + +# check if boot environment is mounted +__be_mounted() { # 1=BE + mount 2> /dev/null | grep -q -E "^${1} " 2> /dev/null } # create new boot environment @@ -76,7 +77,7 @@ __be_new() { # 1=SOURCE 2=TARGET do if ! zfs list -H -o name ${FS}@${SNAPSHOT} 1> /dev/null 2> /dev/null then - echo "ERROR: Child snapshot '${FS}@${SNAPSHOT}' does not exists" + echo "ERROR: Child snapshot '${FS}@${SNAPSHOT}' does not exist" exit 1 fi done @@ -86,12 +87,10 @@ __be_new() { # 1=SOURCE 2=TARGET echo "ERROR: Snapshot '${1}@${2##*/}' already exists" exit 1 fi - FMT=$( date "+%Y-%m-%d-%H:%M:%S" ) # NEW # - if ! zfs snapshot -r ${1}@${FMT} 1> /dev/null 2> /dev/null # NEW # - # if ! zfs snapshot -r ${1}@${2##*/} 1> /dev/null 2> /dev/null # old # + FMT=$( date "+%Y-%m-%d-%H:%M:%S" ) + if ! zfs snapshot -r ${1}@${FMT} 1> /dev/null 2> /dev/null # 2##*/ > FMT then - echo "ERROR: Cannot create snapshot '${1}@${FMT}'" # NEW # - # echo "ERROR: Cannot create snapshot '${1}@${2##*/}'" # old # + echo "ERROR: Cannot create snapshot '${1}@${FMT}'" # 2##*/ > FMT exit 1 fi fi @@ -114,11 +113,9 @@ EOF then zfs clone -o canmount=off ${OPTS} ${FS}@${1##*@} ${DATASET} else - # zfs clone -o canmount=off ${OPTS} ${FS}@${2##*/} ${DATASET} # old # - zfs clone -o canmount=off ${OPTS} ${FS}@${FMT} ${DATASET} # NEW # + zfs clone -o canmount=off ${OPTS} ${FS}@${FMT} ${DATASET} # 2##*/ > FMT fi done - echo "Created successfully" } ROOTFS=$( mount | awk '/ \/ / {print $1}' ) @@ -177,6 +174,7 @@ case ${1} in esac if [ "${2}" = "-S" ] then + # do the detailed space calculation [-S] USED_ALL=$( zfs list -H -t all -o name,used ) while read I do @@ -191,18 +189,18 @@ case ${1} in (*E) USED=$( echo ${USED} | awk '{TMP=gsub(/E/,""); TMP=$0*10; gsub(/\..*/,"",TMP); print TMP"00000000000000000"}' ) ;; (*Z) USED=$( echo ${USED} | awk '{TMP=gsub(/Z/,""); TMP=$0*10; gsub(/\..*/,"",TMP); print TMP"00000000000000000000"}' ) ;; esac - TOTAL=$( echo ${USED} + ${TOTAL} | bc ) + TOTAL=$( echo | awk -v used=${USED} -v total=${TOTAL} '{print used + total}' ) done << EOF $( zfs list -H -t all -o name,origin -r "${BENAME_STARTS_WITH}/${NAME}" | tr '\t' '\n' | grep -v "^-$" ) EOF - case $( echo "${TOTAL}" | wc -c | tr -c -d '[0-9]\n' ) in - (5|6|7) TOTAL=$( echo ${TOTAL} | awk '{printf (substr($0,0,length($0)-2)/10)"K"}' ); ;; - (8|9|10) TOTAL=$( echo ${TOTAL} | awk '{printf (substr($0,0,length($0)-5)/10)"M"}' ); ;; - (11|12|13) TOTAL=$( echo ${TOTAL} | awk '{printf (substr($0,0,length($0)-8)/10)"G"}' ); ;; - (14|15|16) TOTAL=$( echo ${TOTAL} | awk '{printf (substr($0,0,length($0)-11)/10)"T"}' ); ;; - (17|18|19) TOTAL=$( echo ${TOTAL} | awk '{printf (substr($0,0,length($0)-14)/10)"P"}' ); ;; - (20|21|22) TOTAL=$( echo ${TOTAL} | awk '{printf (substr($0,0,length($0)-17)/10)"E"}' ); ;; - (23|24|25) TOTAL=$( echo ${TOTAL} | awk '{printf (substr($0,0,length($0)-20)/10)"Z"}' ); ;; + case $( echo "${TOTAL}" | awk '{print length($0)}' ) in + (4|5|6) TOTAL=$( echo ${TOTAL} | awk '{printf (substr($0,0,length($0)-2)/10)"K"}' ); ;; + (7|8|9) TOTAL=$( echo ${TOTAL} | awk '{printf (substr($0,0,length($0)-5)/10)"M"}' ); ;; + (10|11|12) TOTAL=$( echo ${TOTAL} | awk '{printf (substr($0,0,length($0)-8)/10)"G"}' ); ;; + (13|14|15) TOTAL=$( echo ${TOTAL} | awk '{printf (substr($0,0,length($0)-11)/10)"T"}' ); ;; + (16|17|18) TOTAL=$( echo ${TOTAL} | awk '{printf (substr($0,0,length($0)-14)/10)"P"}' ); ;; + (19|20|21) TOTAL=$( echo ${TOTAL} | awk '{printf (substr($0,0,length($0)-17)/10)"E"}' ); ;; + (22|23|24) TOTAL=$( echo ${TOTAL} | awk '{printf (substr($0,0,length($0)-20)/10)"Z"}' ); ;; esac else TOTAL=${USED} @@ -235,7 +233,6 @@ EOF echo "ERROR: Cannot create '${2}' recursive snapshot" exit 1 fi - echo "Created successfully" else __be_new ${ROOTFS} ${POOL}/ROOT/${2} fi @@ -244,6 +241,7 @@ EOF __usage ;; esac + echo "Created successfully" ;; (activate) # ---------------------------------------------------------------- @@ -262,7 +260,7 @@ EOF MNT=$( mount | grep -E "^${POOL}/ROOT/${2} " | awk '{print $3}' ) if [ "${MNT}" != "/" ] then - echo "ERROR: The '${2}' is mounted at '${MNT}'" + echo "ERROR: Boot environment '${2}' is mounted at '${MNT}'" echo "ERROR: Cannot activate mounted boot environment" exit 1 fi @@ -308,7 +306,7 @@ EOF fi if ! zpool set bootfs=${POOL}/ROOT/${2} ${POOL} 2> /dev/null then - echo "ERROR: Failed to activate '${POOL}/ROOT/${2}'" + echo "ERROR: Failed to activate '${2}'" exit 1 fi fi @@ -319,7 +317,7 @@ EOF do zfs set canmount=noauto ${I} done - # enable automatic mount for active BE and promote it + # enable automatic mount for active boot environment and promote it zfs list -H -o name,origin -t filesystem -r ${POOL}/ROOT/${2} \ | while read I ORIGIN do @@ -355,7 +353,7 @@ EOF __be_exist ${POOL}/ROOT/${DESTROY} if [ "${BOOTFS}" = "${POOL}/ROOT/${DESTROY}" ] then - echo "ERROR: '${POOL}/ROOT/${2}' is current active boot environment" + echo "ERROR: Cannot destroy active boot environment" exit 1 fi case ${CHOICE} in @@ -366,7 +364,7 @@ EOF # destroy desired snapshot if ! zfs destroy -r ${POOL}/ROOT/${DESTROY} 1> /dev/null 2> /dev/null then - echo "ERROR: Snapshot '${2}' is origin for other boot environment(s)" + echo "ERROR: Snapshot '${2}' is origin for other boot environment" exit 1 fi else @@ -380,20 +378,39 @@ EOF fi done # get origins used by destroyed boot environment - ORIGINS=$( zfs list -H -t all -o origin -r ${POOL}/ROOT/${DESTROY} | grep -v '^-$' ) + ORIGIN_SNAPSHOTS=$( zfs list -H -t all -o origin -r ${POOL}/ROOT/${DESTROY} | grep -v '^-$' | awk -F "@" '{print $2}' | sort -u ) # destroy boot environment zfs destroy -r ${POOL}/ROOT/${DESTROY} - # destroy origins used by destroyed boot environment - echo "${ORIGINS}" \ - | while read I + # promote datasets dependent on origins used by destroyed boot environment + ALL_ORIGINS=$( zfs list -H -t all -o name,origin ) + echo "${ORIGIN_SNAPSHOTS}" \ + | while read S do - zfs destroy ${I} + echo "${ALL_ORIRINS}" \ + | grep "${S}" \ + | awk '{print $1}' \ + | while read I + do + zfs promote ${I} + done + done + # destroy origins used by destroyed boot environment + ALL_ORIGINS=$( zfs list -H -t all -o origin ) + echo "${ORIGIN_SNAPSHOTS}" \ + | while read S + do + echo "${ALL_ORIRINS}" \ + | grep "@${S}$" \ + | while read I + do + zfs destroy ${I} + done done fi echo "Destroyed successfully" ;; (*) - echo "'${DESTROY}' has not been destroyed" + echo "Boot environment '${DESTROY}' has not been destroyed" ;; esac ;; @@ -406,7 +423,7 @@ EOF __be_exist ${POOL}/ROOT/${2} if [ "${BOOTFS}" = "${POOL}/ROOT/${2}" ] then - echo "ERROR: Renaming the active BE is not supported" + echo "ERROR: Renaming active boot environment is not supported" exit 1 fi if zfs list -H -o name ${POOL}/ROOT/${3} 2> /dev/null @@ -419,63 +436,59 @@ EOF ;; (mount) # ------------------------------------------------------------------- - COMPLETE=1 - if [ ${#} -eq 1 -o ${#} -eq 2 ] + if [ ${#} -eq 1 ] then - if [ ${2} ] - then - BE=${2} - else - zfs list -H -o name -d 1 -r ${POOL}/ROOT \ - | grep "${POOL}/ROOT/" \ - | while read NAME - do - NAME=${NAME##*/} - if mount | grep -E "^${POOL}/ROOT/${NAME}" 1> /dev/null 2> /dev/null - then - echo ${NAME} - mount \ - | grep -E "^${POOL}/ROOT/${NAME}" \ - | awk '{print $1 " " $3}' \ - | column -t \ - | awk '{print " " $0}' - echo - fi - done - exit 0 - fi + zfs list -H -o name -d 1 -r ${POOL}/ROOT \ + | grep "${POOL}/ROOT/" \ + | while read NAME + do + NAME=${NAME##*/} + if mount | grep -q -E "^${POOL}/ROOT/${NAME}" 2> /dev/null + then + echo ${NAME} + mount \ + | grep -E "^${POOL}/ROOT/${NAME}" \ + | awk '{print $1 " " $3}' \ + | column -t \ + | awk '{print " " $0}' + echo + fi + done + exit 0 + elif [ ${#} -eq 2 ] + then + TARGET=$( mktemp -d /tmp/tmp.XXXXXX ) elif [ ${#} -eq 3 ] then - BE=${2} TARGET=${3} else __usage fi - __be_exist "${POOL}/ROOT/${BE}" - if mount | grep -E "^${POOL}/ROOT/${BE} " 1> /dev/null 2> /dev/null + __be_exist "${POOL}/ROOT/${2}" + if __be_mounted "${POOL}/ROOT/${2}" then - MNT=$( mount | grep -E "^${POOL}/ROOT/${BE} " | awk '{print $3}' ) - echo "The '${BE}' is already mounted at '${MNT}'" + MNT=$( mount | grep -E "^${POOL}/ROOT/${2} " | awk '{print $3}' ) + echo "Boot environment '${2}' is already mounted at '${MNT}'" exit 1 fi - if ! [ ${3} ] + if ! mkdir -p ${TARGET} 2> /dev/null then - TARGET=$( mktemp -d /tmp/tmp.XXXXXX ) - mkdir -p ${TARGET} - fi - if ! mount -t zfs ${POOL}/ROOT/${BE} ${TARGET} - then - echo "ERROR: Cannot mount '${POOL}/ROOT/${BE}' at '${TARGET}' mountpoint" + echo "ERROR: Cannot create '${TARGET}' mountpoint" exit 1 fi - PREFIX=$( echo ${POOL}/ROOT/${BE}/ | sed 's/\//\\\//g' ) - zfs list -H -o name,mountpoint -r ${POOL}/ROOT/${BE} \ + if ! mount -t zfs ${POOL}/ROOT/${2} ${TARGET} + then + echo "ERROR: Cannot mount '${2}' at '${TARGET}' mountpoint" + exit 1 + fi + PREFIX=$( echo ${POOL}/ROOT/${2}/ | sed 's/\//\\\//g' ) + zfs list -H -o name,mountpoint -r ${POOL}/ROOT/${2} \ | grep -v "legacy$" \ | sort -n \ - | grep -E "^${POOL}/ROOT/${BE}/" \ + | grep -E "^${POOL}/ROOT/${2}/" \ | while read FS MOUNTPOINT do - if [ "{FS}" != "${POOL}/ROOT/${BE}" ] + if [ "{FS}" != "${POOL}/ROOT/${2}" ] then INHERIT=$( zfs get -H -o source mountpoint ${FS} ) if [ "${INHERIT}" = "local" ] @@ -503,38 +516,33 @@ EOF echo "Mounted successfully on '${TARGET}'" ;; - (umount|unmount) # ------------------------------------------------------------------ - COMPLETE=1 + (umount|unmount) # ---------------------------------------------------------- if [ ${#} -eq 2 ] then - BE=${2} - elif [ ${#} -eq 3 ] + # we need this section for argument checking + : + elif [ ${#} -eq 3 -a "${2}" = "-f" ] then - BE=${3} - if [ "${2}" = "-f" ] - then - OPTS="-f" - else - __usage - fi + OPTS="-f" + shift else __usage fi - __be_exist "$POOL/ROOT/$BE" - if ! mount | grep -E "^${POOL}/ROOT/${BE} " 1> /dev/null 2> /dev/null + __be_exist "${POOL}/ROOT/${2}" + if ! __be_mounted "${POOL}/ROOT/${2}" then - echo "The '${BE}' is not mounted" + echo "Boot environment '${2}' is not mounted" exit 1 fi mount \ - | grep "^${POOL}/ROOT/${BE}" \ + | grep "^${POOL}/ROOT/${2}" \ | awk '{print $1}' \ | sort -n -r \ | while read FS do if ! umount ${OPTS} ${FS} 1> /dev/null 2> /dev/null then - echo "ERROR: Cannot umount '${FS}'" + echo "ERROR: Cannot umount '${FS}' dataset" exit 1 fi done