summaryrefslogblamecommitdiffstats
path: root/slx-tools
blob: 1a95fa1958221e635894d960f45a5309bf3bef29 (plain) (tree)
1
2
3
4
5
6
7
8
9
          


                                                                                



                                                                    
 

                                          
 







                                                               

 






                                                          
 





                                                          
                                 
                                                        
 
 



                                                                
                      
                               

                                     
                                                            

                                                                                                                       

                                                                    
                                 
                                                                                    
                                                                                                      

 





                                                               
                               


















































                                                                                                                                                                                

 












                                                               

 







                                                      


                                                                   
                            
                           
                     



                              
          

                                


                                                                
                                                        


                                  
                                                                                                                                

                                                                                         

                                                                                           


                                                                                    
                                                    


                                                                    

                                               
                                                                   
                                                                              

                                             
 
#!/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 "<free> <total>"
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
}