diff --git a/install b/install index 6c02cc7..798a892 100755 --- a/install +++ b/install @@ -5,7 +5,7 @@ # Downloads and installs rpi-clone and rpi-clone-setup into /usr/local/sbin # # Command to use to install rpi-clone: -# curl https://raw.githubusercontent.com/geerlingguy/rpi-clone/master/install | sudo bash +# curl https://raw.githubusercontent.com/geerlingguy/rpi-clone/master/install | sudo bash # # rpi-clone is Copyright (c) 2018-2019 Bill Wilson # @@ -17,54 +17,62 @@ # This updated code is located in a fork of Bill Willsons git repository # at https://github.com/geerlingguy/rpi-clone +# shellcheck enable=require-variable-braces + set -uo pipefail readonly PACKAGE="rpi-clone" -readonly DOWNLOAD_REPOSITORY="https://raw.githubusercontent.com/geerlingguy/rpi-clone/master" -readonly FILES_2_DOWNLOAD"=rpi-clone rpi-clone-setup" -readonly TMP_DIR=$(mktemp -d) +readonly GITHUB_USER="geerlingguy" +readonly DOWNLOAD_REPOSITORY="https://raw.githubusercontent.com/${GITHUB_USER}/${PACKAGE}/master" +readonly FILES_TO_DOWNLOAD=("rpi-clone" "rpi-clone-setup") readonly DEFAULT_INSTALLATION_DIR="/usr/local/sbin" -pwd=$PWD -trap "{ cd $pwd; rmdir $TMP_DIR &>/dev/null; }" SIGINT SIGTERM EXIT -cd $TMP_DIR +TMP_DIR=$(mktemp -d) +OLD_PWD=${PWD} + +trap 'cd "${OLD_PWD}"; rmdir "${TMP_DIR}" &>/dev/null' SIGINT SIGTERM EXIT + +cd "${TMP_DIR}" || exit 1 -installDir="${1:-$DEFAULT_INSTALLATION_DIR}" +installDir="${1:-${DEFAULT_INSTALLATION_DIR}}" -if [[ ! -d $installDir ]]; then - echo "Installation directory $installDir not found" +if [[ ! -d "${installDir}" ]]; then + echo "Installation directory ${installDir} not found" echo "Pass a valid installation directory as a parameter to $0" exit 1 fi -echo "Installing $PACKAGE into $installDir ..." +echo "Installing ${PACKAGE} into ${installDir}..." -for file in $FILES_2_DOWNLOAD; do - echo -n "Downloading $file from $DOWNLOAD_REPOSITORY/$file ... " - http_code=$(curl -w "%{http_code}" -L -s $DOWNLOAD_REPOSITORY/$file -o $file) - (($?)) && { +for file in "${FILES_TO_DOWNLOAD[@]}"; do + echo -n "Downloading ${file} from ${DOWNLOAD_REPOSITORY}/${file}..." + + if ! http_code=$(curl -w "%{http_code}" -L -s "${DOWNLOAD_REPOSITORY}/${file}" -o "${file}"); then echo "Curl failed" exit 1 - } - [[ $http_code != 200 ]] && { - echo "http request failed with $http_code" + fi + + if [[ "${http_code}" != 200 ]]; then + echo "http request failed with ${http_code}" exit 1 - } + fi + echo "done" - echo -n "Installing $file into $installDir ... " - sudo chmod +x $file - (($?)) && { + + echo -n "Installing ${file} into ${installDir}..." + + if ! sudo chmod +x "${file}"; then echo "chmod failed" exit 1 - } - sudo mv $file $installDir - (($?)) && { + fi + + if ! sudo mv "${file}" "${installDir}"; then echo "mv failed" exit 1 - } - echo "done" + fi + echo "done" done -echo "$PACKAGE installed" -cd $pwd +echo "${PACKAGE} installed" +cd "${OLD_PWD}" || exit 1 diff --git a/rpi-clone b/rpi-clone index cbb8d6b..5382bf3 100755 --- a/rpi-clone +++ b/rpi-clone @@ -157,41 +157,39 @@ usage: $PGM sdN {-v|--verbose} {-f|--force-initialize} {-f2} exit 1 } -readable_MiB() { +readable_size() { val=$1 - if [ "$val" == "" ]; then - result=" ??" - else - blk_size=$2 - val=$((val / 1024 * blk_size)) + blk_size=$2 + format=${4:-MiB} # Default to MiB if not specified - if ((val < 1024 * 1024)); then - result=$(echo $val | awk '{ byte = $1 / 1024; printf "%.1f%s", byte, "M" }') - elif ((val < 1024 * 1024 * 1024)); then - result=$(echo $val | awk '{ byte = $1 / 1024 / 1024; printf "%.1f%s", byte, "G" }') - else - result=$(echo $val | awk '{ byte = $1 / 1024 / 1024 / 1024; printf "%.1f%s", byte, "T" }') - fi - fi - printf -v "${3}" "%s" "$result" -} - -readable_MB() { - val=$1 if [ "$val" == "" ]; then result=" ??" else - blk_size=$2 - val=$((val / 1000 * blk_size)) - - if ((val < 1000 * 1000)); then - result=$(echo $val | awk '{ byte = $1 / 1000; printf "%.1f%s", byte, "MB" }') - elif ((val < 1000 * 1000 * 1000)); then - result=$(echo $val | awk '{ byte = $1 / 1000 / 1000; printf "%.1f%s", byte, "GB" }') + if [ "$format" == "MB" ]; then + # Decimal (MB, GB, TB) + val=$((val / 1000 * blk_size)) + + if ((val < 1000 * 1000)); then + result=$(echo $val | awk '{ byte = $1 / 1000; printf "%.1f%s", byte, "MB" }') + elif ((val < 1000 * 1000 * 1000)); then + result=$(echo $val | awk '{ byte = $1 / 1000 / 1000; printf "%.1f%s", byte, "GB" }') + else + result=$(echo $val | awk '{ byte = $1 / 1000 / 1000 / 1000; printf "%.1f%s", byte, "TB" }') + fi else - result=$(echo $val | awk '{ byte = $1 / 1000 / 1000 / 1000; printf "%.1f%s", byte, "TB" }') + # Binary (MiB, GiB, TiB) + val=$((val / 1024 * blk_size)) + + if ((val < 1024 * 1024)); then + result=$(echo $val | awk '{ byte = $1 / 1024; printf "%.1f%s", byte, "M" }') + elif ((val < 1024 * 1024 * 1024)); then + result=$(echo $val | awk '{ byte = $1 / 1024 / 1024; printf "%.1f%s", byte, "G" }') + else + result=$(echo $val | awk '{ byte = $1 / 1024 / 1024 / 1024; printf "%.1f%s", byte, "T" }') + fi fi fi + printf -v "${3}" "%s" "$result" } @@ -299,8 +297,8 @@ print_partitions() { n_parts=$(((n_src_parts >= n_dst_parts) ? n_src_parts : n_dst_parts)) - readable_MB $src_disk_size "512" src_size_readable - readable_MB $dst_disk_size "512" dst_size_readable + readable_size $src_disk_size "512" src_size_readable "MB" + readable_size $dst_disk_size "512" dst_size_readable "MB" printf "\n%-43s%s" "Booted disk: $src_disk $src_size_readable" \ "Destination disk: $dst_disk $dst_size_readable" @@ -309,7 +307,7 @@ print_partitions() { out=$'Part, Size,FS,Label ,Part, Size,FS,Label\n' for ((p = 1; p <= n_parts; p++)); do if ((p <= n_src_parts && src_exists[p])); then - readable_MiB ${src_size_sectors[p]} "512" tmp + readable_size ${src_size_sectors[p]} "512" tmp printf -v sectors_readable "%7s" $tmp pname="$p ${src_name[p]}" out=${out}$"$pname,$sectors_readable,${src_fs_type[p]},${src_label[p]}," @@ -318,7 +316,7 @@ print_partitions() { fi if ((p <= n_dst_parts && dst_exists[p])); then - readable_MiB ${dst_size_sectors[p]} "512" tmp + readable_size ${dst_size_sectors[p]} "512" tmp printf -v sectors_readable "%7s" $tmp out=${out}$"$p,$sectors_readable,${dst_fs_type[p]},${dst_label[p]}," else @@ -361,8 +359,8 @@ print_sync_actions() { src_label="/dev/${src_partition[p]}" action_label="MOUNT SYNC" fi - readable_MiB ${src_used_sectors[p]} "512" used - readable_MiB ${dst_size_sectors[p]} "512" size + readable_size ${src_used_sectors[p]} "512" used + readable_size ${dst_size_sectors[p]} "512" size printf "%-22s%-14s : %s %s\n" \ "$src_label" "(${used} used)" "$action_label" \ "$flow (${size} size)" @@ -410,10 +408,10 @@ print_image_actions() { fi if ((p == n_image_parts)); then - readable_MiB ${src_used_sectors[n_image_parts]} "512" used + readable_size ${src_used_sectors[n_image_parts]} "512" used printf "%-22s%-14s : RESIZE %s\n" "$pname" "(${used} used)" "$action" elif ((src_used_sectors[$p] > 0 && p < n_image_parts)); then - readable_MiB ${src_used_sectors[p]} "512" used + readable_size ${src_used_sectors[p]} "512" used printf "%-22s%-14s : $action\n" "$pname" "(${used} used)" elif [ "$action" != "" ]; then printf "%-36s : $action\n" "$pname" @@ -561,6 +559,45 @@ get_src_disk() { printf -v "${3}" "%s" "$num" } +change_disk_id() { + local disk=$1 + local msg=${2:-} + + if [ -n "$msg" ]; then + qecho "$msg" + fi + + new_id=$(od -A n -t x -N 4 /dev/urandom | tr -d " ") + qprintf "x\ni\n0x$new_id\nr\nw\nq\n" | fdisk /dev/"$disk" > /dev/null + + sync + sleep 2 +} + +find_extended_partition() { + local -r fdisk_table=$1 + local -r dev=$(grep "Extended" <<< "${fdisk_table}" | awk '{print $1}') + + if [ -n "${dev}" ]; then + echo "${dev: -1}" + else + echo "0" + fi +} + +edit_disk_reference() { + local file=$1 + local description=$2 + + if grep -q "$src_disk_ID" "$file"; then + qecho "Editing $description PARTUUID to use $dst_disk_ID" + sed -i "s/${src_disk_ID}/${dst_disk_ID}/g" "$file" + elif [ "$edit_fstab_name" != "" ] && grep -q "${src_part_base}" "$file"; then + qecho "Editing $description references from $src_part_base to $edit_fstab_name" + sed -i "s/${src_part_base}/${edit_fstab_name}/" "$file" + fi +} + # ==== source (booted) disk info and default mount list # src_boot_dev=$(findmnt /boot -o source -n | grep "/dev/") @@ -634,13 +671,7 @@ Don't know how to partition the destination disk! exit 1 fi -line=$(echo "$src_fdisk_table" | grep "Extended") -if [ "$line" != "" ]; then - dev=$(echo "$line" | cut -d " " -f 1) - ext_part_num="${dev: -1}" -else - ext_part_num=0 -fi +ext_part_num=$(find_extended_partition "$src_fdisk_table") for ((p = 1; p <= n_src_parts; p++)); do line=$(echo "$src_partition_table" | grep -e "^${p}:") @@ -1028,13 +1059,7 @@ dst_root_dev=/dev/${dst_part_base}${root_part_num} dst_mount_table=$(findmnt -o source,target -n -l | grep "^/dev/$dst_disk" | tr -s " ") dst_fdisk_table=$(fdisk -l /dev/$dst_disk | grep "^/dev/") -line=$(echo "$dst_fdisk_table" | grep "Extended") -if [ "$line" != "" ]; then - dev=$(echo "$line" | cut -d " " -f 1) - ext_num="${dev: -1}" -else - ext_num=0 -fi +ext_num=$(find_extended_partition "$dst_fdisk_table") for ((p = 1; p <= n_dst_parts; p++)); do line=$(echo "$dst_partition_table" | grep -e "^${p}:") @@ -1203,7 +1228,7 @@ Use -U for unattended even if initializing. printf "%-22s : %s\n" "** FATAL **" "Initialize needed - $reason" printf "%-22s : %s %s %s\n" "" \ "But destination is too small to clone" "$n_image_parts" "partitions." - readable_MiB $((start_sector + 7812)) "512" min_size + readable_size $((start_sector + 7812)) "512" min_size printf "%-22s : %s\n" "" "Minimum destination size required is $min_size." if ((n_image_parts > 2)); then printf "%-22s : %s\n" " " "Possible options:" @@ -1222,7 +1247,7 @@ Use -U for unattended even if initializing. exit 1 fi - readable_MiB $((last_part_sectors + 7812)) "512" image_space_readable + readable_size $((last_part_sectors + 7812)) "512" image_space_readable echo "== Initialize: IMAGE partition table - $reason ==" print_image_actions @@ -1239,7 +1264,7 @@ Use -U for unattended even if initializing. abort=0 if ((!last_part_space)); then - readable_MiB $last_part_used "512" used_readable + readable_size $last_part_used "512" used_readable printf "%-22s : %s\n" "** WARNING **" \ "Destination last partition resize to $image_space_readable" printf "%-22s : %s\n" "" " is too small to hold source used $used_readable." @@ -1320,20 +1345,13 @@ Use -U for unattended even if initializing. printf " Try running $PGM again.\n\n" # Don't let dst disk keep source disk ID. Can lead to remounts. - new_id=$(od -A n -t x -N 4 /dev/urandom | tr -d " ") - qprintf "x\ni\n0x$new_id\nr\nw\nq\n" | fdisk /dev/$dst_disk > /dev/null + change_disk_id "$dst_disk" exit 1 fi done printf "\n Resize success.\n" printf " Changing destination Disk ID ..." - sync - sleep 2 - - new_id=$(od -A n -t x -N 4 /dev/urandom | tr -d " ") - qprintf "x\ni\n0x$new_id\nr\nw\nq\n" | fdisk /dev/$dst_disk > /dev/null - sync - sleep 2 + change_disk_id "$dst_disk" partprobe "/dev/$dst_disk" sleep 2 echo "" @@ -1457,11 +1475,7 @@ fi line=$(fdisk -l /dev/$dst_disk | grep "Disk identifier:") dst_disk_ID=${line#*x} if [ "$dst_disk_ID" == "$src_disk_ID" ]; then - qecho "Destination disk has same Disk ID as source, changing it." - new_id=$(od -A n -t x -N 4 /dev/urandom | tr -d " ") - qprintf "x\ni\n0x$new_id\nr\nw\nq\n" | fdisk /dev/$dst_disk | grep changed - sync - sleep 2 + change_disk_id "$dst_disk" "Destination disk has same Disk ID as source, changing it." partprobe "/dev/$dst_disk" sleep 2 @@ -1538,13 +1552,7 @@ if [ -f $cmdline_txt ]; then cp $cmdline_txt ${clone}/${cmdlinedir}/cmdline.boot cmdline_txt=${clone}/${cmdlinedir}/cmdline.boot fi - if grep -q $src_disk_ID $cmdline_txt; then - qecho "Editing $cmdline_txt PARTUUID to use $dst_disk_ID" - sed -i "s/${src_disk_ID}/${dst_disk_ID}/" "$cmdline_txt" - elif [ "$edit_fstab_name" != "" ] && grep -q ${src_part_base} $cmdline_txt; then - qecho "Editing $cmdline_txt references from $src_part_base to $edit_fstab_name" - sed -i "s/${src_part_base}/$edit_fstab_name/" "$cmdline_txt" - fi + edit_disk_reference "$cmdline_txt" "$cmdline_txt" if ((leave_sd_usb_boot && SD_slot_boot)); then qecho "Copying USB cmdline.txt to SD card to set up USB boot." cp /${cmdlinedir}/cmdline.txt /${cmdlinedir}/cmdline.boot @@ -1552,13 +1560,7 @@ if [ -f $cmdline_txt ]; then fi fi -if grep -q $src_disk_ID $fstab; then - qecho "Editing $fstab PARTUUID to use $dst_disk_ID" - sed -i "s/${src_disk_ID}/${dst_disk_ID}/g" "$fstab" -elif [ "$edit_fstab_name" != "" ] && grep -q ${src_part_base} $fstab; then - qecho "Editing $fstab references from $src_part_base to $edit_fstab_name" - sed -i "s/${src_part_base}/${edit_fstab_name}/" "$fstab" -fi +edit_disk_reference "$fstab" "$fstab" rm -f $clone/etc/udev/rules.d/70-persistent-net.rules diff --git a/rpi-clone-setup b/rpi-clone-setup index 5a3ff2e..00e5737 100755 --- a/rpi-clone-setup +++ b/rpi-clone-setup @@ -25,40 +25,43 @@ # Test new scripting by running: rpi-clone-setup -t newhostname # -file_list="etc/hostname etc/hosts" +# shellcheck enable=require-variable-braces +file_list=("etc/hostname" "etc/hosts") clone=/mnt/clone clone_test=/tmp/clone-test -PGM=$(basename $0) +PGM=$(basename "$0") -if [ $(id -u) != 0 ]; then - echo "You must be root to run $PGM" - exit 0 +if [ "$(id -u)" != 0 ]; then + echo "You must be root to run ${PGM}" + exit 1 fi -function usage { - echo "Usage: $PGM hostname {-t|--test}" - echo " Eg: $PGM rpi1" +usage() { + echo "Usage: ${PGM} hostname {-t|--test}" + echo " Eg: ${PGM} rpi1" echo " Modify files appropriate to set up for a new host." echo " Files handled are:" - for file in $file_list; do - echo " $file" + for file in "${file_list[@]}"; do + echo " ${file}" done echo "" - echo "If testing (-t flag) files are copied and processed to $clone_test" + echo "If testing (-t flag) files are copied and processed to ${clone_test}" echo "" - exit 0 + exit 1 } testing=0 -while [ "$1" ]; do +newhost="" + +while [ "$#" -gt 0 ]; do case "$1" in -t | --test) testing=1 ;; *) - if [ "$newhost" != "" ]; then + if [ -n "${newhost}" ]; then echo "Bad args" usage fi @@ -68,31 +71,31 @@ while [ "$1" ]; do shift done -if [ "$newhost" = "" ]; then +if [ -z "${newhost}" ]; then echo -e "You must specify a target hostname\n" usage fi -echo -e "\t$newhost\t- target hostname" +echo -e "\t${newhost}\t- target hostname" if ((!testing)) && [ ! -d /mnt/clone/etc ]; then echo "A destination clone file system is not mounted on /mnt/clone" echo "Aborting!" - exit 0 + exit 1 fi if ((testing)); then - cd /tmp - rm -rf $clone_test - clone=$clone_test + cd /tmp || exit 1 + rm -rf "${clone_test}" + clone=${clone_test} - mkdir -p $clone/etc + mkdir -p "${clone}/etc" echo "**********************************************" - echo "Testing setup: copying files to $clone" - for file in $file_list; do - echo " cp /$file $clone/$file" - cp /$file $clone/$file + echo "Testing setup: copying files to ${clone}" + for file in "${file_list[@]}"; do + echo " cp /${file} ${clone}/${file}" + cp "/${file}" "${clone}/${file}" done echo "This test run will modify those files." echo "**********************************************" @@ -102,26 +105,26 @@ fi ## # Set /etc/hostname # -cd $clone/etc -echo $newhost > hostname +echo "${newhost}" > "${clone}/etc/hostname" + # # Read it back to verify. # -echo "$clone/etc/hostname - set new hostname: " -LINE=$(cat hostname) -echo -e "$LINE\n" +echo "${clone}/etc/hostname - set new hostname: " +LINE=$(cat "${clone}/etc/hostname") +echo -e "${LINE}\n" ## # Edit /etc/hosts - edit the sed command if editing fails for your /etc/hosts. # -cd $clone/etc -sed -i s/"$HOSTNAME"/"$newhost"/g hosts +sed -i "s/${HOSTNAME}/${newhost}/g" "${clone}/etc/hosts" + # # Read it back to verify. # -echo "$clone/etc/hosts - set new hostname \"$newhost\" in lines: " -LINE=$(grep $newhost hosts) -echo -e "$LINE\n" +echo "${clone}/etc/hosts - set new hostname \"${newhost}\" in lines: " +LINE=$(grep "${newhost}" "${clone}/etc/hosts") +echo -e "${LINE}\n" ## # Add more customizations if needed.