| #!/usr/bin/env bash |
| # |
| # Check if a given custom skeleton or overlay complies to the merged |
| # requirements: |
| # |
| # - for unmerged: |
| # /bin missing*, or an existing directory; not a symlink |
| # /lib missing*, or an existing directory; not a symlink |
| # /sbin missing*, or an existing directory; not a symlink |
| # /usr/bin/ missing*, or an existing directory; not a symlink |
| # /usr/lib/ missing*, or an existing directory; not a symlink |
| # /usr/sbin/ missing*, or an existing directory; not a symlink |
| # |
| # *: must be present for skeletons, can be missing for overlays |
| # |
| # - for merged-usr: all of the above, except: |
| # /bin missing, or a relative symlink to usr/bin |
| # /lib missing, or a relative symlink to usr/lib |
| # /sbin missing, or a relative symlink to usr/sbin |
| # |
| # - for merged-bin: all of the above, except: |
| # /usr/sbin missing, or a relative symlink to bin (thus points |
| # to /usr/bin) |
| # |
| # Input: |
| # -t TYPE the type of root to check: 'skeleton' or 'overlay' |
| # -u check for merged /usr |
| # -b check for merged /usr/bin |
| # $*: the root directories (skeleton, overlays) to check |
| # Output: |
| # stdout: the list of non-compliant paths (empty if compliant). |
| # Exit code: |
| # 0: in case of success (stdout will be empty) |
| # !0: if any directory to check is improperly merged |
| # |
| |
| type= |
| merged_usr=false |
| merged_bin=false |
| while getopts "t:ub" OPT; do |
| case "${OPT}" in |
| (t) type="${OPTARG}";; |
| (u) merged_usr=true;; |
| (b) merged_bin=true;; |
| (:) printf "option %s expects a mandatory argument\n" "${OPTARG}"; exit 1;; |
| (\?) printf "unknown option %s\n" "${OPTARG}"; exit 1;; |
| esac |
| done |
| |
| # Remove the options processed by getopts from $@, |
| # so that $@ now contains only the root directories to check. |
| shift $((OPTIND -1)) |
| |
| if [ "${type}" = "skeleton" ]; then |
| strict=true |
| else |
| strict=false |
| fi |
| |
| report_error() { |
| local type="${1}" |
| local root="${2}" |
| local fmt="${3}" |
| shift 3 |
| |
| if ${first}; then |
| printf "The %s in %s is not properly setup:\n" \ |
| "${type}" "${root}" |
| fi |
| first=false |
| # shellcheck disable=SC2059 # fmt *is* a format string |
| printf " - ${fmt}" "${@}" |
| is_success=false |
| } |
| |
| test_merged() { |
| local type="${1}" |
| local root="${2}" |
| local base="${3}" |
| local dir1="${4}" |
| local dir2="${5}" |
| |
| if ! test -e "${root}${base}${dir1}"; then |
| return 0 |
| elif [ "$(readlink "${root}${base}${dir1}")" = "${dir2}" ]; then |
| return 0 |
| fi |
| |
| # Otherwise, this directory is not merged |
| report_error "${type}" "${root}" \ |
| '%s%s should be missing, or be a relative symlink to %s\n' \ |
| "${base}" "${dir1}" "${dir2}" |
| } |
| |
| test_dir() { |
| local type="${1}" |
| local root="${2}" |
| local base="${3}" |
| local dir="${4}" |
| |
| if ! test -e "${root}${base}${dir}" && ! ${strict}; then |
| return 0 |
| elif test -d "${root}${base}${dir}" && ! test -L "${root}${base}${dir}"; then |
| return 0 |
| fi |
| |
| # Otherwise, this entry is not a proper directory |
| report_error "${type}" "${root}" \ |
| "%s%s should exist, be a directory, and not be a symlink\n" \ |
| "${base}" "${dir}" |
| } |
| |
| is_success=true |
| for root; do |
| first=true |
| test_dir "${type}" "${root}" "/" "usr/bin" |
| test_dir "${type}" "${root}" "/" "usr/lib" |
| if ${merged_usr}; then |
| test_merged "${type}" "${root}" "/" "bin" "usr/bin" |
| test_merged "${type}" "${root}" "/" "lib" "usr/lib" |
| test_merged "${type}" "${root}" "/" "sbin" "usr/sbin" |
| if ${merged_bin}; then |
| test_merged "${type}" "${root}" "/usr/" "sbin" "bin" |
| else |
| test_dir "${type}" "${root}" "/" "usr/sbin" |
| fi |
| else |
| test_dir "${type}" "${root}" "/" "bin" |
| test_dir "${type}" "${root}" "/" "lib" |
| test_dir "${type}" "${root}" "/" "sbin" |
| test_dir "${type}" "${root}" "/" "usr/sbin" |
| fi |
| done |
| |
| ${is_success} |