From 58a01fd257b6f190d6a44662fbd78944fbc608aa Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Mon, 27 May 2019 16:58:01 +0200 Subject: Modularize --- .gitignore | 1 + make.sh | 30 +++++++++ modules/dev.inc | 49 ++++++++++++++ modules/fs/path.inc | 112 ++++++++++++++++++++++++++++++++ modules/regex.inc | 17 +++++ slx-tools | 180 ---------------------------------------------------- slx-tools.template | 23 +++++++ 7 files changed, 232 insertions(+), 180 deletions(-) create mode 100644 .gitignore create mode 100755 make.sh create mode 100644 modules/dev.inc create mode 100644 modules/fs/path.inc create mode 100644 modules/regex.inc delete mode 100644 slx-tools create mode 100644 slx-tools.template diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1b2f329 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/slx-tools diff --git a/make.sh b/make.sh new file mode 100755 index 0000000..a8a4760 --- /dev/null +++ b/make.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +if [ -z "$1" ]; then + echo "Pass abolute path to where slx-tools will be located" + exit 1 +fi + +declare -rg BASE="$1" + +shopt -s globstar +set -e + + +DEFS= + +for file in modules/**/*.inc; do + name="${file#modules/}" + name="${name//\//_}" + name="${name%.inc}" + [[ "$name" =~ ^[a-z0-9_]+$ ]] || continue + echo " * Handling $file" + for var in $( grep -oP '^[a-z0-9_]+\s*\(\)' "$file" | grep -oE '^[a-z0-9_]+' ); do + echo "Found $var" + DEFS+="$var() { . \$BASE/$file; $var "'"$@"; }\n' + done +done + +sed "s#%DEFS%#${DEFS}#;s#^BASE=.*\$#BASE='$BASE'#" slx-tools.template > slx-tools +chmod +x slx-tools + diff --git a/modules/dev.inc b/modules/dev.inc new file mode 100644 index 0000000..c2046d3 --- /dev/null +++ b/modules/dev.inc @@ -0,0 +1,49 @@ +#!/bin/ash + +# Get all partitions with given id (list of /dev/sdXX) +# Works for MBR/DOS by looking at the type (1 byte) +# and for GPT by looking for the label 'OpenSLX-ID$1' +# in case an id was given, or with the given UUID, +# or with the given name. +# The output will be a list of matching devices, +# sorted from largest to smallest. +dev_find_partitions() { + local ID dev exp target + exp= + # target for the scan, defaults to /dev to check everything + if [ -b "$1" ]; then + target="$1" + shift + elif [ -d "$1" ]; then + target="$1/" + else + target="/dev/" + fi + while [ "$#" -gt 0 ]; do + ID="$1" + shift + [ -z "$ID" ] && continue + # if single digit, e.g. 7, look for 0x7 and 0x07 + if regex_imatch "$ID" "^[0-9a-f]$"; then + ID="0?$ID" + fi + + if regex_imatch "$ID" "^[0-9a-f?]{2,3}$"; then # Match two digit and the expanded three digit version from above + # if double digit look for MBR types and OpenSLX-ID$ID GPT labels + exp="$exp|ID_PART_ENTRY_(NAME=OpenSLX-ID|TYPE=0x)$ID" + elif regex_imatch "$ID" "^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$"; then + # if UUID, look for TYPE + exp="$exp|ID_PART_ENTRY_TYPE=$ID" + else + # something else, look for names of partitions / filesystems + ID="$( regex_escape "$ID" )" + exp="$exp|ID_(PART_ENTRY_NAME|FS_LABEL)=$ID" + fi + done + exp="${exp:1}" + for dev in $(find $target* -type b); do + udevadm info --name="$dev" | grep -iqE "($exp)\$" \ + && printf "%s\n" "$(blockdev --getsize64 "$dev") $dev" + done | sort -n -k1 -r | cut -d' ' -f2 +} + diff --git a/modules/fs/path.inc b/modules/fs/path.inc new file mode 100644 index 0000000..f3a041b --- /dev/null +++ b/modules/fs/path.inc @@ -0,0 +1,112 @@ +#!/bin/ash + +DM_STATE_FILE="/run/openslx/dmsetup.state" +readonly DM_STATE_FILE + +# Return the device corresponding to the given path, or +# empty if it's not mounted from a device (tmpfs, cifs, ...) +fs_path_getdev() { + local _dev + _dev="$( df -P "$1" | awk '$1 ~ "^/dev/" {print $1}' )" + [ -n "$_dev" ] || return 1 + [ -b "$_dev" ] || return 1 + printf "%s" "$_dev" +} + +# Return base mount point of given path +# eg. call with "/mnt/nfs/temp/data", returns "/mnt/nfs" +fs_path_getmountpoint() { + local _mp + _mp="$( df -P "$1" | awk '$6 ~ "^/" {print $6}' )" + [ -n "$_mp" ] || return 1 + [ -d "$_mp" ] || return 1 + printf "%s" "$_mp" +} + +# Get fstype of given directory +fs_path_gettype() { + local _mp + _mp="$( df -P "$1" | awk '$6 ~ "^/" {print $6}' )" + [ -n "$_mp" ] || return 1 + [ -d "$_mp" ] || return 1 + awk '$2 == "'"$_mp"'" {print $3}' '/proc/mounts' +} + +# Helper to check whether given directory resides in RAM, either +# by being mounted as tmpfs or not mounted at all in which case +# we assume the same. Returns 0 if so, 1 if otherwise backed, +# 2 when errors occur. +fs_path_isvolatile() { + [ -z "$1" ] && return 2 + local _dev _mp + _dev="$(fs_path_getdev "$1")" + [ -z "$_dev" ] && return 0 # No result, assume tmpfs + [ "$_dev" = "${_dev#/dev/zram}" ] || return 0 # zram is in RAM, like tmpfs + [ "$_dev" != "${_dev#/dev/mapper/}" ] || return 1 # Doesn't start with /dev/mapper, assume some kind of storage + [ -s "$DM_STATE_FILE" ] || return 2 # No point in continuing + _mp="$(fs_path_getmountpoint "$1")" + [ -z "$_mp" ] && return 2 + # it is a device mapper device, check if it was setup in dracut's initramfs. + grep -qE "^$( regex_escape "${_dev}" )\s+$( regex_escape "${_mp}" )\s+type=0" "$DM_STATE_FILE" +} + +# Return free/total space at given path, in KiB. +# If the given path is backed by a thin pool, snapshot, etc. +# it returns a conservative estimate, considering the remaining +# space of the backing device. +# Returns " " +fs_path_space() { + [ -z "$1" ] && return 1 + local _free _total _dev _type _id _name _bs _tmp _tfree _ttotal + _type="$( fs_path_gettype "$1" )" + [ -z "$_type" ] && return 1 + _free="$( df -P "$1" | awk '$6 ~ "^/" {print $4 " " $2}' )" + case "$_type" in # EASY! + tmpfs|nfs*|cifs) + printf "%s" "$_free" + return + esac + _dev="$( fs_path_getdev "$1" )" + if [ "$_dev" = "${_dev#/dev/mapper/}" ]; then # does *NOT* start with /dev/mapper, assume not device mapped + # In case we start using aufs again, check if it needs special handling... + printf "%s" "$_free" + return + fi + # Split, since we might need to do Min() + _total="${_free#* }" + _free="${_free% *}" + _type="$( dmsetup status "$_dev" | awk '{print $3}' )" + _tfree=0 + _ttotal=0 + case "$_type" in + snapshot) + # Field 4 is used/total in sectors of 512 byte, so divide by two for KiB + _tmp="$( dmsetup status "$_dev" | awk '{split($4, a, "/"); print (a[2] - a[1]) / 2 " " a[2] / 2}' )" + _tfree="${_tmp% *}" + _ttotal="${_tmp#* }" + ;; + thin) + # Determine matching pool + _id="$( dmsetup table "$_dev" | awk '{print $4}' )" + if [ -n "$_id" ]; then + _name="$( cat "/sys/dev/block/${_id}/dm/name" )" + if [ -n "$_name" ]; then + # Get data block size (in sectors of 512 byte) + _bs="$( dmsetup table "$_name" | awk '{print $6}' )" + if [ "$_bs" -gt 0 ]; then + # Field 6 is used/total (in data blocks), so transform into KiB + _tmp="$( dmsetup status "$_name" | awk -v "bs=$_bs" '{split($6, a, "/"); print ((a[2] - a[1]) / 2) * bs " " (a[2] / 2) * bs}' )" + _tfree="${_tmp% *}" + _ttotal="${_tmp#* }" + fi + fi + fi + ;; + esac + if [ "$_tfree" -ge 0 ] && [ "$_ttotal" -gt 0 ]; then + [ "$_tfree" -lt "$_free" ] && _free="$_tfree" + [ "$_ttotal" -lt "$_total" ] && _total="$_ttotal" + fi + printf "%s" "$_free $_total" +} + diff --git a/modules/regex.inc b/modules/regex.inc new file mode 100644 index 0000000..21c3d65 --- /dev/null +++ b/modules/regex.inc @@ -0,0 +1,17 @@ +#!/bin/ash + +# Match $1 against perl regex $2, case insensitive +regex_imatch() { + printf "%s" "$1" | grep -qPi "$2" +} + +# Escapes $1 for use in grep -E/-P, sed -r, etc. +# Pass --slash to escape '/' too. (e.g. for sed) +regex_escape() { + if [ "x$1" = "x--slash" ]; then + printf "%s" "$2" | sed 's,[][()\.^$?*+/],\\&,g' + else + printf "%s" "$1" | sed 's/[][()\.^$?*+]/\\&/g' + fi +} + diff --git a/slx-tools b/slx-tools deleted file mode 100644 index 1a95fa1..0000000 --- a/slx-tools +++ /dev/null @@ -1,180 +0,0 @@ -#!/bin/ash - -# Collection of small bash functions utilized in various scripts. -################################################################################ -case "$PATH" in - *opt/openslx*) ;; - *) export PATH="$PATH:/opt/openslx/bin:/opt/openslx/sbin" ;; -esac - -DM_STATE_FILE="/run/openslx/dmsetup.state" -readonly DM_STATE_FILE - -# Return the device corresponding to the given path, or -# empty if it's not mounted from a device (tmpfs, cifs, ...) -fs_path_getdev() { - local _dev - _dev="$( df -P "$1" | awk '$1 ~ "^/dev/" {print $1}' )" - [ -n "$_dev" ] || return 1 - [ -b "$_dev" ] || return 1 - printf "%s" "$_dev" -} - -# Return base mount point of given path -fs_path_getmountpoint() { - local _mp - _mp="$( df -P "$1" | awk '$6 ~ "^/" {print $6}' )" - [ -n "$_mp" ] || return 1 - [ -d "$_mp" ] || return 1 - printf "%s" "$_mp" -} - -# Get fstype of given directory -fs_path_gettype() { - local _mp - _mp="$( df -P "$1" | awk '$6 ~ "^/" {print $6}' )" - [ -n "$_mp" ] || return 1 - [ -d "$_mp" ] || return 1 - awk '$2 == "'"$_mp"'" {print $3}' '/proc/mounts' -} - -# Helper to check whether given directory resides in RAM, either -# by being mounted as tmpfs or not mounted at all in which case -# we assume the same. Returns 0 if so, 1 if otherwise backed, -# 2 when errors occur. -fs_path_isvolatile() { - [ -z "$1" ] && return 2 - local _dev _mp - _dev="$(fs_path_getdev "$1")" - [ -z "$_dev" ] && return 0 # No result, assume tmpfs - [ "$_dev" = "${_dev#/dev/zram}" ] || return 0 # zram is in RAM, like tmpfs - [ "$_dev" != "${_dev#/dev/mapper/}" ] || return 1 # Doesn't start with /dev/mapper, assume some kind of storage - [ -s "$DM_STATE_FILE" ] || return 2 # No point in continuing - _mp="$(fs_path_getmountpoint "$1")" - [ -z "$_mp" ] && return 2 - # it is a device mapper device, check if it was setup in dracut's initramfs. - grep -qE "^$( regex_escape "${_dev}" )\s+$( regex_escape "${_mp}" )\s+type=0" "$DM_STATE_FILE" -} - -# Return free/total space at given path, in KiB. -# If the given path is backed by a thin pool, snapshot, etc. -# it returns a conservative estimate, considering the remaining -# space of the backing device. -# Returns " " -fs_path_space() { - [ -z "$1" ] && return 1 - local _free _total _dev _type _id _name _bs _tmp _tfree _ttotal - _type="$( fs_path_gettype "$1" )" - [ -z "$_type" ] && return 1 - _free="$( df -P "$1" | awk '$6 ~ "^/" {print $4 " " $2}' )" - case "$_type" in # EASY! - tmpfs|nfs*|cifs) - printf "%s" "$_free" - return - esac - _dev="$( fs_path_getdev "$1" )" - if [ "$_dev" = "${_dev#/dev/mapper/}" ]; then # does *NOT* start with /dev/mapper, assume not device mapped - # In case we start using aufs again, check if it needs special handling... - printf "%s" "$_free" - return - fi - # Split, since we might need to do Min() - _total="${_free#* }" - _free="${_free% *}" - _type="$( dmsetup status "$_dev" | awk '{print $3}' )" - _tfree=0 - _ttotal=0 - case "$_type" in - snapshot) - # Field 4 is used/total in sectors of 512 byte, so divide by two for KiB - _tmp="$( dmsetup status "$_dev" | awk '{split($4, a, "/"); print (a[2] - a[1]) / 2 " " a[2] / 2}' )" - _tfree="${_tmp% *}" - _ttotal="${_tmp#* }" - ;; - thin) - # Determine matching pool - _id="$( dmsetup table "$_dev" | awk '{print $4}' )" - if [ -n "$_id" ]; then - _name="$( cat "/sys/dev/block/${_id}/dm/name" )" - if [ -n "$_name" ]; then - # Get data block size (in sectors of 512 byte) - _bs="$( dmsetup table "$_name" | awk '{print $6}' )" - if [ "$_bs" -gt 0 ]; then - # Field 6 is used/total (in data blocks), so transform into KiB - _tmp="$( dmsetup status "$_name" | awk -v "bs=$_bs" '{split($6, a, "/"); print ((a[2] - a[1]) / 2) * bs " " (a[2] / 2) * bs}' )" - _tfree="${_tmp% *}" - _ttotal="${_tmp#* }" - fi - fi - fi - ;; - esac - if [ "$_tfree" -ge 0 ] && [ "$_ttotal" -gt 0 ]; then - [ "$_tfree" -lt "$_free" ] && _free="$_tfree" - [ "$_ttotal" -lt "$_total" ] && _total="$_ttotal" - fi - printf "%s" "$_free $_total" -} - -# Match $1 against perl regex $2, case insensitive -regex_imatch() { - printf "%s" "$1" | grep -qPi "$2" -} - -# Escapes $1 for use in grep -E/-P, sed -r, etc. -# Pass --slash to escape '/' too. (e.g. for sed) -regex_escape() { - if [ "x$1" = "x--slash" ]; then - printf "%s" "$2" | sed 's,[][()\.^$?*+/],\\&,g' - else - printf "%s" "$1" | sed 's/[][()\.^$?*+]/\\&/g' - fi -} - -# Get all partitions with given id (list of /dev/sdXX) -# Works for MBR/DOS by looking at the type (1 byte) -# and for GPT by looking for the label 'OpenSLX-ID$1' -# in case an id was given, or with the given UUID, -# or with the given name. -# The output will be a list of matching devices, -# sorted from largest to smallest. -dev_find_partitions() { - local ID dev exp target - exp= - # target for the scan, defaults to /dev to check everything - if [ -b "$1" ]; then - target="$1" - shift - elif [ -d "$1" ]; then - target="$1/" - else - target="/dev/" - fi - while [ "$#" -gt 0 ]; do - ID="$1" - shift - [ -z "$ID" ] && continue - # if single digit, e.g. 7, look for 0x7 and 0x07 - if regex_imatch "$ID" "^[0-9a-f]$"; then - ID="0?$ID" - fi - - if regex_imatch "$ID" "^[0-9a-f?]{2,3}$"; then # Match two digit and the expanded three digit version from above - # if double digit look for MBR types and OpenSLX-ID$ID GPT labels - exp="$exp|ID_PART_ENTRY_(NAME=OpenSLX-ID|TYPE=0x)$ID" - elif regex_imatch "$ID" "^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$"; then - # if UUID, look for TYPE - exp="$exp|ID_PART_ENTRY_TYPE=$ID" - else - # something else, look for names of partitions / filesystems - ID="$( regex_escape "$ID" )" - exp="$exp|ID_(PART_ENTRY_NAME|FS_LABEL)=$ID" - fi - done - exp="${exp:1}" - for dev in $(find $target* -type b); do - udevadm info --name="$dev" | grep -iqE "($exp)\$" \ - && printf "%s\n" "$(blockdev --getsize64 "$dev") $dev" - done | sort -n -k1 -r | cut -d' ' -f2 -} - diff --git a/slx-tools.template b/slx-tools.template new file mode 100644 index 0000000..0fb99b3 --- /dev/null +++ b/slx-tools.template @@ -0,0 +1,23 @@ +#!/bin/ash + +# Collection of small ash functions utilized in various scripts. +################################################################################ + +case "$PATH" in + *opt/openslx*) ;; + *) export PATH="$PATH:/opt/openslx/bin:/opt/openslx/sbin" ;; +esac + +BASE=. + +%DEFS% + +if [ "${0##*/}" = "slx-tools" ]; then + if [ $# -ge 1 ]; then + "$@" + else + echo "slx-tools [function] [args...]" + fi +else + : +fi -- cgit v1.2.3-55-g7522