#!/bin/bash
#
# Copyright (C) 2007-2010 Red Hat, Inc. All rights reserved.
#
# This file is part of LVM2.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions
# of the GNU General Public License v.2.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Author: Zdenek Kabelac <zkabelac at redhat.com>
#
# Author: Lukas Czerner <lczerner at redhat.com>
#    - Significantly extended fsadm functionality
#    - Implemented new commands "add", "create", "list", "remove"
#    - Rework of "resize" command
#
# Utility to manage logical volumes
#
# Needed utilities:
#   mount, umount, grep, readlink, blockdev, blkid, fsck, xfs_check, bc, df
#   xfs_db, mktemp
#
# ext2/ext3/ext4: resize2fs, tune2fs
# reiserfs: resize_reiserfs, reiserfstune
# xfs: xfs_growfs, xfs_info
#
# Return values:
#   0 success
#   1 error
#   2 break detected
#   3 unsupported online filesystem check for given mounted fs

TOOL=$(basename "$0")

# utilities
TUNE_EXT=tune2fs
RESIZE_EXT=resize2fs
TUNE_REISER=reiserfstune
RESIZE_REISER=resize_reiserfs
TUNE_XFS=xfs_info
RESIZE_XFS=xfs_growfs

MOUNT=mount
UMOUNT=umount
MKDIR=mkdir
RMDIR=rmdir
BLOCKDEV=blockdev
BLKID=blkid
DATE=date
GREP=grep
READLINK=readlink
READLINK_E="-e"
FSCK=fsck
XFS_CHECK=xfs_check
BC=bc
DF=df

# user may override lvm location by setting LVM_BINARY
LVM=${LVM_BINARY:-lvm}

FORCE=${_FSADM_FORCE}
DRY=0
VERB=
FORCE=
EXTOFF=0
FSTYPE=unknown
VOLUME=unknown
TEMPDIR="${TMPDIR:-/tmp}/${TOOL}_${RANDOM}$$/m"
DM_DEV_DIR="${DM_DEV_DIR:-/dev}"
BLOCKSIZE=
BLOCKCOUNT=
MOUNTPOINT=
MOUNTED=
REMOUNT=
IN_GROUP=
NOT_IN_GROUP=
GROUP=
PROCMOUNTS="/proc/mounts"
PROCDEVICES="/proc/devices"
PROCPARTITIONS="/proc/partitions"
NULL="$DM_DEV_DIR/null"

CONFIG_PATHS="/etc/fsadm.conf ~/fsadm.conf"
CONFIG=

MAX_VGS=999

# without bash $'\n'
NL='
'

check_usage() {
	echo "  ${TOOL} [options] check device"
	echo "    - Check the filesystem on device using fsck"
	echo
}

resize_usage() {
	echo "  ${TOOL} [options] resize [resize options] [device...] volume"
	echo "    - Change the size of the logical volume and file system on it"
	echo
	echo "  Resize options:"
	echo "    size=n        Resize the volume to the given size."
	echo "    size=+n       Extend the volume by the given size."
	echo "    size=-n       Shrink the volume by the given size."
	echo "    [device...]   Devices to use when extending the volume. If the"
	echo "                  device is not in any pool, it is added to the"
	echo "                  volume's pool prior to the extension."
	echo "    A suffix K, M, G, T, P, E can be used to define size units."
	echo
}

create_usage() {
	echo "  ${TOOL} [options] create [create options] [device...] [pool]"
	echo "    - Create a new logical volume from the pool"
	echo
	echo "  Create options:"
	echo "    stripesize=n     Gives the number of kilobytes for the granularity"
	echo "                     of stripes (see man 8 fsadm for more information)."
	echo "                     This is optional and if not given, linear logical"
	echo "                     volume will be created."
	echo "    name=n           The name for the new logical volume. Without this"
	echo "                     option the default names of "lvol#" will be generated"
	echo "                     where # is internal number of the logical volume"
	echo "                     The default can be changed by setting LVOL_PREFIX"
	echo "                     fsadm configuration file."
	echo "    fstyp=n | fs=n   Gives the file system type to create on the new"
	echo "                     logical volume. Supported file systems are (ext3,"
	echo "                     ext4, xfs). This is optional and if not given"
	echo "                     file system will not be created."
	echo "    size=n           Gives the size to allocate for the new logical volume"
	echo "                     A size suffix K, M, G, T, P, E can be used to define"
	echo "                     units (see man 8 fsadm for more information). This"
	echo "                     is optional if if not given maximum size will be"
	echo "                     used."
	echo "    stripes=n        Gives the number of stripes. This is equal to the"
	echo "                     number of physical volumes to scatter the logical"
	echo "                     volume. This is optional and if stripesize is set"
	echo "                     and multiple devices are provided stripes is"
	echo "                     determined automatically."
	echo
}

list_usage() {
	echo "  ${TOOL} [options] list [filesystems|fs|devices|dev|pool]"
	echo "    - List devices, file systems and pools in your system"
	echo
	echo "  List options:"
	echo "    filesystems | fs   List all logical volumes."
	echo "    devices | dev      List all devices except the ones create by DM."
	echo "    pool               List all available pools to create new logical"
	echo "                       volumes from."
	echo
}

add_usage() {
	echo "  ${TOOL} [options] add device [device...] [pool]"
	echo "    - Add one, or more devices into the defined, or default pool"
	echo
}

remove_usage() {
	echo "  ${TOOL} [options] remove [remove options] item [item...]"
	echo "    - Remove one, or more logical volumes, devices, or pools defined"
	echo "      by item"
	echo
	echo "  Remove options:"
	echo "    item   Can be logical volume, mount point, device in the pool"
	echo "             or name of the pool to remove."
	echo "    --all    Removes all pools and corresponding logical volumes"
	echo

}

tool_usage() {
	echo "${TOOL}: Utility to manage logical volumes"
	echo
	check_usage
	resize_usage
	create_usage
	list_usage
	add_usage
	remove_usage
	echo "  Options:"
	echo "    -h | --help           Show this help message"
	echo "    -v | --verbose        Be verbose"
	echo "    -e | --ext-offline    unmount filesystem before ext2/ext3/ext4 resize"
	echo "    -f | --force          Force all commands without asking questions"
	echo "    -n | --dry-run        Print commands without running them"
	echo "    -c | --config path    Specify the path to the configuration file"
	echo
	exit
}

