| 1 | #!/bin/sh |
|---|
| 2 | |
|---|
| 3 | # manageBE [activate|create|delete|list|src-upgrade|freebsd-update|freebsd-upgrade] {params} |
|---|
| 4 | |
|---|
| 5 | if [ `uname -r |cut -d '.' -f1` -lt "8" ]; then |
|---|
| 6 | echo "Sorry, only FreeBSD starting with 8.0 is supported." |
|---|
| 7 | fi |
|---|
| 8 | |
|---|
| 9 | type=$1 |
|---|
| 10 | |
|---|
| 11 | mgm="manageBE" |
|---|
| 12 | usage_activate="Usage: ${mgm} activate -n <BE> -p <pool>" |
|---|
| 13 | usage_create="Usage: ${mgm} create -n <newBE> -s <sourceBE> -p <pool>" |
|---|
| 14 | usage_delete="Usage: ${mgm} delete -n <BE> -p <pool> -o [yes|no]" |
|---|
| 15 | usage_src_upgrade="Usage: ${mgm} src-upgrade -n <BE> -p <pool>" |
|---|
| 16 | usage_freebsd_update="Usage: ${mgm} freebsd-update -n <BE> -p <pool>" |
|---|
| 17 | usage_freebsd_upgrade="Usage: ${mgm} freebsd-upgrade -n <BE> -p <pool> -r <release>" |
|---|
| 18 | not_supported="Sorry, your FreeBSD version does not support this option" |
|---|
| 19 | |
|---|
| 20 | error_delete="If filesystem has dependent clones, please promote them via 'zfs promote <filesystem>' and re-run ${mgm} delete" |
|---|
| 21 | |
|---|
| 22 | # define our bail out shortcut |
|---|
| 23 | exerr () { echo -e "$*" >&2 ; exit 1; } |
|---|
| 24 | |
|---|
| 25 | check_for_be_existence () { |
|---|
| 26 | be_status=`/sbin/zfs list -H -o freebsd:boot-environment ${pool}/ROOT/${bootfs} 2> /dev/null` |
|---|
| 27 | if [ "${be_status}" != "1" ]; then |
|---|
| 28 | echo "Sorry, ${bootfs} is no valid boot-environment" |
|---|
| 29 | exit |
|---|
| 30 | fi |
|---|
| 31 | } |
|---|
| 32 | |
|---|
| 33 | case ${type} in |
|---|
| 34 | activate) |
|---|
| 35 | # activate a boot-environment |
|---|
| 36 | |
|---|
| 37 | shift; while getopts :n:p: arg; do case ${arg} in |
|---|
| 38 | n) bootfs=${OPTARG};; |
|---|
| 39 | p) pool=${OPTARG};; |
|---|
| 40 | ?) exerr ${usage_activate};; |
|---|
| 41 | esac; done; shift $(( ${OPTIND} - 1 )) |
|---|
| 42 | |
|---|
| 43 | [ "${bootfs}" -a "${pool}" ] || exerr ${usage_activate} |
|---|
| 44 | |
|---|
| 45 | check_for_be_existence |
|---|
| 46 | |
|---|
| 47 | old_bootfs=`/sbin/zpool list -H -o bootfs ${pool} |/usr/bin/sed s:^$pool/ROOT/::` |
|---|
| 48 | /sbin/zpool set bootfs=${pool}/ROOT/${bootfs} ${pool} |
|---|
| 49 | |
|---|
| 50 | for filesystem in `/sbin/zfs list -H -o name -t filesystem -r ${pool}/ROOT/${bootfs}`; do |
|---|
| 51 | /sbin/zfs promote ${filesystem} |
|---|
| 52 | done |
|---|
| 53 | ;; |
|---|
| 54 | create) |
|---|
| 55 | # create a boot-environment |
|---|
| 56 | |
|---|
| 57 | shift; while getopts :n:s:p: arg; do case ${arg} in |
|---|
| 58 | n) new_bootfs=${OPTARG};; |
|---|
| 59 | s) bootfs=${OPTARG};; |
|---|
| 60 | p) pool=${OPTARG};; |
|---|
| 61 | ?) exerr ${usage_create};; |
|---|
| 62 | esac; done; shift $(( ${OPTIND} - 1 )) |
|---|
| 63 | |
|---|
| 64 | [ "${new_bootfs}" -a "${pool}" ] || exerr ${usage_create} |
|---|
| 65 | |
|---|
| 66 | if [ -z "${bootfs}" ]; then |
|---|
| 67 | bootfs=`/sbin/zpool list -H -o bootfs ${pool} | /usr/bin/sed s:${pool}/ROOT/::` |
|---|
| 68 | fi |
|---|
| 69 | |
|---|
| 70 | check_for_be_existence |
|---|
| 71 | |
|---|
| 72 | # create snapshot of the source BE and clone the snapshot to create the new BE |
|---|
| 73 | /sbin/zfs snapshot -r ${pool}/ROOT/${bootfs}@${new_bootfs} |
|---|
| 74 | /sbin/zfs clone ${pool}/ROOT/${bootfs}@${new_bootfs} ${pool}/ROOT/${new_bootfs} |
|---|
| 75 | |
|---|
| 76 | # clone the children snapshot to create the new BE children, mark the filesystem as not being BEs |
|---|
| 77 | for filesystem in `/sbin/zfs list -H -o name -t filesystem -r ${pool}/ROOT/${bootfs} | /usr/bin/sed s:${pool}/ROOT/${bootfs}:: | /usr/bin/sed s:^/:: | /usr/bin/xargs`; do |
|---|
| 78 | /sbin/zfs clone ${pool}/ROOT/${bootfs}/${filesystem}@${new_bootfs} ${pool}/ROOT/${new_bootfs}/${filesystem} |
|---|
| 79 | /sbin/zfs set freebsd:boot-environment=0 ${pool}/ROOT/${new_bootfs}/${filesystem} |
|---|
| 80 | done |
|---|
| 81 | |
|---|
| 82 | # mark the BE as boot-environment and mount the freebsd-update dir in the correct path |
|---|
| 83 | /sbin/zfs set freebsd:boot-environment=1 ${pool}/ROOT/${new_bootfs} |
|---|
| 84 | |
|---|
| 85 | grep -v '^vfs.root.mountfrom' /boot/loader.conf > /${pool}/ROOT/${new_bootfs}/boot/loader.conf |
|---|
| 86 | echo vfs.root.mountfrom=\"zfs:${pool}/ROOT/${new_bootfs}\" >> /${pool}/ROOT/${new_bootfs}/boot/loader.conf |
|---|
| 87 | |
|---|
| 88 | # fill the fstab with the mounts of the BE children |
|---|
| 89 | grep -v ${pool}/ROOT/${bootfs} /etc/fstab > /${pool}/ROOT/${new_bootfs}/etc/fstab |
|---|
| 90 | echo "# Automatically generated by manageBE (${pool}/ROOT/${new_bootfs})" >> /${pool}/ROOT/${new_bootfs}/etc/fstab |
|---|
| 91 | for filesystem in `/sbin/zfs list -H -o name -t filesystem -r ${pool}/ROOT/${new_bootfs} | /usr/bin/sed s:${pool}/ROOT/${new_bootfs}:: | /usr/bin/sed s:^/:: | /usr/bin/xargs`; do |
|---|
| 92 | echo "${pool}/ROOT/${new_bootfs}/${filesystem} /${filesystem} zfs rw 0 0" >> /${pool}/ROOT/${new_bootfs}/etc/fstab |
|---|
| 93 | done |
|---|
| 94 | |
|---|
| 95 | echo "The new Boot-Environment is ready to be updated and/or activated." |
|---|
| 96 | ;; |
|---|
| 97 | delete) |
|---|
| 98 | # delete a boot-environment |
|---|
| 99 | |
|---|
| 100 | shift; while getopts :n:p:o: arg; do case ${arg} in |
|---|
| 101 | n) bootfs=${OPTARG};; |
|---|
| 102 | p) pool=${OPTARG};; |
|---|
| 103 | o) delete_origin=${OPTARG};; |
|---|
| 104 | ?) exerr ${usage_delete};; |
|---|
| 105 | esac; done; shift $(( ${OPTIND} - 1 )) |
|---|
| 106 | |
|---|
| 107 | [ "${bootfs}" -a "${pool}" -a "${delete_origin}" ] || exerr ${usage_delete} |
|---|
| 108 | |
|---|
| 109 | check_for_be_existence |
|---|
| 110 | |
|---|
| 111 | current_bootfs=`/sbin/zpool list -H -o bootfs ${pool}` |
|---|
| 112 | |
|---|
| 113 | if [ ! "${pool}/ROOT/${bootfs}" = "${current_bootfs}" ]; then |
|---|
| 114 | if [ "${delete_origin}" = "yes" ]; then |
|---|
| 115 | origin=`/sbin/zfs list -H -o origin ${pool}/ROOT/${bootfs}` |
|---|
| 116 | fi |
|---|
| 117 | /sbin/zfs destroy -r ${pool}/ROOT/${bootfs} || exerr ${error_delete} |
|---|
| 118 | if [ "${delete_origin}" = "yes" -a "${origin}" ]; then |
|---|
| 119 | /sbin/zfs destroy -r ${origin} |
|---|
| 120 | fi |
|---|
| 121 | else |
|---|
| 122 | echo "${bootfs} is ZFS bootfs, unable to delete!" |
|---|
| 123 | fi |
|---|
| 124 | ;; |
|---|
| 125 | list) |
|---|
| 126 | shift; while getopts :v: arg; do |
|---|
| 127 | verbose="YES"; |
|---|
| 128 | done; shift $(( ${OPTIND} - 1 )) |
|---|
| 129 | |
|---|
| 130 | rootfs=`mount | head -n1 | grep 'on / ' |cut -d ' ' -f1` |
|---|
| 131 | for pool in `/sbin/zpool list -H -o name`; do |
|---|
| 132 | echo "Poolname: $pool" |
|---|
| 133 | zpool_bootfs=`/sbin/zpool list -H -o bootfs ${pool}` |
|---|
| 134 | |
|---|
| 135 | if [ "${zpool_bootfs}" = "-" ]; then |
|---|
| 136 | echo "No BE setup" |
|---|
| 137 | echo |
|---|
| 138 | continue |
|---|
| 139 | fi |
|---|
| 140 | |
|---|
| 141 | mountpoint_length=`/sbin/zfs list -H -o mountpoint -r $pool/ROOT | tail +2 |wc -L` |
|---|
| 142 | if [ "$mountpoint_length" -lt "10" ]; then |
|---|
| 143 | mountpoint_length=10 |
|---|
| 144 | fi |
|---|
| 145 | name_length=`/sbin/zfs list -H -o name -r $pool/ROOT | tail +2 | /usr/bin/sed s:^$pool/ROOT/:: |wc -L` |
|---|
| 146 | if [ "$name_length" -lt "4" ]; then |
|---|
| 147 | name_length=4 |
|---|
| 148 | fi |
|---|
| 149 | space_length=`/sbin/zfs list -H -o referenced -r $pool/ROOT | tail +2 |wc -L` |
|---|
| 150 | if [ "$space_length" -lt "5" ]; then |
|---|
| 151 | space_length=5 |
|---|
| 152 | fi |
|---|
| 153 | snapshot_length=`/sbin/zfs list -t snapshot -H -o name -r $pool/ROOT | /usr/bin/sed s:^$pool/ROOT/:: | wc -L` |
|---|
| 154 | |
|---|
| 155 | printf "%-${name_length}s %-6s %-6s %-${mountpoint_length}s %-${space_length}s\\n" BE Active Active Mountpoint Space |
|---|
| 156 | printf "%-${name_length}s %-6s %-6s %-${mountpoint_length}s %-${space_length}s\\n" Name Now Reboot - Used |
|---|
| 157 | printf "%-${name_length}s %-6s %-6s %-${mountpoint_length}s %-${space_length}s\\n" ---- ------ ------ ---------- ----- |
|---|
| 158 | |
|---|
| 159 | for be_filesystem in `/sbin/zfs list -H -S creation -o name -r $pool/ROOT |grep -v "^$pool/ROOT\$"`; do |
|---|
| 160 | |
|---|
| 161 | if [ `/sbin/zfs list -H -o freebsd:boot-environment ${be_filesystem}` -eq 1 ]; then |
|---|
| 162 | be_name=`echo ${be_filesystem}|/usr/bin/sed s:^$pool/ROOT/::` |
|---|
| 163 | |
|---|
| 164 | be_active='no' |
|---|
| 165 | if [ "${be_filesystem}" = "${rootfs}" ]; then |
|---|
| 166 | be_active='yes' |
|---|
| 167 | fi |
|---|
| 168 | be_nextboot='no' |
|---|
| 169 | if [ "${be_filesystem}" = "${zpool_bootfs}" ]; then |
|---|
| 170 | be_nextboot='yes' |
|---|
| 171 | fi |
|---|
| 172 | |
|---|
| 173 | if [ "${be_active}" = "yes" -a "${be_nextboot}" = "yes" ]; then |
|---|
| 174 | used_by_snapshots=`/sbin/zfs list -H -o usedbysnapshots $be_filesystem` |
|---|
| 175 | be_space=`/sbin/zfs list -H -o referenced $be_filesystem` |
|---|
| 176 | be_mountpoint='/' |
|---|
| 177 | elif [ "${be_active}" = "no" -a "${be_nextboot}" = "yes" ]; then |
|---|
| 178 | used_by_snapshots=`/sbin/zfs list -H -o usedbysnapshots $be_filesystem` |
|---|
| 179 | be_space=`/sbin/zfs list -H -o referenced $be_filesystem` |
|---|
| 180 | be_mountpoint=`/sbin/zfs list -H -o mountpoint $be_filesystem` |
|---|
| 181 | elif [ "${be_active}" = "yes" -a "${be_nextboot}" = "no" ]; then |
|---|
| 182 | be_origin=`/sbin/zfs list -H -o origin ${be_filesystem}` |
|---|
| 183 | be_space=`/sbin/zfs list -H -t snapshot -o used $be_origin` |
|---|
| 184 | be_mountpoint='/' |
|---|
| 185 | else |
|---|
| 186 | be_origin=`/sbin/zfs list -H -o origin ${be_filesystem}` |
|---|
| 187 | be_space=`/sbin/zfs list -H -t snapshot -o used $be_origin` |
|---|
| 188 | be_mountpoint=`/sbin/zfs list -H -o mountpoint $be_filesystem` |
|---|
| 189 | fi |
|---|
| 190 | printf "%-${name_length}s %-6s %-6s %-${mountpoint_length}s %${space_length}s\\n" $be_name $be_active $be_nextboot $be_mountpoint $be_space |
|---|
| 191 | |
|---|
| 192 | if [ "$verbose" = "YES" ]; then |
|---|
| 193 | be_created=`/sbin/zfs list -H -S creation -o creation $be_filesystem` |
|---|
| 194 | echo |
|---|
| 195 | printf "\t%-${snapshot_length}s %-21s\\n" Name Created |
|---|
| 196 | printf "\t%-${snapshot_length}s %-21s\\n" $be_name "$be_created" |
|---|
| 197 | for snapshot_name in `/sbin/zfs list -t snapshot -H -r -o name $pool/ROOT/$be_name|/usr/bin/sed s:^$pool/ROOT/::`; do |
|---|
| 198 | snapshot_created=`/sbin/zfs list -t snapshot -H -S creation -o creation $pool/ROOT/$snapshot_name` |
|---|
| 199 | printf "\t%-${snapshot_length}s %-21s\\n" $snapshot_name "$snapshot_created" |
|---|
| 200 | done |
|---|
| 201 | echo |
|---|
| 202 | fi |
|---|
| 203 | fi |
|---|
| 204 | done |
|---|
| 205 | echo |
|---|
| 206 | echo "Used by BE snapshots: ${used_by_snapshots}" |
|---|
| 207 | echo |
|---|
| 208 | done |
|---|
| 209 | ;; |
|---|
| 210 | src-upgrade) |
|---|
| 211 | # upgrade a boot-environment |
|---|
| 212 | |
|---|
| 213 | shift; while getopts :n:p: arg; do case ${arg} in |
|---|
| 214 | n) bootfs=${OPTARG};; |
|---|
| 215 | p) pool=${OPTARG};; |
|---|
| 216 | ?) exerr ${usage_src_upgrade};; |
|---|
| 217 | esac; done; shift $(( ${OPTIND} - 1 )) |
|---|
| 218 | |
|---|
| 219 | [ "${bootfs}" -a "${pool}" ] || exerr ${usage_src_upgrade} |
|---|
| 220 | |
|---|
| 221 | check_for_be_existence |
|---|
| 222 | |
|---|
| 223 | mount -t nullfs -o ro /usr/src /${pool}/ROOT/${bootfs}/usr/src |
|---|
| 224 | mount -t nullfs -o ro /usr/obj /${pool}/ROOT/${bootfs}/usr/obj |
|---|
| 225 | |
|---|
| 226 | echo '#!/bin/sh |
|---|
| 227 | mount -t devfs devfs /dev |
|---|
| 228 | cd /usr/src |
|---|
| 229 | make -s installkernel |
|---|
| 230 | make -s installworld |
|---|
| 231 | /usr/sbin/mergemaster -U |
|---|
| 232 | umount /dev' > /${pool}/ROOT/${bootfs}/tmp/manageBE-upgrade.sh |
|---|
| 233 | |
|---|
| 234 | chmod +x /${pool}/ROOT/${bootfs}/tmp/manageBE-upgrade.sh |
|---|
| 235 | chroot /${pool}/ROOT/${bootfs}/ /tmp/manageBE-upgrade.sh |
|---|
| 236 | rm /${pool}/ROOT/${bootfs}/tmp/manageBE-upgrade.sh |
|---|
| 237 | umount /${pool}/ROOT/${bootfs}/usr/obj |
|---|
| 238 | umount /${pool}/ROOT/${bootfs}/usr/src |
|---|
| 239 | |
|---|
| 240 | echo "Please activate ${bootfs} via manageBE and reboot." |
|---|
| 241 | ;; |
|---|
| 242 | freebsd-update) |
|---|
| 243 | [ `uname -r | cut -d '-' -f1,2` = "8.0-RELEASE" ] && exerr ${not_supported} |
|---|
| 244 | # update a boot-environment using freebsd-update |
|---|
| 245 | |
|---|
| 246 | shift; while getopts :n:p: arg; do case ${arg} in |
|---|
| 247 | n) bootfs=${OPTARG};; |
|---|
| 248 | p) pool=${OPTARG};; |
|---|
| 249 | ?) exerr ${usage_freebsd_upgrade};; |
|---|
| 250 | esac; done; shift $(( ${OPTIND} - 1 )) |
|---|
| 251 | |
|---|
| 252 | [ "${bootfs}" -a "${pool}" -a ${release} ] || exerr ${usage_freebsd_update} |
|---|
| 253 | |
|---|
| 254 | check_for_be_existence |
|---|
| 255 | |
|---|
| 256 | chroot /${bootfs} mount -t devfs devfs /dev |
|---|
| 257 | chroot /${bootfs} /usr/sbin/freebsd-update fetch |
|---|
| 258 | chroot /${bootfs} /usr/sbin/freebsd-update install > /dev/null |
|---|
| 259 | chroot /${bootfs} umount /dev |
|---|
| 260 | |
|---|
| 261 | echo "Please activate ${bootfs} via manageBE and reboot." |
|---|
| 262 | ;; |
|---|
| 263 | freebsd-upgrade) |
|---|
| 264 | [ `uname -r | cut -d '-' -f1,2` = "8.0-RELEASE" ] && exerr ${not_supported} |
|---|
| 265 | # upgrade a boot-environment using freebsd-update |
|---|
| 266 | |
|---|
| 267 | shift; while getopts :n:p:r: arg; do case ${arg} in |
|---|
| 268 | n) bootfs=${OPTARG};; |
|---|
| 269 | p) pool=${OPTARG};; |
|---|
| 270 | r) release=${OPTARG};; |
|---|
| 271 | ?) exerr ${usage_freebsd_upgrade};; |
|---|
| 272 | esac; done; shift $(( ${OPTIND} - 1 )) |
|---|
| 273 | |
|---|
| 274 | [ "${bootfs}" -a "${pool}" -a ${release} ] || exerr ${usage_freebsd_upgrade} |
|---|
| 275 | |
|---|
| 276 | check_for_be_existence |
|---|
| 277 | |
|---|
| 278 | chroot /${bootfs} mount -t devfs devfs /dev |
|---|
| 279 | chroot /${bootfs} /usr/sbin/freebsd-update upgrade -r ${release} |
|---|
| 280 | chroot /${bootfs} /usr/sbin/freebsd-update install > /dev/null |
|---|
| 281 | chroot /${bootfs} umount /dev |
|---|
| 282 | |
|---|
| 283 | echo "Please activate ${bootfs} via manageBE and reboot. After rebooting, freebsd-update needs to be run again to install the new userland components, and the system needs to be rebooted again" |
|---|
| 284 | ;; |
|---|
| 285 | *) |
|---|
| 286 | echo echo "Usage: ${mgm} [activate|create|delete|list|src-upgrade|freebsd-update|freebsd-upgrade] {params}" |
|---|
| 287 | ;; |
|---|
| 288 | esac |
|---|