#!/bin/sh

set -e

# Source debconf library:
. /usr/share/debconf/confmodule

# Source bilibop library:
. /lib/bilibop/common.sh
get_udev_root

PATH="${PATH}:/usr/share/bilibop"
ETC_RULES_DIR="/etc/udev/rules.d"

belongs_to_floppy_group() {
    stat -c %G ${1}* | grep -q '^floppy$'
}

global_filter_is_supported() {
    local version="$(dpkg -l lvm2 | awk '/^ii/ {print $3}')"
    dpkg --compare-versions ${version} ge 2.02.98
}

case "${1}" in
    configure|reconfigure)

        # GRUB device.map management
        # ==========================

        # Backup device.map; it will be restored when (if) the package will be
        # purged.
        DEVICE_MAP="/boot/grub/device.map"
        if [ ! -f "${DEVICE_MAP}.bak" -a -s "${DEVICE_MAP}" -a ! -h "${DEVICE_MAP}" ]; then
            cat ${DEVICE_MAP} >${DEVICE_MAP}.bak
        fi

        # Debconf specific configuration
        # ==============================

        # At first, backup some values if this is not already done.
        HELPER="physical_volumes_filter"
        LVMCONF="/etc/lvm/lvm.conf"
        lvm_variables="obtain_device_list_from_udev filter"
        global_filter_is_supported && lvm_variables="${lvm_variables} global_filter"
        if [ -f "${LVMCONF}" ]; then
            for lvmvar in ${lvm_variables}; do
                db_fget bilibop-rules/${HELPER}/${lvmvar} seen
                [ "${RET}" = "true" ] && continue
                lvmval="$(grep "^[[:blank:]]*${lvmvar}[[:blank:]]*=" ${LVMCONF} | sed 's,^[^=]\+=\s*,,')"
                db_set bilibop-rules/${HELPER}/${lvmvar} ${lvmval}
                db_fset bilibop-rules/${HELPER}/${lvmvar} seen true
                # Now the initial values of 'obtain_device_list_from_udev',
                # 'filter' and 'global_filter' are stored in the debconf
                # db. They will be restored if the package is purged.
            done
        fi

        # Modify system settings only if the package is not intended to be
        # installed on a Live System.
        db_get bilibop-rules/on-live-system
        if [ "${RET}" = "false" ]; then

            # make_unpersistent_rules
            # -----------------------

            # Replace persistent rules files by symlinks in a per-file basis
            # (this can be useful if the external system travels with a PCMCIA
            # ethernet card or an external CD/DVD burner)
            HELPER="make_unpersistent_rules"
            db_get bilibop-rules/${HELPER}
            case "${RET}" in
                keep)
                    ;;
                cd|net)
                    ${HELPER} --only ${RET}
                    ;;
                none)
                    ${HELPER} --restore
                    ;;
                all)
                    ${HELPER}
                    ;;
            esac

            # grub_device_map_manager
            # -----------------------

            # Set GRUB device map only if GRUB is installed (both as package
            # and as bootloader):
            # This is for MBR/ms-dos partition tables:
            #GRUB="$(dd if=${BILIBOP_DISK} bs=1 skip=384 count=4 2>/dev/null)"
            HELPER="grub_device_map_manager"
            if [ -d "/boot/grub" -a -x "/usr/sbin/grub-mkdevicemap" ]; then
                db_get bilibop-rules/${HELPER}
                if [ "${RET}" != "keep" ]; then
                    CONFIGFILE="/etc/bilibop/bilibop.conf"
                    KEY="BILIBOP_RULES_FAKE_DEVICE_MAP"
                    case "${RET}" in
                        static)
                            opt="--update"
                            if [ -h "${DEVICE_MAP}" ]; then
                                opt="--remove ${opt}"
                            fi
                            ;;
                        fake)
                            opt="--fake"
                            if [ -h "${DEVICE_MAP}" ]; then
                                opt="--remove ${opt}"
                            fi
                            ;;
                        dynamic)
                            opt="--link"
                            if [ -f "${CONFIGFILE}" ]; then
                                # Override the variable ?
                                #echo "${KEY}=\"true\"" >>${CONFIGFILE}
                                # Change the value ?
                                #sed -i "s:^\(\s*${KEY}\)=\([\"\']\?\)false\2:\1=\2true\2:" ${CONFIGFILE}
                                # Comment the line ?
                                #sed -i "s:^\(\s*${KEY}\)=\([\"\']\?\)false\2:#&:" ${CONFIGFILE}
                                # Delete the line ?
                                sed -ri "/^(\s*${KEY})=([\"\']?)false\2/d" ${CONFIGFILE}
                            fi
                            ;;
                        ondemand)
                            opt="--link --update"
                            if grep -qs "^[[:blank:]]*${KEY}=" ${CONFIGFILE}; then
                                grep -Eq "^[[:blank:]]*${KEY}=([\"\']?)false\1" ${CONFIGFILE} ||
                                sed -ri "s:^(\s*${KEY})=.*:\1=\"false\":" ${CONFIGFILE}
                            elif grep -Eqs "^[[:blank:]]*(#[[:blank:]]*)+${KEY}=([\"\']?)false\2" ${CONFIGFILE}; then
                                sed -ri "s:^(\s*)(#\s*)+(${KEY})=([\"\']?)false\4:\1\3=\"false\":" ${CONFIGFILE}
                            else
                                echo "${KEY}=\"false\"" >>${CONFIGFILE}
                            fi
                            ;;
                    esac
                    ${HELPER} ${opt}
                fi
            fi
        fi

        # Now we have to take care of the order of the processes:
        # 1. Ask the user if she wants to use a custom rules file
        # 2. Trigger uevents to apply bilibop (custom or generic)
        #    udev rules
        # 3. Then ask the user if she wants to modify lvm.conf
        #    (needs BILIBOP udev-tagged devices)
        # 4. Update initramfs by including the (modified or not)
        #    lvm.conf

        if [ -h /proc/mounts -a -d /sys/block -a -c ${udev_root}/null ] &&
            invoke-rc.d udev status >${udev_root}/null 2>&1 &&
            BILIBOP_DISK="$(physical_hard_disk /)" &&
            query_sysfs_attrs ${BILIBOP_DISK} | grep -Eq '^[[:blank:]]*SUBSYSTEMS=="(usb|firewire|memstick|mmc)"'; then
            db_get bilibop-rules/on-live-system
            if [ "${RET}" = "false" ]; then

                # bilibop_rules_generator
                # -----------------------

                # Maybe build custom rules
                CUSTOM_RULES="false"
                HELPER="bilibop_rules_generator"
                if [ -f ${ETC_RULES_DIR}/66-bilibop.rules ]; then
                    db_get bilibop-rules/${HELPER}/overwrite
                    if [ "${RET}" = "rebuild" ]; then
                        CUSTOM_RULES="true"
                    elif [ "${RET}" = "remove" ]; then
                        rm ${ETC_RULES_DIR}/66-bilibop.rules
                        # Now that the custom rules file has been removed,
                        # say to debconf the user does not want to build it.
                        db_reset bilibop-rules/${HELPER}/customize
                        db_fset bilibop-rules/${HELPER}/customize seen true
                        # But also say to keep it if it is rebuilt manually.
                        # We don't set the seen flag to true: if the custom
                        # rules file has been built manually, the question
                        # should be asked on next package upgrade.
                        db_reset bilibop-rules/${HELPER}/overwrite
                    fi
                else
                    db_get bilibop-rules/${HELPER}/customize
                    if [ "${RET}" = "true" ]; then
                        CUSTOM_RULES="true"
                    fi
                fi

                # Build the custom rules:
                if [ "${CUSTOM_RULES}" = "true" ]; then
                    db_get bilibop-rules/${HELPER}/options
                    if ${HELPER} ${RET} -t ${BILIBOP_DISK} 2>${udev_root}/null; then
                        # Custom rules file has been successfully created;
                        # so say debconf to keep it the next time the package
                        # is configured:
                        db_reset bilibop-rules/${HELPER}/overwrite
                        db_fset bilibop-rules/${HELPER}/overwrite seen true
                    else
                        CUSTOM_RULES="false"
                    fi
                fi
            fi

            # Udev specific configuration
            # ===========================

            # Trigger uevents for block devices owned by 'disk' group
            # or being on the same disk than the root filesystem.
            BILIBOP_LIST="$(lsbilibop -l)"
            opt="--sysname-match=${BILIBOP_DISK##*/}*"
            for dev in ${BILIBOP_LIST}; do
                case "${dev}" in
                    ${BILIBOP_DISK}*)
                        ;;
                    *)
                        opt="${opt} --sysname-match=${dev##*/}"
                        ;;
                esac
            done
            udevadm trigger ${opt}
            udevadm settle

            # But it can happen that this doesn't work and new rules must be
            # explicitly loaded before triggering uevents:
            if belongs_to_floppy_group ${BILIBOP_DISK}; then
                udevadm control --reload-rules
                udevadm trigger ${opt}
                udevadm settle

                # Now do something if the drive and its partitions still belong
                # to the floppy group.
                if belongs_to_floppy_group ${BILIBOP_DISK}; then
                    for dev in $(find ${BILIBOP_DISK}* -group floppy); do
                        flop="${flop:+${flop} }${dev}"
                    done
                    if [ -f "${ETC_RULES_DIR}/66-bilibop.rules" -a "${CUSTOM_RULES}" != "true" ]; then
                        # Custom rules file exists, but it has not been created
                        # just before:
                        db_subst bilibop-rules/belongs_to_floppy_group/custom_rules_error DEVICE "${flop}"
                        db_input critical bilibop-rules/belongs_to_floppy_group/custom_rules_error || true
                        db_go || true
                    else
                        # Custom rules file does not exist, or it has just been
                        # created:
                        db_subst bilibop-rules/belongs_to_floppy_group/internal_error DEVICE "${flop}"
                        db_input critical bilibop-rules/belongs_to_floppy_group/internal_error || true
                        db_go || true
                    fi
                fi
            fi

            # Debconf stuff... again (because we need updated udev tags to do it)
            # ======================
            db_get bilibop-rules/on-live-system
            if [ "${RET}" = "false" ]; then

                # physical_volumes_filter
                # -----------------------

                HELPER="physical_volumes_filter"
                # The file copied into the initrd is always /etc/lvm/lvm.conf
                # (the hook does not care about LVM_SYSTEM_DIR); so we reset
                # LVM_SYSTEM_DIR to be sure the helper script will apply to
                # this file.
                export LVM_SYSTEM_DIR="/etc/lvm"
                LVMCONF_RECONFIGURE="0"

                if lsblk ${BILIBOP_DISK} --noheadings -o type,fstype | grep -Eq '\<(lvm|LVM2_member)\>'; then

                    OLDPVFILTER="$(${HELPER})" || true
                    db_get bilibop-rules/${HELPER}/system-only

                    if [ "${RET}" = "true" ]; then
                        unlinked=
                        untagged=
                        PV_LIST="$(lsblk --noheadings -o fstype,kname ${BILIBOP_DISK} | awk '/^LVM2_member/ {print $2}')"
                        for dev in ${PV_LIST}; do
                            linked="false"
                            for symlink in $(udevadm info --query symlink --name ${dev}); do
                                case "${symlink}" in
                                    disk/by-id/lvm-pv-uuid-*)
                                        ;;
                                    disk/by-id/*|mapper/*)
                                        linked="true"
                                        break
                                        ;;
                                esac
                            done
                            [ "${linked}" = "true" ] ||
                                unlinked="${unlinked:+${unlinked} }${udev_root}/${dev}"
                            [ "$(lsbilibop ${udev_root}/${dev})" = "${udev_root}/${dev}" ] ||
                                untagged="${untagged:+${untagged} }${udev_root}/${dev}"
                        done
                    fi

                    if [ "${RET}" = "true" -a -z "${untagged}" -a -z "${unlinked}" ]; then
                        if ! ${HELPER} --udev --accept bilibop --reject all >${udev_root}/null 2>&1; then
                            ${HELPER} --init
                        fi
                        ${HELPER} --overwrite --udev --accept bilibop --reject all

                    else
                        if [ "${RET}" = "true" ]; then
                            db_reset bilibop-rules/${HELPER}/system-only

                            if global_filter_is_supported; then
                                warning="with_global_filter/warning"
                                db_get bilibop-rules/${HELPER}/global_filter
                                db_subst bilibop-rules/${HELPER}/${warning} GLOBAL_FILTER "${RET}"
                            else
                                warning="without_global_filter/warning"
                            fi
                            db_subst bilibop-rules/${HELPER}/${warning} UNTAGGED "${untagged}"
                            db_subst bilibop-rules/${HELPER}/${warning} UNLINKED "${unlinked}"
                            db_get bilibop-rules/${HELPER}/obtain_device_list_from_udev
                            db_subst bilibop-rules/${HELPER}/${warning} FROMUDEV "${RET}"
                            db_get bilibop-rules/${HELPER}/filter
                            db_subst bilibop-rules/${HELPER}/${warning} FILTER "${RET}"
                            db_input critical bilibop-rules/${HELPER}/${warning} || true
                            db_go || true
                        fi
                        for lvmvar in ${lvm_variables}; do
                            db_fget bilibop-rules/${HELPER}/${lvmvar} seen
                            [ "${RET}" = "true" ] || continue
                            db_get bilibop-rules/${HELPER}/${lvmvar}
                            if [ -z "${RET}" ]; then
                                sed -i "/^\s*${lvmvar}\s*=/d" ${LVMCONF}
                            elif grep -q "^[[:blank:]]*${lvmvar}[[:blank:]]*=" ${LVMCONF}; then
                                sed -ri "s@^(\s*${lvmvar}\s*=).*@\1 ${RET}@" ${LVMCONF}
                            else
                                sed -ri "s@^\s*devices\s*\{.*@&\n    ${lvmvar} = ${RET}@" ${LVMCONF}
                            fi
                        done
                    fi

                    NEWPVFILTER="$(${HELPER})" || true
                    if [ "${NEWPVFILTER}" != "${OLDPVFILTER}" ]; then
                        LVMCONF_RECONFIGURE="1"
                        # Regenerate cache file:
                        vgscan --ignorelockingfailure
                    fi
                fi
            fi
        fi
        db_stop

        # Add a new script in the initramfs. And modify it also if LVM has
        # been reconfigured (see above):
        if which update-initramfs >/dev/null; then
            if [ "${DEBCONF_RECONFIGURE}" = "1" ]; then
                if [ "${LVMCONF_RECONFIGURE}" = "1" ]; then
                    update-initramfs -u
                fi
            else
                update-initramfs -u
            fi
        fi
        ;;
esac

#DEBHELPER#
:
# vim: et ts=4 sts=4 sw=4