verbose() {
	test -n "$VERB" && echo "$TOOL: $@" || true
}

error() {
	echo "$TOOL: $@" >&2
	cleanup 1
}

warn() {
	echo "$TOOL: $@" >&2
}

dry_nofail() {
	if [ "$DRY" -ne 0 ]; then
		verbose "Dry execution $@"
		return 0
	fi
	verbose "Executing $@"
	local IFS=" "
	eval "$*"
}

dry() {
	dry_nofail "$@" || error "FAILED. Exitting!"
}

float_math() {
	if [ $# -le 0 ]; then
		return
	fi
	result=$(LANG=C echo "scale=2; $@" | "$BC" -q 2> /dev/null)
	echo "$result"
}

is_natural() {
	case "$1" in
		"" | *[!0-9]*) return 1;;
		*) return 0;;
	esac
}

cleanup() {
	trap '' 2
	# reset MOUNTPOINT - avoid recursion
	test "$MOUNTPOINT" = "$TEMPDIR" && MOUNTPOINT="" temp_umount
	if [ -n "$REMOUNT" ]; then
		verbose "Remounting unmounted filesystem back"
		dry "$MOUNT" "$VOLUME" "$MOUNTED"
	fi
	trap 2

	test "$1" -eq 2 && verbose "Break detected"

	# error exit status for break
	exit ${1:-1}
}

#####################################
# Convet the size into human readable
# form. size in Bytes expected
#####################################
humanize_size() {
	size="$1"
	count=0
	while [ ${size%%.*} -ge 1024 ] && [ $count -lt 7 ]; do
		size=$(float_math $size/1024)
		count=$(($count+1))
	done
	case $count in
		0) unit="KiB" ;;
		1) unit="MiB" ;;
		2) unit="GiB" ;;
		3) unit="TiB" ;;
		4) unit="PiB" ;;
		5) unit="EiB" ;;
		6) unit="ZiB" ;;
		7) unit="YiB" ;;
		*) unit="???" ;;
	esac
	echo "$size $unit"
}

#############################
# Get size of entN filesystem
# by reading tune2fs output
#############################
get_ext_size() {
	local IFS=$NL
	for i in $(LANG=C $TUNE_EXT -l "$VOLUME"); do
		case "$i" in
			"Block size"*) bsize=${i##*  } ;;
			"Block count"*) bcount=${i##*  } ;;
			"Reserved block count"*) rbcount=${i##*  } ;;
			"Free blocks"*) fbcount=${i##*  } ;;
		esac
	done

	total=$(($bcount*$bsize/1024))
	TOTAL=$(humanize_size $total)
	used=$((($bcount-$fbcount)*$bsize/1024))
	USED=$(humanize_size $used)
	free=$((($fbcount-$rbcount)*$bsize/1024))
	FREE=$(humanize_size $free)
}

############################
# Get size of xfs file system
# by reading the df output or
# examine file system with
# xfs_db tool
#############################
get_xfs_size() {
	local IFS=$NL
	if [ -z "$MOUNTED" ]; then

		for i in $(LANG=C xfs_db -c 'sb' -c 'print blocksize fdblocks dblocks logblocks agcount' $VOLUME); do
			case "$i" in
				"blocksize ="*) bsize=${i##* } ;;
				"fdblocks ="*) fbcount=${i##* } ;;
				"dblocks ="*) bcount=${i##* } ;;
				"logblocks ="*) lbcount=${i##* } ;;
				"agcount ="*) agcount=${i##* } ;;
			esac
		done
		bcount=$(($bcount-$lbcount))
		fbcount=$(($fbcount-(4+(4*$agcount))))

		total=$(($bcount*$bsize/1024))
		TOTAL=$(humanize_size $total)
		used=$((($bcount-$fbcount)*$bsize/1024))
		USED=$(humanize_size $used)
		free=$(($fbcount*$bsize/1024))
		FREE=$(humanize_size $free)
		return
	fi
	line=$("$DF" -k "$VOLUME" | "$GREP" -e "$MOUNTED$" | sed -e 's/  */ /g')
	line=${line#* }
	total=$(echo $line | cut -d' ' -f1)
	TOTAL=$(humanize_size $total)
	free=$(echo $line | cut -d' ' -f3)
	FREE=$(humanize_size $free)
	used=$(echo $line | cut -d' ' -f2)
	USED=$(humanize_size $used)
}

detect_fs_size() {
	if [ -z "$FSTYPE" ]; then
		return
	fi
	case "$FSTYPE" in
		ext[234]) get_ext_size ;;
		xfs) get_xfs_size ;;
		*) return 1 ;;
	esac
	return 0
}

# convert parameter from Exa/Peta/Tera/Giga/Mega/Kilo/Bytes and blocks
# (2^(60/50/40/30/20/10/0)) into Kilobytes (KiB)
decode_size() {
	case "$1" in
	 *[eE]) NEWSIZE=$(float_math "${1%[eE]} * 1125899906842624") ;;
	 *[pP]) NEWSIZE=$(float_math "${1%[pP]} * 1099511627776") ;;
	 *[tT]) NEWSIZE=$(float_math "${1%[tT]} * 1073741824") ;;
	 *[gG]) NEWSIZE=$(float_math "${1%[gG]} * 1048576") ;;
	 *[mM]) NEWSIZE=$(float_math "${1%[mM]} * 1024") ;;
	 *[kK]) NEWSIZE=${1%[kK]} ;;
	 *[bB]) NEWSIZE=$(float_math "${1%[bB]} / 1024") ;;
	esac

	NEWSIZE=${NEWSIZE%%.*}
	NEWBLOCKCOUNT=$(float_math "($NEWSIZE / $2) * 1024")
	NEWBLOCKCOUNT=${NEWBLOCKCOUNT%%.*}
}

