From 05538ca8f6024b694deccbc7789030bebfd651aa Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 5 Apr 2019 13:13:49 +0200 Subject: Refactor, rename, break things --- slx-tools | 176 +++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 129 insertions(+), 47 deletions(-) diff --git a/slx-tools b/slx-tools index 972975c..3409ca7 100644 --- a/slx-tools +++ b/slx-tools @@ -1,98 +1,180 @@ -#!/bin/bash +#!/bin/ash # Collection of small bash functions utilized in various scripts. ################################################################################ -export PATH=$PATH:/opt/openslx/bin:/opt/openslx/sbin +case "$PATH" in + *opt/openslx*) ;; + *) export PATH="$PATH:/opt/openslx/bin:/opt/openslx/sbin" ;; +esac -dmstate="/run/openslx/dmsetup.state" -readonly dmstate +DM_STATE_FILE="/run/openslx/dmsetup.state" +readonly DM_STATE_FILE -df_dev_only() { - df -P "$1" 2>&1 | awk '$1 ~ /^\/dev\// {print $0}' +# 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" } -get_backing_dev() { - local _dev="$(df_dev_only "$1" | awk '{print $1}' )" - [ -b "$_dev" ] || return 1 - echo "$_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_backing_dev_mp() { - local _mp="$(df_dev_only "$1" | awk '{print $6}')" + +# 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 - echo "$_mp" + awk '$3 == "'"$_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. -is_volatile() { +fs_path_isvolatile() { [ -z "$1" ] && return 2 - local _dev="$(get_backing_dev "$1")" + 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 - local _mp="$(get_backing_dev_mp "$1")" + [ -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. - if [ -s "$dmstate" ]; then - grep -qE "^${_dev}\s+${_mp}\s+type=0" "$dmstate" - return $? - fi - return 2 + grep -qE "^$( regex_escape "${_dev}" )\s+$( regex_escape "${_mp}" )\s+type=0" "$DM_STATE_FILE" } -get_dm_backing_size() { +# 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 _dev="$(get_backing_dev $1)" - [ "$_dev" != "${_dev#/dev/mapper/}" ] || return 1 # doesn't start with /dev/mapper, assume not device mapped - local _dm_line="$(grep -m1 -E "^${_dev}\s+/\s+type=[^0]" "$dmstate")" - [ -z "$_dm_line" ] && return 1 - local _dm_dev_size="$(echo "$_dm_line" | grep -Po '(?<=physical_size=|virtual_size=)\w*' )" - echo ${_dm_dev_size:--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" } -regex_match() { - echo "$1" | grep -qP "$2" +# 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_partitions_by_id () { +# 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 - target=/dev if [ -b "$1" ]; then - target="$1"'*' + target="$1" shift + elif [ -d "$1" ]; then + target="$1/" + else + target="/dev/" fi - # support commas and pipes to separate identifiers - local args=$@ - set -- ${args//[,|]/ } - while [ $# -gt 0 ]; do - ID=$1 + while [ "$#" -gt 0 ]; do + ID="$1" shift [ -z "$ID" ] && continue # if single digit, e.g. 7, look for 0x7 and 0x07 - #[[ $ID =~ ^[0-9]$ ]] && ID="0?$ID" - if regex_match "$ID" "^[0-9]$" ; then + if regex_imatch "$ID" "^[0-9a-f]$"; then ID="0?$ID" fi - if regex_match "$ID" "^[0-9]{2}$"; then + 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_match "$ID" "^(0x[0-9]{2}|[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12})$"; then - # if full MBR type (e.g. 0x44) or UUID look for TYPE + 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} - #echo "Partition find is '$exp'" >&2 - for dev in $(find $target -type b); do + exp="${exp:1}" + for dev in $(find $target* -type b); do udevadm info --name="$dev" | grep -iqE "($exp)\$" \ - && echo "$(blockdev --getsize64 "$dev") $dev" + && printf "%s\n" "$(blockdev --getsize64 "$dev") $dev" done | sort -n -k1 -r | cut -d' ' -f2 } -- cgit v1.2.3-55-g7522