#!/bin/bb # shellcheck disable=SC2039 shell=sh rollback_path=/var/usrmerge-rollback check_internal_commands() { for cmd in cp ln; do [ "$(command -v "${cmd}")" = ${cmd} ] && continue printf 'busybox does not include the %s command internally\n' "${cmd}" return 1 done return 0 } check_root() { [ "$(id -u)" = 0 ] && return 0 printf 'This script must be run by root\n' return 1 } run_command() { local cmd [ -n "${dryrun}" ] && cmd="echo" ${cmd} "$@" || exit return 0 } setup_rollback() { if [ -e "${rollback_path}" ]; then printf '%s already exists\n' "${rollback_path}" printf 'please remove it before continuing\n' return 1 fi printf 'Creating %s to allow rollbacks\n' "${rollback_path}" run_command mkdir "${rollback_path}" run_command mkdir "${rollback_path}/usr" local dir for dir in /bin /lib* /sbin; do run_command cp -a "${dir}" "${rollback_path}" done for dir in /usr/bin /usr/lib* /usr/sbin; do [ "${dir}" = /usr/libexec ] && continue run_command cp -a "${dir}" "${rollback_path}/usr" done return 0 } action_finish() { run_command rm -fr "${rollback_path}" return 0 } action_help() { local cmd cmd="$(basename "${0}")" printf 'usage: %s -h\n' "${cmd}" printf ' %s [-d] -f|-m|-r\n' "${cmd}" printf '\n' printf '-h | --help - displays this message\n' printf '-d | --dry-run - show what would be done\n' printf '-f | --finish - remove the rollback data\n' printf '-m | --merge - perform the usr merge\n' printf '-r | --roll-back - attempt to undo the usr merge\n' return 0 } action_merge() { if [ -L /bin ] || [ -L /sbin ]; then printf 'The /usr merge has been completed on this system\n' return 0 fi local dir for dir in /lib*; do [ -L "${dir}" ] || continue printf '%s is a symbolic link.\n' "${dir}" printf 'This means you have not migrated to the 17.1 profiles yet\n' printf 'please do so before running this script\n' return 1 done setup_rollback || return # copy root directories to /usr counterparts and create # the /usr merge compatibility symlinks for dir in /bin /lib* /sbin; do run_command cp -a -i "${dir}"/* /usr/"${dir}" run_command rm -rf "${dir}" run_command ln -snf usr/"${dir}" "${dir}" done # merge /usr/sbin into /usr/bin run_cmd cp -a -i /usr/sbin/* /usr/bin run_cmd rm -fr /usr/sbin run_cmd ln -snf bin /usr/sbin return 0 } action_rollback() { if [ ! -d "${rollback_path}" ]; then printf '%s does not exist, unable to roll back\n' "${rollback_path}" return 1 fi local dir rollback_dir for dir in /bin /lib* /sbin /usr/bin /usr/lib* /usr/sbin; do [ "${dir}" = /usr/libexec ] && continue rollback_dir="${rollback_path}/${dir}" [ -d "${rollback_dir}" ] && continue printf 'Unable to perform rollback, %s is missing\n' "${rollback_dir}" return 1 done for dir in /bin /lib* /sbin ; do rollback_dir="${rollback_path}/${dir}" run_cmd rm -fr "${dir}" run_cmd cp -a "${rollback_dir}" / done for dir in /usr/bin /usr/lib* /usr/sbin; do [ "${dir}" = /usr/libexec ] && continue rollback_dir="${rollback_path}/${dir}" run_cmd rm -f "${dir}" run_cmd cp -a "${rollback_dir}" /usr done return 0 } main() { local dryrun finish merge rollback while [ $# -gt 0 ]; do case $1 in -d|--dry-run) dryrun=1 ;; -h|--help) action_help return 0 ;; -f|--finish) finish=1 ;; -m|--merge) merge=1 ;; -r|--roll-back) rollback=1 ;; esac shift done check_internal_commands || return check_root || return if [ "${finish}" = "${merge}" ] && [ "${merge}" = "${rollback}" ]; then printf 'You must select -f, -m or -r\n' action_help return 1 elif [ -n "${finish}" ]; then action_finish || return elif [ -n "${merge}" ]; then action_merge || return elif [ -n "${rollback}" ]; then action_rollback || return fi return 0 } main "$@" || exit exit 0