get_volume() {
	VOLUME=${DM_DEV_DIR}/${1/#"${DM_DEV_DIR}/"/}
	if [ -h "$VOLUME" ]; then
		VOLUME=$("$READLINK" "$READLINK_E" "$VOLUME") || error "Cannot get readlink \"$VOLUME\""
	fi
	RVOLUME=$VOLUME
	case "$RVOLUME" in
          # hardcoded /dev  since udev does not create these entries elsewhere
	  /dev/dm-[0-9]*)
		read </sys/block/${RVOLUME#/dev/}/dm/name SYSVOLUME 2>&1 && VOLUME="$DM_DEV_DIR/mapper/$SYSVOLUME"
		;;
	esac
}

# detect filesystem on the given device
# dereference device name if it is symbolic link
detect_fs() {
	get_volume "$1"
	# use null device as cache file to be sure about the result
	# not using option '-o value' to be compatible with older version of blkid
	FSTYPE=$("$BLKID" -c "$NULL" -s TYPE "$VOLUME") || return 1
	FSTYPE=${FSTYPE##*TYPE=\"} # cut quotation marks
	FSTYPE=${FSTYPE%%\"*}
	return 0
}

# check if the given device is already mounted and where
# FIXME: resolve swap usage and device stacking
detect_mounted()  {
	test -e "$PROCMOUNTS" || error "Cannot detect mounted device \"$VOLUME\""

	MOUNTED=$("$GREP" -e "^$VOLUME " "$PROCMOUNTS")

	# for empty string try again with real volume name
	test -z "$MOUNTED" && MOUNTED=$("$GREP" -e "^$RVOLUME " "$PROCMOUNTS")

	# cut device name prefix and trim everything past mountpoint
	# echo translates \040 to spaces
	MOUNTED=${MOUNTED#* }
	MOUNTED=$(echo -n -e ${MOUNTED%% *})

	# for systems with different device names - check also mount output
	if test -z "$MOUNTED" ; then
		MOUNTED=$(LANG=C "$MOUNT" | "$GREP" -e "^$VOLUME ")
		test -z "$MOUNTED" && MOUNTED=$(LANG=C "$MOUNT" | "$GREP" -e "^$RVOLUME ")
		MOUNTED=${MOUNTED##* on }
		MOUNTED=${MOUNTED% type *} # allow type in the mount name
	fi

	test -n "$MOUNTED"
}

# get the full size of device in bytes
detect_device_size() {
	# check if blockdev supports getsize64
	"$BLOCKDEV" 2>&1 | "$GREP" getsize64 >"$NULL"
	if test $? -eq 0; then
		DEVSIZE=$("$BLOCKDEV" --getsize64 "$VOLUME") || error "Cannot read size of device \"$VOLUME\""
	else
		DEVSIZE=$("$BLOCKDEV" --getsize "$VOLUME") || error "Cannot read size of device \"$VOLUME\""
		SSSIZE=$("$BLOCKDEV" --getss "$VOLUME") || error "Cannot block size read device \"$VOLUME\""
		DEVSIZE=$(($DEVSIZE * $SSSIZE))
	fi
}

# round up $1 / $2
# could be needed to gaurantee 'at least given size'
# but it makes many troubles
round_up_block_size() {
	echo $(( ($1 + $2 - 1) / $2 ))
}

temp_mount() {
	dry "$MKDIR" -p -m 0000 "$TEMPDIR" || error "Failed to create $TEMPDIR"
	dry "$MOUNT" "$VOLUME" "$TEMPDIR" || error "Failed to mount $TEMPDIR"
}

temp_umount() {
	dry "$UMOUNT" "$TEMPDIR" || error "Failed to umount \"$TEMPDIR\""
	dry "$RMDIR" "${TEMPDIR}" || error "Failed to remove \"$TEMPDIR\""
	dry "$RMDIR" "${TEMPDIR%%m}" || error "Failed to remove \"${TEMPDIR%%m}\""
}

yes_no() {
	echo -n "$@? [Y|n] "

	if [ -n "$FORCE" ]; then
		echo y
		return 0
	fi

	while read -r -s -n 1 ANS ; do
		case "$ANS" in
		 "y" | "Y" | "") echo y ; return 0 ;;
		 "n" | "N") echo n ; return 1 ;;
		esac
	done
}

try_umount() {
	yes_no "Do you want to unmount \"$MOUNTED\"" && dry "$UMOUNT" "$MOUNTED" && return 0
	error "Cannot proceed with mounted filesystem \"$MOUNTED\""
}

validate_parsing() {
	test -n "$BLOCKSIZE" -a -n "$BLOCKCOUNT" || error "Cannot parse $1 output"
}
####################################
# Resize ext2/ext3/ext4 filesystem
# - unmounted or mounted for upsize
# - unmounted for downsize
####################################
resize_ext() {
	verbose "Parsing $TUNE_EXT -l \"$VOLUME\""
	for i in $(LANG=C "$TUNE_EXT" -l "$VOLUME"); do
		case "$i" in
		  "Block size"*) BLOCKSIZE=${i##*  } ;;
		  "Block count"*) BLOCKCOUNT=${i##*  } ;;
		esac
	done
	validate_parsing "$TUNE_EXT"
	decode_size $1 $BLOCKSIZE
	FSFORCE=$FORCE

	# ext2 does NOT support online resize
	if [ "$FSTYPE" == "ext2" ]; then
		EXTOFF=1
	fi

	detect_mounted
	if [ "$NEWBLOCKCOUNT" -lt "$BLOCKCOUNT" -o "$EXTOFF" -eq 1 ]; then
		[ "$MOUNTED" ] && verbose "$RESIZE_EXT needs unmounted filesystem" && try_umount
		REMOUNT=$MOUNTED
	fi

	# We should really do the fsck before every resize.
	if [ -z "$MOUNTED" ] || [ "$REMOUNT" ]; then
		# Forced fsck -f for umounted extX filesystem.
		case "$-" in
		  *i*) dry "$FSCK" -f "$VOLUME" ;;
		  *) dry "$FSCK" -f -p "$VOLUME" ;;
		esac
	fi

	verbose "Resizing filesystem on device \"$VOLUME\" to $NEWSIZE KiB ($BLOCKCOUNT -> $NEWBLOCKCOUNT blocks of $BLOCKSIZE bytes)"
	dry "$RESIZE_EXT" $FSFORCE "$VOLUME" "$NEWBLOCKCOUNT"
}

#############################
# Resize reiserfs filesystem
# - unmounted for upsize
# - unmounted for downsize
#############################
resize_reiser() {
	detect_mounted && verbose "ReiserFS resizes only unmounted filesystem" && try_umount
	REMOUNT=$MOUNTED
	verbose "Parsing $TUNE_REISER \"$VOLUME\""
	for i in $(LANG=C "$TUNE_REISER" "$VOLUME"); do
		case "$i" in
		  "Blocksize"*) BLOCKSIZE=${i##*: } ;;
		  "Count of blocks"*) BLOCKCOUNT=${i##*: } ;;
		esac
	done
	validate_parsing "$TUNE_REISER"
	decode_size $1 $BLOCKSIZE
	verbose "Resizing \"$VOLUME\" $BLOCKCOUNT -> $NEWBLOCKCOUNT blocks ($NEWSIZE KiB, bs: $NEWBLOCKCOUNT)"
	if [ -n "$FORCE" ]; then
		echo y | dry "$RESIZE_REISER" -s "${NEWSIZE}K" "$VOLUME"
	else
		dry "$RESIZE_REISER" -s "${NEWSIZE}K" "$VOLUME"
	fi
}

########################
# Resize XFS filesystem
# - mounted for upsize
# - cannot downsize
########################
resize_xfs() {
	detect_mounted
	MOUNTPOINT=$MOUNTED
	if [ -z "$MOUNTED" ]; then
		MOUNTPOINT=$TEMPDIR
		temp_mount || error "Cannot mount Xfs filesystem"
	fi
	verbose "Parsing $TUNE_XFS \"$MOUNTPOINT\""
	for i in $(LANG=C "$TUNE_XFS" "$MOUNTPOINT"); do
		case "$i" in
		  "data"*) BLOCKSIZE=${i##*bsize=} ; BLOCKCOUNT=${i##*blocks=} ;;
		esac
	done
	BLOCKSIZE=${BLOCKSIZE%%[^0-9]*}
	BLOCKCOUNT=${BLOCKCOUNT%%[^0-9]*}
	validate_parsing "$TUNE_XFS"
	decode_size $1 $BLOCKSIZE
	if [ $NEWBLOCKCOUNT -gt $BLOCKCOUNT ]; then
		verbose "Resizing Xfs mounted on \"$MOUNTPOINT\" to fill device \"$VOLUME\""
		dry "$RESIZE_XFS" "$MOUNTPOINT"
	elif [ $NEWBLOCKCOUNT -eq $BLOCKCOUNT ]; then
		verbose "Xfs filesystem already has the right size"
	else
		error "Xfs filesystem shrinking is unsupported"
	fi
}

#####################################
# Create extN filesystem with respect
# to the striped volume
#####################################
make_ext() {
	device="$1"
	fstyp="$2"
	bsize=4

	if [ "$FORCE" ]; then
		force="-F"
	fi

	dry "mkfs.$fstyp" "$force" -b "$(($bsize*1024))" $device
}

############################################
# Create a file system just using mkfs.fstyp
############################################
generic_make_fs() {
	device=$1
	fstyp=$2
	bsize=4096

	if [ "$FORCE" ]; then
		force="-f"
	fi

	dry "mkfs.$fstyp" $force "$device"
}

####################
# Resize filesystem
####################
resize_fs() {
	NEWSIZE=$2
	detect_fs "$1" ||  error "Cannot get FSTYPE of \"$VOLUME\""
	verbose "\"$FSTYPE\" filesystem found on \"$VOLUME\""
	if [ "$NEWSIZE" ]; then
		decode_size $NEWSIZE
		is_natural $NEWSIZE ||  error "$NEWSIZE is not valid number for file system size"
	fi
	detect_device_size
	verbose "Device \"$VOLUME\" size is $DEVSIZE bytes"
	# if the size parameter is missing use device size
	#if [ -n "$NEWSIZE" -a $NEWSIZE <
	test -z "$NEWSIZE" && NEWSIZE=${DEVSIZE}b
	local IFS=$NL
	case "$FSTYPE" in
	  "ext3"|"ext2"|"ext4") resize_ext $NEWSIZE ;;
	  "reiserfs") resize_reiser $NEWSIZE ;;
	  "xfs") resize_xfs $NEWSIZE ;;
	  *) error "Filesystem \"$FSTYPE\" on device \"$VOLUME\" is not supported by this tool" ;;
	esac || error "Resize $FSTYPE failed"
}

