#!/bin/bash
#
# Univention Quota
#  set quota by group membership
#
# SPDX-FileCopyrightText: 2004-2025 Univention GmbH
# SPDX-License-Identifier: AGPL-3.0-only

set -o errexit
set -o pipefail

verbose () {
	if [ "$VERBOSE" -eq 1 ]
	then
		echo "${@}" 1>&2
	fi
}

getUsersInGroupNested () { # GROUP
	local GROUP="$1"
	getent group "$GROUP"|cut -d':' -f4|tr ',' '\n'
}

getUsersInGroupFlat () { # GROUP
	local GROUP="$1"
	if [ -z "$ldap_hostdn" ]; then
		ldap_hostdn=$(univention-config-registry get ldap/hostdn)
	fi
	ldapsearch -xLLL -ZZ -D "$ldap_hostdn" -y /etc/machine.secret "(&(cn=$GROUP)(objectClass=univentionGroup))" uniqueMember | \
		ldapsearch-wrapper | \
		ldapsearch-decode64 | \
		grep '^uniqueMember: uid=' | \
		cut -d' ' -f2- | \
		sed -r 's/uid=([^,]+),.*$/\1/'
}

getUsersInGroup () { # GROUP FLATMODE
	local GROUP="$1"
	local FLATMODE="$2"
	if [ "$FLATMODE" -eq 1 ]
	then
		verbose "Searching for users directly in $GROUP"
		getUsersInGroupFlat "$GROUP"
	else
		verbose "Searching for users in $GROUP or subgroups"
		getUsersInGroupNested "$GROUP"
	fi
}

mountpoint2filesystem () { # MOUNTPOINT
	local MOUNTPOINT="$1"
	tr -s '\t' ' ' < /proc/mounts |cut -d' ' -f1,2|grep " $MOUNTPOINT$"|cut -d' ' -f1|tail -n1
}

filesystem2mountpoint () { # DEVICE
	local FILESYSTEM="$1"
	tr -s '\t' ' ' < /proc/mounts |cut -d' ' -f1,2|grep "^$FILESYSTEM "|cut -d' ' -f2|tail -n1
}

setQuotaForUserOnFilesystem () { # USER FILESYSTEM BSL BHL
	local USER="$1" FILESYSTEM="$2" BSL="$3" BHL="$4" ISL IHL
	ISL="$(quota -wulv "$USER"|tr -s '\t' ' '|grep "^ *$FILESYSTEM "|cut -d' ' -f6)"
	IHL="$(quota -wulv "$USER"|tr -s '\t' ' '|grep "^ *$FILESYSTEM "|cut -d' ' -f7)"
	verbose "Setting quota $BSL/$BHL ($ISL/$IHL) for $USER on $FILESYSTEM"
	setquota --always-resolve -u "$USER" "$BSL" "$BHL" "$ISL" "$IHL" "$FILESYSTEM"
}

userHasQuotaOnFilesystem () { # USER FILESYSTEM
	local USER="$1"
	local FILESYSTEM="$2"
	quota --always-resolve -wul "$USER"|tr -s '\t' ' '|grep -qs "^ *$FILESYSTEM "
}

usage () {
	echo "$0: set usrquota on mountpoint for every user in group"
	echo ""
	echo "USAGE:"
	echo "	$0 [OPTIONS]"
	echo ""
	echo "OPTIONS:"
	echo "	-g GROUP"
	echo "	   the group for whose users the quota will be set"
	echo ""
	echo "	-s SOFTLIMIT"
	echo "	   soft limit in KiB"
	echo ""
	echo "	-h HARDLIMIT"
	echo "	   hard limit in KiB"
	echo ""
	echo "	-m MOUNTPOINT"
	echo "	   which filesystem to set the quota for (must be mounted)"
	echo ""
	echo "	-d DEVICE"
	echo "	   which filesystem to set the quota for (must be mounted)"
	echo ""
	echo "	-f"
	echo "	   force: set quota even if it is already set"
	echo ""
	echo "	-n"
	echo "	   nested groups: process users in subgroups too"
	echo ""
	echo "	-v"
	echo "	   verbose: print debug output"
	echo ""
	echo "	Required options are: -g -s -h -m -d"
	echo "	You can either supply -m or -d"
}

FLATMODE=1
FORCE=0
VERBOSE=0
while getopts 'g:s:h:m:d:fnv' OPTION
do
	case "$OPTION" in
		'g' )
			GROUP="$OPTARG"
			;;
		's' )
			BSL="$OPTARG"
			;;
		'h' )
			BHL="$OPTARG"
			;;
		'm' )
			if mountpoint2filesystem "$OPTARG" > /dev/zero
			then
				FILESYSTEM="$(mountpoint2filesystem "$OPTARG")"
				verbose "Mount point $OPTARG is $FILESYSTEM"
			else
				echo "Invalid mount point $OPTARG!"
				exit 1
			fi
			;;
		'd' )
			if filesystem2mountpoint "$OPTARG" > /dev/zero
			then
				FILESYSTEM="$OPTARG"
			else
				echo "Invalid file system $OPTARG!"
				exit 1
			fi
			;;
		'f' )
			FORCE=1
			;;
		'n' )
			FLATMODE=0
			;;
		'v' )
			VERBOSE=1
			;;
	esac
done

if [ -z "${GROUP+set}" ]
then
	echo "Missing GROUP!"
	usage
	exit 3
fi
if [ -z "${BSL+set}" ]
then
	echo "Missing SOFTLIMIT!"
	usage
	exit 3
fi
if [ -z "${BHL+set}" ]
then
	echo "Missing HARDLIMIT!"
	usage
	exit 3
fi
if [ -z "${FILESYSTEM+set}" ]
then
	echo "Missing MOUNTPOINT/DEVICE!"
	usage
	exit 3
fi

verbose "Using group $GROUP"
verbose "Using filesystem $FILESYSTEM"
for USER in $(getUsersInGroup "$GROUP" "$FLATMODE")
do
	if [ "$FORCE" -eq 0 ]
	then
		if userHasQuotaOnFilesystem "$USER" "$FILESYSTEM"
		then
			verbose "Skipping $USER"
			continue
		fi
	fi
	if [ "$VERBOSE" -eq 1 ]
	then
		if userHasQuotaOnFilesystem "$USER" "$FILESYSTEM"
		then
			verbose "Forcing $USER"
		fi
	fi
	setQuotaForUserOnFilesystem "$USER" "$FILESYSTEM" "$BSL" "$BHL"
done
verbose "Everything OK"
