source: overlays/install-overlay/root/beadm.install @ fa1fd42

9.2-releasereleng/10.0releng/10.0.1
Last change on this file since fa1fd42 was fa1fd42, checked in by Kris Moore <kris@…>, 10 months ago

Fix a uname warning message

  • Property mode set to 100755
File size: 24.3 KB
Line 
1#!/bin/sh -e
2
3# Copyright (c) 2012 Slawomir Wojciech Wojtczak (vermaden). All rights reserved.
4# Copyright (c) 2012 Bryan Drewery (bdrewery). All rights reserved.
5# Copyright (c) 2012 Mike Clarke (rawthey). All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that following conditions are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS 'AS IS' AND ANY
16# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
19# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26unset LC_ALL
27unset LANG
28PATH=${PATH}:/bin:/usr/bin:/sbin:/usr/sbin
29
30if [ $( uname -r | cut -c 1 ) -lt 8 ]
31then
32  echo "ERROR: beadm works on FreeBSD 8.0 or later"
33  exit 1
34fi
35
36__usage() {
37  local NAME=${0##*/}
38  echo "usage:"
39  echo "  ${NAME} activate <beName>"
40  echo "  ${NAME} create [-e nonActiveBe | -e beName@snapshot] <beName>"
41  echo "  ${NAME} create <beName@snapshot>"
42  echo "  ${NAME} destroy [-F] <beName | beName@snapshot>"
43  echo "  ${NAME} list [-a] [-s] [-D] [-H]"
44  echo "  ${NAME} rename <origBeName> <newBeName>"
45  echo "  ${NAME} mount <beName> [mountpoint]"
46  echo "  ${NAME} { umount | unmount } [-f] <beName>"
47  exit 1
48}
49
50# check if boot environment exists
51__be_exist() { # 1=DATASET
52  if ! zfs list -H -o name ${1} 1> /dev/null 2> /dev/null
53  then
54    echo "ERROR: Boot environment '${1##*/}' does not exist"
55    exit 1
56  fi
57}
58
59# check if argument is a snapshot
60__be_snapshot() { # 1=DATASET/SNAPSHOT
61  echo "${1}" | grep -q "@" 2> /dev/null
62}
63
64# check if boot environment is mounted
65__be_mounted() { # 1=BE
66  mount 2> /dev/null | grep -q -E "^${1} " 2> /dev/null
67}
68
69# check if boot environment is a clone
70__be_clone() { # 1=DATASET
71  if zfs list ${1} 1> /dev/null 2> /dev/null
72  then
73    local ORIGIN="$( zfs list -H -o origin ${1} )"
74    if [ "${ORIGIN}" = "-" ]
75    then
76      # boot environment is not a clone
77      return 1
78    else
79      # boot environment is a clone
80      return 0
81    fi
82  else
83    # boot environment does not exist
84    return 2
85  fi
86}
87
88# create new boot environment
89__be_new() { # 1=SOURCE 2=TARGET
90  local SOURCE=$( echo ${1} | cut -d '@' -f 1 )
91  if __be_snapshot ${1}
92  then
93    # create boot environment from snapshot
94    local SNAPSHOT=$( echo ${1} | cut -d '@' -f 2 )
95    zfs list -r -H -t filesystem -o name ${SOURCE} \
96      | while read FS
97        do
98          if ! zfs list -H -o name ${FS}@${SNAPSHOT} 1> /dev/null 2> /dev/null
99          then
100            echo "ERROR: Child snapshot '${FS}@${SNAPSHOT}' does not exist"
101            exit 1
102          fi
103        done
104  else
105    # create boot environment from other boot environment
106    if zfs list -H -o name ${1}@${2##*/} 1> /dev/null 2> /dev/null
107    then
108      echo "ERROR: Snapshot '${1}@${2##*/}' already exists"
109      exit 1
110    fi
111    # snapshot format
112    FMT=$( date "+%Y-%m-%d-%H:%M:%S" )
113    if ! zfs snapshot -r ${1}@${FMT} 1> /dev/null 2> /dev/null
114    then
115      echo "ERROR: Cannot create snapshot '${1}@${FMT}'"
116      exit 1
117    fi
118  fi
119  # clone properties of source boot environment
120  zfs list -H -o name -r ${SOURCE} \
121    | while read FS
122      do
123        local OPTS=""
124        while read NAME PROPERTY VALUE
125        do
126          local OPTS="-o ${PROPERTY}=${VALUE} ${OPTS}"
127        done << EOF
128$( zfs get -o name,property,value -s local,received -H all ${FS} | awk '!/[\t ]canmount[\t ]/' )
129EOF
130        DATASET=$( echo ${FS} | awk '{print $1}' | sed -E s/"^${POOL}\/ROOT\/${SOURCE##*/}"/"${POOL}\/ROOT\/${2##*/}"/g )
131        if [ "${OPTS}" = "-o = " ]
132        then
133          local OPTS=""
134        fi
135        if __be_snapshot ${1}
136        then
137          zfs clone -o canmount=off ${OPTS} ${FS}@${1##*@} ${DATASET}
138        else
139          zfs clone -o canmount=off ${OPTS} ${FS}@${FMT} ${DATASET}
140        fi
141      done
142}
143
144ROOTFS=$( mount | awk '/ \/mnt / {print $1}' )
145
146if echo ${ROOTFS} | grep -q -m 1 -E "^/dev/"
147then
148  echo "ERROR: This system does not boot from ZFS pool"
149  exit 1
150fi
151
152POOL=$( echo ${ROOTFS} | awk -F '/' '{print $1}' )
153
154if ! zfs list ${POOL}/ROOT 1> /dev/null 2> /dev/null
155then
156  echo "ERROR: This system is not configured for boot environments"
157  exit 1
158fi
159
160BOOTFS=$( zpool list -H -o bootfs ${POOL} )
161
162if [ -z "${BOOTFS}" -o "${BOOTFS}" = "-" ]
163then
164 echo "ERROR: ZFS boot pool '${POOL}' has unset 'bootfs' property"
165 exit 1
166fi
167
168case ${1} in
169
170  (list) # --------------------------------------------------------------------
171    OPTION_a=0
172    OPTION_D=0
173    OPTION_s=0
174    shift
175    while getopts "aDHs" OPT
176    do
177      case ${OPT} in
178        (a) OPTION_a=1 ;;
179        (D) OPTION_D=1 ;;
180        (H) OPTION_H=1 ;;
181        (s) OPTION_s=1
182            OPTION_a=1 ;;
183        (*) __usage    ;;
184      esac
185    done
186    awk -v POOL="${POOL}" \
187        -v ROOTFS="${ROOTFS}" \
188        -v BOOTFS="${BOOTFS}" \
189        -v OPTION_a="${OPTION_a}" \
190        -v OPTION_D="${OPTION_D}" \
191        -v OPTION_H="${OPTION_H}" \
192        -v OPTION_s="${OPTION_s}" \
193     'function __normalize(VALUE) {
194        if(VALUE == "-" || VALUE == 0)
195          return 0
196        else
197          return substr(VALUE, 1, length(VALUE) - 1) * MULTIPLIER[substr(VALUE, length(VALUE))]
198      }
199      function __show_units(VALUE) {
200             if(VALUE < 1024)                {                               UNIT = "K"; }
201        else if(VALUE < 1048576)             { VALUE /= 1024;                UNIT = "M"; }
202        else if(VALUE < 1073741824)          { VALUE /= 1048576;             UNIT = "G"; }
203        else if(VALUE < 1099511627776)       { VALUE /= 1073741824;          UNIT = "T"; }
204        else if(VALUE < 1125899906842624)    { VALUE /= 1099511627776;       UNIT = "P"; }
205        else if(VALUE < 1152921504606846976) { VALUE /= 1125899906842624;    UNIT = "E"; }
206        else                                 { VALUE /= 1152921504606846976; UNIT = "Z"; }
207        return sprintf("%.1f%s", VALUE, UNIT)
208      }
209      function __get_bename(BENAME) {
210        sub(BENAME_BEGINS_WITH "\/", "", BENAME)
211        sub("/.*", "", BENAME)
212        return BENAME
213      }
214      function __convert_date(DATE) {
215        CMD_DATE = "date -j -f \"%a %b %d %H:%M %Y\" \"" DATE "\" +\"%Y-%m-%d %H:%M\""
216        CMD_DATE | getline NEW
217        close(CMD_DATE)
218        return NEW
219      }
220      BEGIN {
221        BENAME_BEGINS_WITH = POOL "/ROOT"
222        MULTIPLIER["K"] = 1
223        MULTIPLIER["M"] = 1024
224        MULTIPLIER["G"] = 1048576
225        MULTIPLIER["T"] = 1073741824
226        MULTIPLIER["P"] = 1099511627776
227        MULTIPLIER["E"] = 1125899906842624
228        MULTIPLIER["Z"] = 1152921504606846976
229        MOUNTPOINT_LENGTH = 10
230        FSNAME_LENGTH = 2
231        if(OPTION_a == 1)
232          FSNAME_LENGTH = 19
233        CMD_MOUNT="mount"
234        while(CMD_MOUNT | getline)
235          if($1 ~ "^" BENAME_BEGINS_WITH)
236            MOUNTS[$1] = $3
237        close(CMD_MOUNT)
238        FS = "\\t"
239        CMD_ZFS_LIST = "zfs list -H -t all -s creation -o name,used,usedds,usedbysnapshots,usedrefreserv,refer,creation,origin -r "
240        while(CMD_ZFS_LIST BENAME_BEGINS_WITH | getline) {
241          if($1 != BENAME_BEGINS_WITH) {
242            FSNAME = $1
243            FSNAMES[length(FSNAMES) + 1] = FSNAME
244            USED              = __normalize($2)
245            USEDBYDATASET     = __normalize($3)
246            USEDBYSNAPSHOTS   = __normalize($4)
247            USEDREFRESERV     = __normalize($5)
248            REFER[FSNAME]     = __normalize($6)
249            CREATIONS[FSNAME] = $7
250            ORIGINS[FSNAME]   = $8
251            if(FSNAME ~ /@/)
252              SPACES[FSNAME] = USED
253            else {
254              SPACES[FSNAME] = USEDBYDATASET + USEDREFRESERV
255              if(OPTION_D != 1)
256                SPACES[FSNAME] += USEDBYSNAPSHOTS
257              BE = " " __get_bename(FSNAME) " "
258              if(index(BELIST, BE) == 0)
259                BELIST = BELIST " " BE
260              MOUNTPOINT = MOUNTS[FSNAME]
261              if(MOUNTPOINT) {
262                if((OPTION_a == 0 && FSNAME == (BENAME_BEGINS_WITH "/" __get_bename(FSNAME))) || (OPTION_a == 1)) {
263                  LM = length(MOUNTPOINT)
264                  if(LM > MOUNTPOINT_LENGTH)
265                    MOUNTPOINT_LENGTH = LM
266                }
267              }
268              else
269                MOUNTPOINT = "-"
270            }
271            if(OPTION_a == 1)
272              LF = length(FSNAME)
273            else if(FSNAME !~ /@/)
274              LF = length(__get_bename(FSNAME))
275            if(LF > FSNAME_LENGTH)
276              FSNAME_LENGTH = LF
277          }
278        }
279        close(CMD_ZFS_LIST)
280        split(BELIST, BENAMES, " ")
281        if(OPTION_a == 1) {
282          BE_HEAD = "BE/Dataset/Snapshot"
283          printf "%-" FSNAME_LENGTH + 2 "s %-6s %-" MOUNTPOINT_LENGTH "s %6s %s\n", BE_HEAD, "Active", "Mountpoint", "Space", "Created"
284        }
285        else if(OPTION_H == 1)
286          BE_HEAD = ""
287        else {
288          BE_HEAD = "BE"
289          printf "%-" FSNAME_LENGTH "s %-6s %-" MOUNTPOINT_LENGTH "s %6s %s\n", BE_HEAD, "Active", "Mountpoint", "Space", "Created"
290        }
291        if(OPTION_s != 1)
292          SNAPSHOT_FILTER = "(/[^@]*)?$"
293        for(I = 1; I <= length(BENAMES); I++) {
294          BENAME = BENAMES[I]
295          if(OPTION_a == 1) {
296            printf "\n"
297            print BENAME
298            for(J = 1; J <= length(FSNAMES); J++) {
299              FSNAME = FSNAMES[J]
300              if(FSNAME ~ "^" BENAME_BEGINS_WITH "/" BENAME SNAPSHOT_FILTER) {
301                ACTIVE = ""
302                if(FSNAME == ROOTFS)
303                  ACTIVE = ACTIVE "N"
304                if(FSNAME == BOOTFS)
305                  ACTIVE = ACTIVE "R"
306                if(! ACTIVE)
307                  ACTIVE = "-"
308                MOUNTPOINT = MOUNTS[FSNAME]
309                if(! MOUNTPOINT)
310                  MOUNTPOINT = "-"
311                printf "  %-" FSNAME_LENGTH "s %-6s %-" MOUNTPOINT_LENGTH "s %6s %s\n", FSNAME, ACTIVE, MOUNTPOINT, __show_units(SPACES[FSNAME]), __convert_date(CREATIONS[FSNAME])
312                ORIGIN = ORIGINS[FSNAME]
313                ORIGIN_DISPLAY = ORIGIN
314                sub(BENAME_BEGINS_WITH "/", "", ORIGIN_DISPLAY)
315                if(ORIGIN != "-") {
316                  if(OPTION_D == 1)
317                    SPACE = REFER[ORIGIN]
318                  else
319                    SPACE = SPACES[ORIGIN]
320                  printf  "  %-" FSNAME_LENGTH "s %-6s %-" MOUNTPOINT_LENGTH "s %6s %s\n", "  " ORIGIN_DISPLAY, "-", "-", __show_units(SPACE), __convert_date(CREATIONS[ORIGIN])
321                }
322              }
323            }
324          }
325          else {
326            SPACE = 0
327            ACTIVE = ""
328            NAME = BENAME_BEGINS_WITH "/" BENAME
329            if(NAME == ROOTFS)
330              ACTIVE = ACTIVE "N"
331            if(NAME == BOOTFS)
332              ACTIVE = ACTIVE "R"
333            if(! ACTIVE)
334              ACTIVE = "-"
335            for(J = 1; J <= length(FSNAMES); J++) {
336              FSNAME = FSNAMES[J]
337              if(FSNAME ~ "^" BENAME_BEGINS_WITH "/" BENAME "(/[^@]*)?$") {
338                if((BENAME_BEGINS_WITH "/" BENAME) == FSNAME) {
339                  MOUNTPOINT = MOUNTS[FSNAME]
340                  if(! MOUNTPOINT)
341                    MOUNTPOINT = "-"
342                  CREATION = __convert_date(CREATIONS[FSNAME])
343                }
344                ORIGIN = ORIGINS[FSNAME]
345                if(ORIGIN == "-")
346                  SPACE += SPACES[FSNAME]
347                else {
348                  if(OPTION_D == 1)
349                    SPACE += REFER[FSNAME]
350                  else
351                    SPACE += SPACES[FSNAME] + SPACES[ORIGIN]
352                }
353              }
354            }
355            if(OPTION_H == 1)
356              printf "%s\t%s\t%s\t%s\t%s\n", BENAME, ACTIVE, MOUNTPOINT, __show_units(SPACE), CREATION
357            else
358              printf "%-" FSNAME_LENGTH "s %-6s %-" MOUNTPOINT_LENGTH "s %6s %s\n", BENAME, ACTIVE, MOUNTPOINT, __show_units(SPACE), CREATION
359          }
360        }
361      }'
362    ;;
363
364  (create) # ------------------------------------------------------------------
365    case ${#} in
366      (4)
367        if ! [ ${2} = "-e" ]
368        then
369          __usage
370        fi
371        # check if argument for -e option is full path dataset
372        # argument for -e option must be 'beName' or 'beName@snapshot'
373        if echo ${3} | grep -q "/" 2> /dev/null
374        then
375          __usage
376        fi
377        __be_exist ${POOL}/ROOT/${3}
378        if zfs list -H -o name ${POOL}/ROOT/${4} 1> /dev/null 2> /dev/null
379        then
380          echo "ERROR: Boot environment '${4}' already exists"
381          exit 1
382        fi
383        __be_new ${POOL}/ROOT/${3} ${POOL}/ROOT/${4}
384        ;;
385      (2)
386        if __be_snapshot ${2}
387        then
388          if ! zfs snapshot -r ${POOL}/ROOT/${2} 1> /dev/null 2> /dev/null
389          then
390            echo "ERROR: Cannot create '${2}' recursive snapshot"
391            exit 1
392          fi
393        else
394          __be_new ${ROOTFS} ${POOL}/ROOT/${2}
395        fi
396        ;;
397      (*)
398        __usage
399        ;;
400    esac
401    echo "Created successfully"
402    ;;
403
404  (activate) # ----------------------------------------------------------------
405    if [ ${#} -ne 2 ]
406    then
407      __usage
408    fi
409    __be_exist ${POOL}/ROOT/${2}
410    if [ "${BOOTFS}" = "${POOL}/ROOT/${2}" ]
411    then
412      echo "Already activated"
413      exit 0
414    else
415      if __be_mounted ${POOL}/ROOT/${2}
416      then
417        MNT=$( mount | grep -E "^${POOL}/ROOT/${2} " | awk '{print $3}' )
418        if [ "${MNT}" != "/" ]
419        then
420          # boot environment is not current root and its mounted
421          echo "ERROR: Boot environment '${2}' is mounted at '${MNT}'"
422          echo "ERROR: Cannot activate manually mounted boot environment"
423          exit 1
424        fi
425      fi
426      # do not change root (/) mounted boot environment mountpoint
427      if [ "${ROOTFS}" != "${POOL}/ROOT/${2}" ]
428      then
429        TMPMNT=$( mktemp -d -t BE-${2} )
430        if ! mkdir -p ${TMPMNT} 2> /dev/null
431        then
432          echo "ERROR: Cannot create '${TMPMNT}' directory"
433          exit 1
434        fi
435        MOUNT=0
436        while read FS MNT TYPE OPTS DUMP FSCK;
437        do
438          if [ "${FS}" = "${POOL}/ROOT/${2}" ]
439          then
440            MOUNT=${MNT}
441            break
442          fi
443        done << EOF
444$( mount -p )
445EOF
446        if [ ${MOUNT} -eq 0 ]
447        then
448          zfs set canmount=noauto ${POOL}/ROOT/${2}
449          zfs set mountpoint=${TMPMNT} ${POOL}/ROOT/${2}
450          zfs mount ${POOL}/ROOT/${2}
451        else
452          TMPMNT=${MOUNT}
453        fi
454        cp /boot/zfs/zpool.cache ${TMPMNT}/boot/zfs/zpool.cache
455        LOADER_CONFIGS=${TMPMNT}/boot/loader.conf
456        if [ -f ${TMPMNT}/boot/loader.conf.local ]
457        then
458          LOADER_CONFIGS="${LOADER_CONFIGS} ${TMPMNT}/boot/loader.conf.local"
459        fi
460        sed -i '' -E s/"^vfs.root.mountfrom=.*$"/"vfs.root.mountfrom=\"zfs:${POOL}\/ROOT\/${2##*/}\""/g ${LOADER_CONFIGS}
461        if [ ${MOUNT} -eq 0 ]
462        then
463          zfs umount ${POOL}/ROOT/${2}
464          zfs set mountpoint=legacy ${POOL}/ROOT/${2}
465        fi
466      fi
467      if ! zpool set bootfs=${POOL}/ROOT/${2} ${POOL} 1> /dev/null 2> /dev/null
468      then
469        echo "ERROR: Failed to activate '${2}' boot environment"
470        exit 1
471      fi
472    fi
473    # execute ZFS LIST only once
474    ZFS_LIST=$( zfs list -H -o name -r ${POOL}/ROOT )
475    # disable automatic mount on all inactive boot environments
476    echo "${ZFS_LIST}" \
477      | grep -v "^${POOL}/ROOT/${2}$" \
478      | grep -v "^${POOL}/ROOT/${2}/" \
479      | while read NAME
480        do
481          zfs set canmount=noauto ${NAME}
482        done
483    # enable automatic mount for active boot environment and promote it
484    echo "${ZFS_LIST}" \
485      | grep -E "^${POOL}/ROOT/${2}(/|$)" \
486      | while read NAME
487        do
488          zfs set canmount=on ${NAME}
489          while __be_clone ${NAME}
490          do
491            zfs promote ${NAME}
492          done
493        done
494    echo "Activated successfully"
495    ;;
496
497  (destroy) # -----------------------------------------------------------------
498    if [ "${2}" != "-F" ]
499    then
500      DESTROY=${2}
501    else
502      DESTROY=${3}
503    fi
504    __be_exist ${POOL}/ROOT/${DESTROY}
505    case ${#} in
506      (2)
507        echo "Are you sure you want to destroy '${2}'?"
508        echo -n "This action cannot be undone (y/[n]): "
509        read CHOICE
510        ;;
511      (3)
512        if [ "${2}" != "-F" ]
513        then
514          __usage
515        fi
516        CHOICE=Y
517        ;;
518      (*)
519        __usage
520        ;;
521    esac
522    if [ "${BOOTFS}" = "${POOL}/ROOT/${DESTROY}" ]
523    then
524      echo "ERROR: Cannot destroy active boot environment"
525      exit 1
526    fi
527    case ${CHOICE} in
528      (Y|y|[Yy][Ee][Ss])
529        # destroy snapshot or boot environment
530        if __be_snapshot ${POOL}/ROOT/${DESTROY}
531        then
532          # destroy desired snapshot
533          if ! zfs destroy -r ${POOL}/ROOT/${DESTROY} 1> /dev/null 2> /dev/null
534          then
535            echo "ERROR: Snapshot '${2}' is origin for other boot environment"
536            exit 1
537          fi
538        else
539          if __be_clone ${POOL}/ROOT/${DESTROY}
540          then
541            # promote clones dependent on snapshots used by destroyed boot environment
542            zfs list -H -t all -o name,origin \
543              | while read NAME ORIGIN
544                do
545                  if echo "${ORIGIN}" | grep -q -E "${POOL}/ROOT/${DESTROY}(/.*@|@)" 2> /dev/null
546                  then
547                    zfs promote ${NAME}
548                  fi
549                done
550            # get origins used by destroyed boot environment
551            ORIGIN_SNAPSHOTS=$( zfs list -H -t all -o origin -r ${POOL}/ROOT/${DESTROY} | grep -v '^-$' | awk -F "@" '{print $2}' | sort -u )
552          fi
553          # check if boot environment was created from existing snapshot
554          ORIGIN=$( zfs list -H -o origin ${POOL}/ROOT/${DESTROY} )
555          CREATION=$( zfs list -H -o creation ${POOL}/ROOT/${DESTROY} )
556          CREATION=$( date -j -f "%a %b %d %H:%M %Y" "${CREATION}" +"%Y-%m-%d-%H:%M" )
557          SNAPSHOT_NAME=$( echo "${ORIGIN}" | cut -d '@' -f 2 | sed -E 's/:[0-9]{2}$//g' )
558          if [ "${2}" = "-F" ]
559          then
560            CHOICE=1
561          elif [ "${SNAPSHOT_NAME}" != "${CREATION}" ]
562          then
563            ORIGIN=$( basename ${ORIGIN} )
564            echo "Boot environment '${DESTROY}' was created from existing snapshot"
565            echo -n "Destroy '${ORIGIN}' snapshot? (y/[n]): "
566            read CHOICE
567            case ${CHOICE} in
568              (Y|y|[Yy][Ee][Ss])
569                CHOICE=1
570                ;;
571              (*)
572                CHOICE=0
573                echo "Origin snapshot '${ORIGIN}' will be preserved"
574                ;;
575            esac
576          else
577            CHOICE=1
578          fi
579          # destroy boot environment
580          zfs destroy -r ${POOL}/ROOT/${DESTROY}
581          # check if boot environment is a clone
582          if __be_clone ${POOL}/ROOT/${DESTROY}
583          then
584            # promote datasets dependent on origins used by destroyed boot environment
585            ALL_ORIGINS=$( zfs list -H -t all -o name,origin )
586            echo "${ORIGIN_SNAPSHOTS}" \
587              | while read S
588                do
589                  echo "${ALL_ORIGINS}" \
590                    | grep "${S}" \
591                    | awk '{print $1}' \
592                    | while read I
593                      do
594                        zfs promote ${I}
595                      done
596                done
597          fi
598          # destroy origins used by destroyed boot environment
599          SNAPSHOTS=$( zfs list -H -t snapshot -o name )
600          echo "${ORIGIN_SNAPSHOTS}" \
601            | while read S
602              do
603                echo "${SNAPSHOTS}" \
604                  | grep "@${S}$" \
605                  | while read I
606                    do
607                      if [ ${CHOICE} -eq 1 ]
608                      then
609                        zfs destroy ${I}
610                      fi
611                    done
612              done
613        fi
614        echo "Destroyed successfully"
615        ;;
616      (*)
617        echo "Boot environment '${DESTROY}' has not been destroyed"
618        ;;
619    esac
620    ;;
621
622  (rename) # ------------------------------------------------------------------
623    if [ ${#} -ne 3 ]
624    then
625      __usage
626    fi
627    __be_exist ${POOL}/ROOT/${2}
628    if [ "${BOOTFS}" = "${POOL}/ROOT/${2}" ]
629    then
630      echo "ERROR: Renaming active boot environment is not supported"
631      exit 1
632    fi
633    if zfs list -H -o name ${POOL}/ROOT/${3} 2> /dev/null
634    then
635      echo "ERROR: Boot environment '${3}' already exists"
636      exit 1
637    fi
638    zfs rename ${POOL}/ROOT/${2} ${POOL}/ROOT/${3}
639    echo "Renamed successfully"
640    ;;
641
642  (mount) # ------------------------------------------------------------
643    if [ ${#} -eq 2 ]
644    then
645      TARGET=$( mktemp -d -t BE-${2} )
646    elif [ ${#} -eq 3 ]
647    then
648      TARGET=${3}
649    else
650      __usage
651    fi
652    __be_exist "${POOL}/ROOT/${2}"
653    if __be_mounted "${POOL}/ROOT/${2}"
654    then
655      MNT=$( mount | grep -E "^${POOL}/ROOT/${2} " | awk '{print $3}' )
656      echo "Boot environment '${2}' is already mounted at '${MNT}'"
657      exit 1
658    fi
659    if ! mkdir -p ${TARGET} 2> /dev/null
660    then
661      echo "ERROR: Cannot create '${TARGET}' mountpoint"
662      exit 1
663    fi
664    if ! mount -t zfs ${POOL}/ROOT/${2} ${TARGET}
665    then
666      echo "ERROR: Cannot mount '${2}' at '${TARGET}' mountpoint"
667      exit 1
668    fi
669    PREFIX=$( echo ${POOL}/ROOT/${2}/ | sed 's/\//\\\//g' )
670    zfs list -H -o name,mountpoint -r ${POOL}/ROOT/${2} \
671      | grep -v -E "[[:space:]](legacy|none)$" \
672      | sort -n \
673      | grep -E "^${POOL}/ROOT/${2}/" \
674      | while read FS MOUNTPOINT
675        do
676          if [ "{FS}" != "${POOL}/ROOT/${2}" ]
677          then
678            INHERIT=$( zfs get -H -o source mountpoint ${FS} )
679            if [ "${INHERIT}" = "local" ]
680            then
681              case ${MOUNTPOINT} in
682                (legacy|none)
683                  continue
684                  ;;
685                (*)
686                  MOUNTPOINT="/$( echo "${FS}" | sed s/"${PREFIX}"//g )"
687                  ;;
688              esac
689            fi
690          fi
691          if ! mkdir -p ${TARGET}${MOUNTPOINT} 1> /dev/null 2> /dev/null
692          then
693            echo "ERROR: Cannot create '${TARGET}${MOUNTPOINT}' mountpoint"
694            exit 1
695          fi
696          if ! mount -t zfs ${FS} ${TARGET}${MOUNTPOINT} 1> /dev/null 2> /dev/null
697          then
698            echo "ERROR: Cannot mount '${FS}' at '${TARGET}${MOUNTPOINT}' mountpoint"
699            exit 1
700          fi
701        done
702    echo "Mounted successfully on '${TARGET}'"
703    ;;
704
705  (umount|unmount) # ----------------------------------------------------------
706    if [ ${#} -eq 2 ]
707    then
708      # we need this empty section for argument checking
709      :
710    elif [ ${#} -eq 3 -a "${2}" = "-f" ]
711    then
712      OPTS="-f"
713      shift
714    else
715      __usage
716    fi
717    __be_exist "${POOL}/ROOT/${2}"
718    if ! __be_mounted "${POOL}/ROOT/${2}"
719    then
720      echo "Boot environment '${2}' is not mounted"
721      exit 1
722    fi
723    MOUNT=$( mount )
724    MOUNTPOINT=$( echo "${MOUNT}" | grep -m 1 "^${POOL}/ROOT/${2} on " | awk '{print $3}' )
725    echo "${MOUNT}" \
726      | awk '{print $1}' \
727      | grep -E "^${POOL}/ROOT/${2}(/|$)" \
728      | sort -n -r \
729      | while read FS
730        do
731          if ! umount ${OPTS} ${FS} 1> /dev/null 2> /dev/null
732          then
733            echo "ERROR: Cannot umount '${FS}' dataset"
734            exit 1
735          fi
736        done
737    echo "Unmounted successfully"
738    # only delete the temporary mountpoint directory
739    if echo "${MOUNTPOINT}" | grep -q -E "/BE-${2}\.[a-zA-Z0-9]{8}" 1> /dev/null 2> /dev/null
740    then
741      # delete only when it is an empty directory
742      if [ $( find ${MOUNTPOINT} | head | wc -l | bc ) -eq 1 ]
743      then
744        rm -r ${MOUNTPOINT}
745      fi
746    fi
747    ;;
748
749  (*) # -----------------------------------------------------------------------
750    __usage
751    ;;
752
753esac
Note: See TracBrowser for help on using the repository browser.