resize_lvolume() {
	lvname=$1
	newsize=$2

	lvsize=$(LANG=C "$LVM" lvs -o lv_size --separator ' ' --noheadings --nosuffix --units k $lvname 2> /dev/null | sed -e 's/^ *//')
	if [ $lvsize != $newsize ]; then
		dry "$LVM" lvresize "$VERB" "$FORCE" -L "${newsize}k" "$lvname"
	else
		verbose "Logical volume already is of the size you're trying to resize it to"
	fi
}

resize() {
	if [ "$1" == "--help" ]; then
		resize_usage
		exit
	fi
	local size=
	devcount=

	# Special case for the situation we have been called from within the lvresize code.
	if [ "$RESIZE_FS_ONLY" ]; then
		resize_fs "$@"
		return
	fi

	for i in "$@"; do
		get_volume "$i"
		if [ -b "$VOLUME" ]; then
			if [ -z "$devcount" ]; then
				"$LVM" lvs "$i" &> /dev/null || error "$i is not valid logical volume"
				lvname=$i
				devcount=0
				continue
			fi
			devices="$devices $i"
			devcount=$(($devcount+1))
			continue
		fi
		case $i in
			"size="*)		[ -z "$size" ] && size=${i##*=};;
			[[:digit:]]*)		[ -z "$size" ] && size=${i##*=};;
			+[[:digit:]]*)		[ -z "$size" ] && size=${i##*=};;
			-[[:digit:]]*)		[ -z "$size" ] && size=${i##*=};;
			*) error "Wrong option $i. (see: $TOOL --help)";;
		esac
	done

	if [ -z "$size" ]; then
		error "Please provide the size argument. (see: $TOOL --help )"
	fi

	[ -z $lvname ] && error "Logical volume to resize was not specified"
	[ $devcount -gt 0 ] && [ "$shrink" ] && warn "While shrinking the file system we "\
						     "do not need additional devices. "\
						     "Those provided will be ignored"

	# We need to add provided devices into particular group
	if [ $devcount -gt 0 ] && [ -z $shrink ]; then
		# Get the volume group of the logical volume
		vgname=$(detect_volume_group $lvname)
		[ -z $vgname ] && error "Wrong argument $lvname. (see: $TOOL --help)"

		# Determine whether the devices are in the group
		detect_device_group $devices ||
			error "Devices are in different groups. Please run "\
				"\"$TOOL list\" to get more information on layout."

		[ "$GROUP" ] && [ "$GROUP" != "$vgname" ] && error "Some devices are in different"\
								   "group than the logical volume"\
								   "($lvname). Please provide unused"\
								   "devices"
		# Add missing devices into the group
		if [ "$NOT_IN_GROUP" ]; then
			dry "$LVM" vgextend "$VERB" $vgname $NOT_IN_GROUP
		fi
	fi

	resizefs=
	detect_fs $lvname
	[ "$FSTYPE" ] && resizefs="-r"

	# Avoid callinf lvresize in recurse
	if [ "$FSADM_RESIZE_RECURSE" ]; then
		error "Recursive lvresize call detected! Exiting."
	fi

	# We are going to call lvresize with -r parameter which will not only
	# resize the logical volume, but also the file system (if present)
	# by calling fsadm recursively with --resize-fs-only argument.
	# To be able to do this we should set some appropriate variables.
	export _FSADM_FORCE=$FORCE
	export FSADM_RESIZE_RECURSE=1
	unset FSADM_RUNNING

	dry exec "$LVM" lvresize $VERB $FORCE $resizefs -L${size} "$lvname"

}

