summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--slx-tools176
1 files 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 "<free> <total>"
+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
}