#################################
# Check the device list to detect
# if there is not multiple groups
#################################
detect_device_group() {
	ret=0
	prev_vgname=""
	vgs=""
	tmp=$(mktemp)

	LANG=C "$LVM" vgs -o vg_name,pv_name --separator ' ' --noheadings --nosuffix 2> /dev/null | sed -e 's/^ *//' | sort | uniq > $tmp
	NOT_IN_GROUP=
	IN_GROUP=
	for i in "$@"; do
		cur_vgname=$("$GREP" -e "$i$" "$tmp" | cut -d' ' -f1)

		if [ -z "$cur_vgname" ]; then
			NOT_IN_GROUP+="$i "
			continue
		else
			IN_GROUP+="$i "
		fi

		if [ -z "$prev_vgname" ]; then
			prev_vgname=$cur_vgname
			vgs=$cur_vgname
			continue;
		fi

		if [ "$cur_vgname" != "$prev_vgname" ]; then
			ret=1
			vgs+=" $cur_vgname"
		fi
	done
	rm -f $tmp
	GROUP=$vgs
	return $ret
}

#############################
# Create a new logical volume
#############################
create() {
	devcount=0
	vg_create=0

	for i in "$@"; do
		if [ -b $i ]; then
			devices="$devices $i"
			devcount=$(($devcount+1))
			continue
		fi
		case $i in
			"stripesize"*) stripesize=${i##*=} ;;
			"name"*) name=${i##*=} ;;
			"fstyp"* | "fs"*) fstyp=${i##*=} ;;
			"size"*) size=${i##*=} ;;
			"stripes"*) stripes=${i##*=} ;;
			"--help") create_usage
				  exit
				  ;;
			*) if [ -z $vgname ]; then vgname=$i; else
				error "Wrong option $i. (see: $TOOL --help)"
			   fi ;;
		esac
	done

	detect_device_group $devices ||
		error "Devices are in different groups. Please run "\
			"\"$TOOL list\" to get more information on layout."

	if [ "$GROUP" ] && [ "$vgname" ];then
		if [ "$vgname" != "$GROUP" ]; then
			error "You specified group ($vgname) and devices which does "\
				"belong to a different group ($GROUP). Either remove "\
				"devices first, or specify devices which are free."
		fi
	elif [ "$GROUP" ]; then
		vgname=$GROUP
	fi

	if [ -z "$vgname" ]; then
		vgname=$DEFAULT_DEVICE_POOL
		"$LVM" vgs "$vgname" &> /dev/null || vg_create=1
	fi

	if [ "$stripesize" ] || [ "$stripes" ]; then
		if [ -z "$stripes" ] && [ $devcount -eq 0 ]; then
			error "Stripe size specified ($stripesize), but " \
			      "neither devices, nor number of stripes " \
			      "has been provided"
		fi
		[ $devcount -gt 0 ] && [ -z "$stripes" ] && stripes=$devcount
		if [ "$stripesize" ]; then
			striped="-i $stripes -I $stripesize"
		else
			striped="-i $stripes"
		fi
	fi

	if [ "$name" ]; then
		lvname="--name $name"
	else
		tmp=$(mktemp)
		"$LVM" lvs "$vgname" --separator ' ' --noheadings > $tmp
		for i in $(seq -w $MAX_VGS); do
			"$GREP" -e " ${LVOL_PREFIX}${i} " $tmp &> /dev/null && continue
			name="${LVOL_PREFIX}${i}"
			lvname="--name $name"
			break;
		done
		rm -f $tmp
	fi

	[ -z "$lvname" ] && error "No suitable name for the logical volume."

	if [ $vg_create -eq 1 ]; then
		dry "$LVM" vgcreate "$VERB" $vgname $devices
	elif [ ! -z "$NOT_IN_GROUP" ]; then
		dry "$LVM" vgextend "$VERB" $vgname $NOT_IN_GROUP
	fi


	if [ -z $size ] && [ -n "$devices" ]; then
		size="-l 100%PVS"
	elif [ -z $size ]; then
		size="-l 100%FREE"
	else
		size="-L $size"
	fi
	dry "$LVM" lvcreate "$VERB" $lvname $size $striped "$vgname" $devices
	device="/dev/${vgname}/${name}"

	guess=0
	if [ -z $fstyp ]; then
		fstyp=$(echo $TOOL | sed 's/^.*\.//g')
		guess=1
	fi

	case $fstyp in
		ext[234]) make_ext $device $fstyp;;
		xfs|reiserfs) generic_make_fs $device $fstyp;;
		*)	if [ $guess -eq 1 ]; then
				return 0
			else
				error "Filesystem $fstyp is not supported"
			fi
			;;
	esac
}

###############################
# Remove device form the group
###############################
remove_device() {
	vgname=$("$LVM" pvs "$1" -o vg_name --separator ' ' --noheadings) 2> /dev/null || return 1
	vgname=$(echo $vgname | sed 's/[ \t]*//')
	if [ "$vgname" ]; then
		dry_nofail "$LVM" vgreduce "$vgname" "$device"
	else
		warn "Device $1 is not in any pool."
	fi
	return 0
}

#############################
# Remove the logical volume
# volume group, or the device
# from the group
#############################
do_remove() {
	item="${1%% }"
	device=
	MOUNTED=

	get_volume "$item"
	# Block device has been given
	if [ -b "$VOLUME" ]; then
		device=$VOLUME
	fi

	# Mount point has been given
	if [ -z "$device" ] && [ -d "$item" ]; then
		mp=$(echo "$item" | sed -e 's/\/$//')

		count=$("$GREP" " $mp " "$PROCMOUNTS" | wc -l)

		if [ $count -eq 1 ]; then
			device=$("$GREP" " $mp " "$PROCMOUNTS" | cut -d' ' -f1)
		else
			count=$(LANG=C "$MOUNT" | "$GREP" " $mp " | wc -l)
			[ $count -ne 1 ] && warn "Could not find device mounted at $mp" && return
			device=$(LANG=C "$MOUNT" | "$GREP" " $mp " | cut -d' ' -f1)
		fi
		VOLUME=$device
	fi

	if [ -z "$device" ]; then
		"$LVM" vgs "$item" &> /dev/null
		# Volume group has been given
		if [ $? -eq 0 ]; then
			dry "$LVM" vgremove "$FORCE" "$item"
			return
		fi
	fi

	[ -z "$device" ] && warn "$item is not a valid mount point, dm device nor volume group name."&& return

	# Try to remove device from the group
	remove_device "$device" && return 0

	detect_mounted && try_umount
	dry "$LVM" lvremove "$FORCE" "$device"
	return
}

###############################
# Iterate through the arguments
# and do_remove on them
###############################
remove() {
	if [ $# -eq 0 ]; then
		error "Please specify what do you want to remove"\
		      "(see $TOOL remove --help)."
	fi
	# help
	if [ "$1" == "--help" ]; then
		remove_usage
		exit
	elif [ "$1" == "--all" ]; then
		list="$(LANG=C $LVM vgs -o vg_name --separator ' ' --noheadings --nosuffix --units k 2> /dev/null)"
	else
		list="$@"
	fi

	for item in $list; do
		do_remove "$item"
	done
}

#############################
# List all file systems built
# on top of DM device
#############################
list_filesystems() {
	local IFS=$NL
	local c=0
	for line in $(LANG=C "$LVM" lvs -o lv_path,lv_size,segtype --noheadings --separator ' ' --nosuffix --units k 2> /dev/null); do
		line=$(echo $line | sed -e 's/^ *\//\//')
		volume=$(echo $line | cut -d' ' -f1)
		[ "$volume" == "$last_volume" ] && continue
		c=$((c+1))
		local volumes[$c]=$volume
		local segtype[$c]=$(echo $line | cut -d' ' -f3)
		local lvsize[$c]=$(humanize_size $(echo $line | cut -d' ' -f2))
		detect_fs "$volume"
		detect_mounted
		detect_fs_size
		local total[$c]=$TOTAL
		local fstype[$c]=$FSTYPE
		local free[$c]=$FREE
		local used[$c]=$USED
		local mounted[$c]=$MOUNTED
		FSTYPE=
		FREE=
		USED=
		TOTAL=
		MOUNTED=
		last_volume=$volume
	done

	if [ $c -eq 0 ]; then
		warn " No file systems suitable for managing by $TOOL found."
		return
	fi

	len_volume=6
	len_fstype=2
	len_free=4
	len_used=4
	len_total=5
	len_mounted=11
	len_segtype=4
	len_lvsize=4
	for i in $(seq $c); do
		local _volume=${volumes[$i]}
		local _fstype=${fstype[$i]}
		local _total=${total[$i]}
		local _free=${free[$i]}
		local _used=${used[$i]}
		local _segtype=${segtype[$i]}
		local _lvsize=${lvsize[$i]}
		local _mounted=${mounted[$i]}
		[ ${#_volume} -gt "0$len_volume" ] && len_volume=${#_volume}
		[ ${#_fstype} -gt "0$len_fstype" ] && len_fstype=${#_fstype}
		[ ${#_total} -gt "0$len_total" ] && len_total=${#_total}
		[ ${#_free} -gt "0$len_free" ] && len_free=${#_free}
		[ ${#_used} -gt "0$len_used" ] && len_used=${#_used}
		[ ${#_segtype} -gt "0$len_segtype" ] && len_segtype=${#_segtype}
		[ ${#_lvsize} -gt "0$len_lvsize" ] && len_lvsize=${#_lvsize}
		[ ${#_mounted} -gt "0$len_mounted" ] && len_mounted=${#_mounted}
	done

	format="%-$(($len_volume+2))s %$(($len_lvsize))s  %-$(($len_fstype+2))s%$(($len_free))s  %$(($len_used))s  %$(($len_total))s  %-$(($len_segtype+2))s%-$(($len_mounted))s\n"
	header=$(printf "$format" "Volume" "LV size" "FS" "Free" "Used" "Total" "Type" "Mount point")
	separator=""
	for i in $(seq ${#header}); do
		separator+="-"
	done
	echo $separator
	printf "$format" "Volume" "LV size" "FS" "Free" "Used" "Total" "Type" "Mount point"
	echo $separator

	for i in $(seq $c); do
		printf "$format" "${volumes[$i]}" "${lvsize[$i]}" "${fstype[$i]}" "${free[$i]}" "${used[$i]}" "${total[$i]}" "${segtype[$i]}" "${mounted[$i]}"
	done

	echo $separator
}

###########################
# List all non DM devices
###########################
list_devices() {
	local IFS=$NL
	tmp=$(mktemp)

	c=0
	dmnumber=$(cat "$PROCDEVICES" | "$GREP" device-mapper | sed -e 's/^  *//')
	dmnumber=${dmnumber%% *}
	LANG=C "$LVM" pvs -o pv_name,vg_name,pv_size,pv_free,pv_used --separator ' ' --noheadings --nosuffix --units k > $tmp
	for line in $(cat "$PROCPARTITIONS" | tail -n +3 | sed -e 's/^  *//' | grep -v -e "^$dmnumber"); do
		c=$((c+1))
		line=$(echo $line | sed -e 's/  */ /g')
		_total=$(echo $line | cut -d' ' -f3)
		local total[$c]=$(humanize_size ${_total%%.*})
		VOLUME=$(echo $line | cut -d' ' -f4)
		RVOLUME="/dev/$(echo $line | cut -d' ' -f4)"
		line=$("$GREP" -e " $RVOLUME " $tmp)
		detect_mounted
		if [ -z $MOUNTED ]; then
			count=$("$GREP" "$VOLUME" "$PROCPARTITIONS" | wc -l)
			[ $count -gt 1 ] && MOUNTED="PARTITIONED"
		fi

		if [ ! -z $line ]; then
			line=$(echo $line | sed -e 's/^ *\//\//')
			local group[$c]=$(echo $line | cut -d' ' -f2)
			_total=$(echo $line | cut -d' ' -f3)
			local total[$c]=$(humanize_size ${_total%%.*})
			_free=$(echo $line | cut -d' ' -f4)
			local free[$c]=$(humanize_size ${_free%%.*})
			_used=$(echo $line | cut -d' ' -f5)
			local used[$c]=$(humanize_size ${_used%%.*})
		fi
		local volumes[$c]=$RVOLUME
		local mounted[$c]=$MOUNTED
		free=
		used=
		total=
		MOUNTED=
	done
	rm -f $tmp

	if [ $c -eq 0 ]; then
		warn " No devices found."
		return
	fi

	len_volume=6
	len_free=4
	len_used=4
	len_total=5
	len_group=5
	len_mounted=11
	for i in $(seq $c); do
		local _volume=${volumes[$i]}
		local _free=${free[$i]}
		local _used=${used[$i]}
		local _total=${total[$i]}
		local _group=${group[$i]}
		local _mounted=${mounted[$i]}
		[ ${#_volume} -gt "0$len_volume" ] && len_volume=${#_volume}
		[ ${#_free} -gt "0$len_free" ] && len_free=${#_free}
		[ ${#_used} -gt "0$len_used" ] && len_used=${#_used}
		[ ${#_total} -gt "0$len_total" ] && len_total=${#_total}
		[ ${#_group} -gt "0$len_group" ] && len_group=${#_group}
		[ ${#_mounted} -gt "0$len_mounted" ] && len_mounted=${#_mounted}
	done

	format="%-$(($len_volume+2))s%$(($len_free))s  %$(($len_used))s  %$(($len_total))s  %-$(($len_group+2))s%-$(($len_mounted))s\n"
	header=$(printf "$format" "Device" "Free" "Used" "Total" "Group" "Mount point")
	separator=""
	for i in $(seq ${#header}); do
		separator+="-"
	done
	echo $separator
	printf "$format" "Device" "Free" "Used" "Total" "Group" "Mount point"
	echo $separator

	for i in $(seq $c); do
		printf "$format" "${volumes[$i]}" "${free[$i]}" "${used[$i]}" "${total[$i]}" "${group[$i]}" "${mounted[$i]}"
	done

	echo $separator
}

################################
# List all pools (volume groups)
################################
list_pool() {
	local IFS=$NL
	c=0
	for line in $(LANG=C "$LVM" vgs -o vg_name,pv_count,vg_size,vg_free --separator ' ' --noheadings --nosuffix --units k 2> /dev/null); do
		c=$((c+1))
		line=$(echo $line | sed -e 's/^ *//')
		local group[$c]=$(echo $line | cut -d' ' -f1)
		local devices[$c]=$(echo $line | cut -d' ' -f2)
		_total=$(echo $line | cut -d' ' -f3)
		_free=$(echo $line | cut -d' ' -f4)
		_used=$((${_total%%.*}-${_free%%.*}))
		local used[$c]=$(humanize_size ${_used%%.*})
		local total[$c]=$(humanize_size ${_total%%.*})
		local free[$c]=$(humanize_size ${_free%%.*})
	done

	if [ $c -eq 0 ]; then
		warn " No pools found on the system."
		return
	fi

	len_group=5
	len_devices=6
	len_free=4
	len_used=4
	len_total=5
	for i in $(seq $c); do
		local _group=${group[$i]}
		local _devices=${devices[$i]}
		local _free=${free[$i]}
		local _used=${used[$i]}
		local _total=${total[$i]}
		[ ${#_group} -gt "0$len_group" ] && len_group=${#_group}
		[ ${#_devices} -gt "0$len_devices" ] && len_devices=${#_devices}
		[ ${#_free} -gt "0$len_free" ] && len_free=${#_free}
		[ ${#_used} -gt "0$len_used" ] && len_used=${#_used}
		[ ${#_total} -gt "0$len_total" ] && len_total=${#_total}
	done

	format="%-$(($len_group+2))s%-$(($len_devices+2))s%$(($len_free))s  %$(($len_used))s  %$(($len_total))s\n"
	header=$(printf "$format" "Group" "Devices" "Free" "Used" "Total")
	separator=""
	for i in $(seq ${#header}); do
		separator+="-"
	done
	echo $separator
	printf "$format" "Group" "Devices" "Free" "Used" "Total"
	echo $separator

	for i in $(seq $c); do
		printf "$format" "${group[$i]}" "${devices[$i]}" "${free[$i]}" "${used[$i]}" "${total[$i]}"
	done
	echo $separator
}

#############################
# List the available storage
# and file systems
#############################
list() {
	for i in "$@"; do
		case "$i" in
			"filesystems" | "fs") list_filesystems ;;
			"devices" | "dev") list_devices ;;
			"pool") list_pool ;;
			"--help") list_usage
				  exit
				  ;;
			*) error "Wrong option $i. (see: $TOOL --help)"
		esac
	done

	if [ $# -eq 0 ]; then
		list_devices
		echo ""
		list_pool
		echo ""
		list_filesystems
	fi
}

detect_volume_group() {
	_vg=$("$LVM" lvs -o vg_name --separator ' ' --noheadings $1 2> /dev/null)
	ret=$?
	echo "$_vg" | sed -e 's/^ *//'
	return $ret
}

############################
# Add devices into the group
############################
add() {
	if [ "$1" == "--help" ]; then
		add_usage
		exit
	fi
	vg_create=0
	devcount=0
	tmp=$(mktemp)

	for i in "$@"; do
		if [ -b "$i" ]; then
			devices="$devices $i"
			devcount=$(($devcount+1))
			continue
		elif [ -z "$vgname" ]; then vgname=$i; else
			error "Wrong argument $i. (see: $TOOL --help)"
		fi
	done

	[ $devcount -eq 0 ] && error "No suitable device specified."
	"$LVM" vgs -o vg_name --separator ' ' --noheadings | sed -e 's/^ *//' > $tmp

	if [ -z "$vgname" ]; then
		vgname=$DEFAULT_DEVICE_POOL
		"$GREP" "$vgname" $tmp &> /dev/null || vg_create=1
	else
		"$GREP" -e "$vgname" $tmp &> /dev/null
		vg_create=$?
	fi
	rm -f $tmp

	[ -z "$vgname" ] && error "No suitable name for volume group found."
	detect_device_group $devices

	if [ $vg_create -eq 1 ]; then
		dry "$LVM" vgcreate "$VERB" "$vgname" $devices
	elif [ ! -z "$NOT_IN_GROUP" ]; then
		dry "$LVM" vgextend "$VERB" "$vgname" $NOT_IN_GROUP
	else
		warn "Nothing to do"
	fi
}

####################################
# Calclulate diff between two dates
#  LANG=C input is expected the
#  only one supported
####################################
diff_dates() {
         echo $(( $("$DATE" -u -d"$1" +%s 2>"$NULL") - $("$DATE" -u -d"$2" +%s 2>"$NULL") ))
}

###################
# Check filesystem
###################
check() {
	if [ "$1" == "--help" ]; then
		check_usage
		exit
	fi
	detect_fs "$1" || error "Cannot get FSTYPE of \"$VOLUME\""
	verbose "\"$FSTYPE\" filesystem found on \"$VOLUME\""
	if detect_mounted ; then
		verbose "Skipping filesystem check for device \"$VOLUME\" as the filesystem is mounted on $MOUNTED";
		cleanup 3
	fi

	case "$FSTYPE" in
	  "ext2"|"ext3"|"ext4")
		local IFS=$NL
		for i in $(LANG=C "$TUNE_EXT" -l "$VOLUME"); do
			case "$i" in
			  "Last mount"*) LASTMOUNT=${i##*: } ;;
			  "Last checked"*) LASTCHECKED=${i##*: } ;;
			esac
		done
		case "$LASTMOUNT" in
		  *"n/a") ;; # nothing to do - system was not mounted yet
		  *)
			LASTDIFF=$(diff_dates $LASTMOUNT $LASTCHECKED)
			if test "$LASTDIFF" -gt 0 ; then
				verbose "Filesystem has not been checked after the last mount, using fsck -f"
				FORCE="-f"
			fi
			;;
		esac
	esac

	case "$FSTYPE" in
	  "xfs") dry "$XFS_CHECK" "$VOLUME" ;;
	  *)    # check if executed from interactive shell environment
		case "$-" in
		  *i*) dry "$FSCK" "$FORCE" "$VOLUME" ;;
		  *) dry "$FSCK" "$FORCE" -p "$VOLUME" ;;
		esac
	esac
}

set_default_config() {
	[ -z "$DEFAULT_DEVICE_POOL" ] && DEFAULT_DEVICE_POOL="device_pool"
	[ -z "$LVOL_PREFIX" ] && LVOL_PREFIX="lvol"
}

parse_config() {
	for line in $(cat "$1"); do
		case "$line" in
		  "DEFAULT_DEVICE_POOL"*) DEFAULT_DEVICE_POOL=${line##*=} ;;
		  "LVOL_PREFIX"*) LVOL_PREFIX=${line##*=} ;;
		  "#"*) continue ;;
		  *) error "Unknown value \"$line\" in configuration file"
		esac
	done
}

#############################
# start point of this script
# - parsing parameters
#############################
trap "cleanup 2" 2

# test if we are not invoked recursively
test -n "$FSADM_RUNNING" && exit 0

# test some prerequisities
test -n "$TUNE_EXT" -a -n "$RESIZE_EXT" -a -n "$TUNE_REISER" -a -n "$RESIZE_REISER" \
  -a -n "$TUNE_XFS" -a -n "$RESIZE_XFS" -a -n "$MOUNT" -a -n "$UMOUNT" -a -n "$MKDIR" \
  -a -n "$RMDIR" -a -n "$BLOCKDEV" -a -n "$BLKID" -a -n "$GREP" -a -n "$READLINK" \
  -a -n "$DATE" -a -n "$FSCK" -a -n "$XFS_CHECK" -a -n "$LVM" \
  || error "Required command definitions in the script are missing!"

"$LVM" version >"$NULL" 2>&1 || error "Could not run lvm binary \"$LVM\""
$("$READLINK" -e / >"$NULL" 2>&1) || READLINK_E="-f"
TEST64BIT=$(( 1000 * 1000000000000 ))
test "$TEST64BIT" -eq 1000000000000000 || error "Shell does not handle 64bit arithmetic"
$(echo Y | "$GREP" Y >"$NULL") || error "Grep does not work properly"
test $("$DATE" -u -d"Jan 01 00:00:01 1970" +%s) -eq 1 || error "Date translation does not work"


if [ "$#" -eq 0 ] ; then
	tool_usage
fi

while [ "$#" -ne 0 ]
do
	 case "$1" in
	  "") ;;
	  "-h"|"--help") tool_usage ;;
	  "-v"|"--verbose") VERB="-v" ;;
	  "-n"|"--dry-run") DRY=1 ;;
	  "-f"|"--force") FORCE="-f" ;;
	  "-e"|"--ext-offline") EXTOFF=1 ;;
	  "-o"|"--resize-fs-only") RESIZE_FS_ONLY=1 ;;
	  "-l"|"--lvresize") ;;
	  "-c"|"--config") CONFIG=$2; shift 1 ;;
	  "check") COMMAND=$1; shift; ARGS="$@"; break ;;
	  "resize") COMMAND=$1; shift; ARGS="$@"; break ;;
	  "create") COMMAND=$1; shift; ARGS="$@"; break ;;
	  "list") COMMAND=$1; shift; ARGS="$@"; break ;;
	  "add") COMMAND=$1; shift; ARGS="$@"; break ;;
	  "remove") COMMAND=$1; shift; ARGS="$@"; break ;;
	  *) error "Wrong argument \"$1\". (see: $TOOL --help)"
	esac
	shift
done

if [ -z "$COMMAND" ]; then
	error "Missing command. (see: $TOOL --help)"
fi

set_default_config
if [ "$CONFIG" ]; then
	[ ! -f "$CONFIG" ] && error "\"$CONFIG\" is not proper configuration file"
	CONFIG_PATHS+=" $CONFIG"
fi
for i in "$CONFIG_PATHS"; do
	[ ! -f "$i" ] && continue
	parse_config "$i"
done

export FSADM_RUNNING="fsadm"
$COMMAND $ARGS
cleanup 0
