#!/bin/bash
#
#  Copyright (C) 2002 Dell Computer Corporation <gary_lerhaupt@dell.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    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
#
#  Devlabel by Gary Lerhaupt <gary_lerhaupt@dell.com>

function readlink()
{
    # $1 = the symlink to read
    read_link=""
    if [ -L "$1" ]; then
	read_link="$1"
	while [ -L "$read_link" ]; do
	    read_link=`ls -l $read_link | sed 's/.*-> //'`
	done
    fi
}

function show_usage ()
{
    echo $"Usage: $0 [action] [options]" >&2
    echo $"      [action]  = { reload | status | add | remove | printid | reverseremap }" >&2
    echo $"      [options] = [-d device] [-s symlink] [-u UUID] [-v] [-q]" >&2
    echo $"                = [--automount] [--multipath] [--uid] [--gid] [--perms]" >&2
}

function parse_config_data()
{
    # This functions takes in 1 line of input from $CONFIG_FILE and parses it into the
    # proper format. It is able to tell if something is a UNIQUE_ID by the presence of a ":".
    # If the line is blank or is a comment, then $IGNORE_LINE is set.
    # $1 = 1 full line of input from the rawdevices file.
   
    SYMLINK=""
    DEVICE=""
    UNIQUE_ID=""
    IGNORE_LINE=""
    SYM_TYPE=""
    OPTIONS=""
    AUTOMOUNT=""
    MULTIPATH=""
    D_UID=""
    D_GID=""
    PERMS=""
    
    # Get rid of carriage returns, if necessary
    line_arg=`echo "$1" | awk '{gsub(/\r/, ""); print}'`

    # Initially test if it is a comment or a blank line
    if [ -z "$line_arg" ] || [ `echo "$line_arg" | grep -c "^ *#"` -gt 0 ]; then
	IGNORE_LINE=1
    else
	set -- $line_arg
	SYMLINK="$1"
	DEVICE="$2"
	UNIQUE_ID="$3"
	OPTIONS="$4"

	# Figure out options
	if [ -n "$OPTIONS" ]; then
	    for config_option in `echo "$OPTIONS" | sed 's/,/ /g'`; do
		config_option_type=`echo "$config_option" | awk -F '=' '{ print tolower($1) }'`
		config_option_value=`echo "$config_option" | sed 's/.*=//'`
		case $config_option_type in
		    automount)
			AUTOMOUNT="automount"
			;;
		    multipath)
			MULTIPATH="multipath"
			;;
		    uid)
			D_UID=$config_option_value
			;;
		    gid)
			D_GID=$config_option_value
			;;
		    perms)
			PERMS=$config_option_value
			;;		    
		esac
	    done
	fi

	# Figure out symtype
	if [ `echo "$SYMLINK" | grep -c "/dev/raw/raw"` -gt 0 ]; then
	    SYM_TYPE="rawdevice"
	elif [ -n "$MULTIPATH" ]; then
	    SYM_TYPE="multipath"
	else
	    SYM_TYPE="symlink"
	fi
	
	if [ -z "$SYMLINK" ] || [ -z "$DEVICE" ] || [ -z "$UNIQUE_ID" ]; then
	    echo $"" >&2
	    echo $"Ignoring bad entry: $1." >&2
	    echo $"Expecting entry to have at least 3 parameters: <SYMLINK> <DEVICE> <UUID>" >&2
	    IGNORE_LINE=1
	fi
    fi
}

function ide_id()
{
    # sets ID for an ide device
    shift 10
    ID="I:"
    for i in 1 2 3 4 5 6 7 8 9 10 ; do
	ID="${ID}$1"
	shift
    done
}


function get_id()
{
    # This function determines the ID that is returned for the DEVICE
    # $1 = device
    # Returns $ID
    
    # Set variables
    ID=""
    model=""
    identifier_type=""
    dev_prefix=`echo "$1" | sed 's/.*\([hs]d[a-z]\+\).*/\1/'`
    dev_suffix=`echo "$1" | sed 's/.*[hs]d[a-z]\+//'`

    if [ `grep -c "$1$" $IGNORE_LIST` -gt 0 ]; then
	return 1
    fi

    if [ -n "$dev_suffix" ] && [ `echo "${sfdisk_outputs[*]}" | grep -c "/dev/$dev_prefix=bad"` -gt 0 ]; then
	return 1
    fi

    # Set the ID
    if [ -n "$dev_suffix" ] && ID=`partition_uuid $1 2>/dev/null` && [ -n "$ID" ]; then 
	identifier_type="partition"
	ID="P:$ID"
    elif scsi_unique_id=`scsi_unique_id "$1" 2>/dev/null` && [ -n "$scsi_unique_id" ]; then 
	identifier_type="scsi"
        model=`echo "$scsi_unique_id" | grep "model:" | sed -e 's/model://' -e 's/ //g'`
	if ID=`echo "$scsi_unique_id" | grep "page83 type3:" | sed 's/page83 type3: //' | while read foo ; do echo -n $foo ; done` && [ -n "$ID" ]; then
		ID="S83.3:$ID"
	elif ID=`echo "$scsi_unique_id" | grep "page83 type2:" | sed 's/page83 type2: //' | while read foo ; do echo -n $foo ; done` && [ -n "$ID" ]; then
		ID="S83.2:$ID"
        elif ID=`echo "$scsi_unique_id" | grep "page83 type1:" | sed 's/page83 type1: //' | while read foo ; do echo -n $foo ; done` && [ -n "$ID" ]; then
		ID="S83.1:$ID"
        elif ID=`echo "$scsi_unique_id" | grep "page80:" | grep -v "No page80" | sed 's/page80: //' | while read foo ; do echo -n $foo ; done` && [ -n "$ID" ]; then
		ID="S80:$ID"
        elif ID=`echo "$scsi_unique_id" | grep "page83 type0:" | sed 's/page83 type0: //' | while read foo ; do echo -n $foo ; done` && [ -n "$ID" ]; then
		ID="S83.0:$ID"
	else
		ID="S:"
	fi
    elif [ -d /proc/ide/$dev_prefix ] && [ `echo "$proc_partitions" | awk '{print $4}' | grep -c "^$dev_prefix$dev_suffix$"` -eq 1 ]; then
	identifier_type="ide"
	model="`cat /proc/ide/$dev_prefix/model 2>/dev/null | sed 's/ //g'`"
	ide_id `cat /proc/ide/$dev_prefix/identify 2>/dev/null`
    fi

    # Get the start sector for usage with non partition uuids
    # Hack around differing partx format whether partition -lt 10 or -gt 9
    start_sector=""
    if [ -n "$dev_suffix" ]; then
	[ "$dev_suffix" -lt 10 ] && start_sector=sector`partx /dev/$dev_prefix 2>/dev/null | grep "^# $dev_suffix:" | awk '{print $3}' | sed 's/-$//'` || start_sector=sector`partx /dev/$dev_prefix 2>/dev/null | grep "^#$dev_suffix:" | awk '{print $2}' | sed 's/-$//'`
    fi
  
    # If the identifier is scsi or ide, add the model to the end
    if [ "$identifier_type" == "scsi" ] || [ "$identifier_type" == "ide" ]; then
	ID="$ID$model$start_sector"
    fi	
    
    # If No ID, then add it to the ignore_list
    if [ -z "$ID" ]; then
	echo "$1" >> $IGNORE_LIST
	[ -n "`type -p logger`" ] && logger -t devlabel -p syslog.warning "The device $1 is being put in devlabel's temporary ignore list $IGNORE_LIST to avoid errors."
    fi

    # Get rid of nasty characters in the ID
    ID=`echo $ID | sed 's/[[\/=^*$]/_/'`
}

function rotate_old_files()
{
    for old_file_number in 8 7 6 5 4 3 2 1 0; do
	new_file_number=$(($old_file_number + 1))
	mv -f $CONFIG_FILE.$old_file_number $CONFIG_FILE.$new_file_number 2>/dev/null
    done
    mv -f $CONFIG_FILE.old $CONFIG_FILE.0 2>/dev/null
}

function check_uniqueness()
{
    # This function checks a given device ID against the device_mappings array to determine if
    # it is a unique ID or not
    # $1 = the UUID to check against device_mappings
    # $2 = the SYM_TYPE being checked {symlink, rawdevice, multipath}

    IFS='
'

    if [ `echo "${device_mappings[*]}" |  grep -c "$1$"` -eq 0 ]; then
	# Cannot find any devices with this ID
	unset IFS
	return 1
    fi
    if [ `echo "${device_mappings[*]}" | grep -c "$1$"` -ne 1 ] && [ "$2" != "multipath" ]; then
	echo $"" >&2
	echo $"Uniqueness check failed.  The following devices have the same UUID:" >&2
	echo "${device_mappings[*]}" | grep $1 | sed 's/=.*//' >&2
	unset IFS
	return 2
    fi
    unset IFS
    return 0
}

function print_id()
{
    # Check that we have all the arguments
    if [ -z "$cli_device" ]; then
	echo $"" >&2
	echo $"Error! Invalid number of arguments passed." >&2
	echo $"Usage: printid [-d device]" >&2
	exit 1
    fi
    
    get_id $cli_device
    if [ -n "$ID" ]; then
	echo $ID
    else
	exit 1
    fi
}

function make_link()
{
    # This function does the actual linking of symlink to device
    # globals used: $SYM_TYPE, $SYMLINK, $DEVICE, $AUTOMOUNT, $D_UID, $D_GID, $PERMS, $UNIQUE_ID (multipath)

    if [ "$SYM_TYPE" == "symlink" ]; then
	echo $"SYMLINK: $SYMLINK -> $DEVICE"
	ln -fs $DEVICE $SYMLINK
	
	# Automount the device if AUTOMOUNT is set and its in /etc/fstab
	mountpoint=`grep "^$SYMLINK\b" /etc/fstab | awk '{print $2 ; nextfile}'`
	if [ -n "$AUTOMOUNT" ] && [ -n "$mountpoint" ] && [ `mount | grep -c "^$SYMLINK\b"` -eq 0 ]; then
	    mount $mountpoint
	fi
    elif [ "$SYM_TYPE" == "rawdevice" ]; then
	echo $"RAW: $SYMLINK -> $DEVICE"
	raw $SYMLINK $DEVICE 1>/dev/null
    elif [ "$SYM_TYPE" == "multipath" ]; then
	device_suffix=`echo $DEVICE | sed 's/.*[hs]d[a-z]\+\([0-9]*\)/\1/'`
	device_genericified=`echo $DEVICE | sed 's/\([hs]d\)[a-z]\+/\1#/'`
	echo $"MULTIPATH: ${SYMLINK}_multipath# -> $device_genericified"

	# Delete all multipath symlinks first
	for sym_to_delete in `ls ${SYMLINK}_multipath[0-9]* 2>/dev/null`; do
	    if [ -h "$sym_to_delete" ]; then
		rm -f "$sym_to_delete"
	    fi
	done

	# Create the new multipath symlinks
	IFS='
'
	count=0
	for multipath_dev in `echo "${device_mappings[*]}" | grep "[a-z]$device_suffix=$UNIQUE_ID" | sed 's/=.*//'`; do
	    multipath_symlink="${SYMLINK}_multipath$count"
	    echo $"     $multipath_symlink -> $multipath_dev"
	    ln -fs $multipath_dev $multipath_symlink

            # Set the ownership/permissions
	    if [ -n "$D_UID" ] || [ -n "$D_GID" ]; then
		chown $D_UID:$D_GID $multipath_dev
	    fi
	    
	    if [ -n "$PERMS" ]; then
		chmod $PERMS $multipath_dev
	    fi

	    # Automount the device if AUTOMOUNT is set and its in /etc/fstab
	    mountpoint=`grep "^$multipath_symlink\b" /etc/fstab | awk '{print $2 ; nextfile }'`
	    if [ -n "$AUTOMOUNT" ] && [ -n "$mountpoint" ] && [ `mount | grep -c "^$multipath_symlink\b"` -eq 0 ]; then
		mount $mountpoint
	    fi

	    count=$(($count + 1))
	done
	unset IFS
    fi
    
    # Set the ownership/permissions
    if [ -n "$D_UID" ] || [ -n "$D_GID" ]; then
	chown $D_UID:$D_GID $DEVICE
    fi

    if [ -n "$PERMS" ]; then
	chmod $PERMS $DEVICE
    fi		   
}

function remove_entry()
{
    # Check that we have all the arguments
    if [ -z "$cli_symlink" ]; then
	echo $"" >&2
	echo $"Error! Invalid number of arguments passed." >&2
	echo $"Usage: remove [-s symlink]" >&2
	exit 1
    fi

    cp -f $CONFIG_FILE $CONFIG_FILE.old 2>/dev/null
    cat $CONFIG_FILE.old | grep -v "^$cli_symlink\b" > $CONFIG_FILE 2>/dev/null
    rotate_old_files

    # If there is a difference, report back
    if [ `diff $CONFIG_FILE $CONFIG_FILE.0 | grep -c $` -gt 0 ]; then
	if [ `echo "$cli_symlink" | grep -c "/dev/raw/raw"` -gt 0 ]; then
	    SYM_TYPE="rawdevice"
	    raw $cli_symlink 0 0
	    echo $"Unbound rawdevice $cli_symlink"
	else
	    # Do multipath or symlink specific deletion stuff
	    if [ `diff -n $CONFIG_FILE $CONFIG_FILE.0 | awk {'print $4'} | grep -c "multipath"` -gt 0 ]; then
		SYM_TYPE="multipath"
		for sym_to_delete in `ls ${cli_symlink}_multipath[0-9]*`; do
		    if [ -h "$sym_to_delete" ]; then
			rm -f $sym_to_delete
			echo $"Deleted multipath $sym_to_delete"
		    fi
		done		
	    else
		SYM_TYPE="symlink"
		rm -f $cli_symlink
		echo $"Deleted symlink $cli_symlink"
	    fi
	fi
	echo $"Removed $SYM_TYPE $cli_symlink from $CONFIG_FILE"
    else
	echo $"" >&2
	echo $"Unable to find $SYM_TYPE $cli_symlink in $CONFIG_FILE" >&2
	echo $"No changes made." >&2
	exit 1
    fi
}

function add_entry()
{
    # Disable for s390 and s390x. Needs to done fairly different.
    if [ "`uname -m`" = "s390" -o "`uname -m`" = "s390x" ]; then
        echo $"" >&2
        echo $"Fdisk is not available for s390 or s390x." >&2
        echo $"Aborting device check." >&2
        exit 4
    fi

    # Check that the right arguments were passed
    if [ -z "$cli_symlink" ] || [ -z "$cli_device" ] && [ -z "$cli_uuid" ]; then
	echo $"" >&2
	echo $"Error! Invalid number of parameters passed." >&2
	echo $"Usage: add [-s symlink] [-d device]" >&2
	echo $"   or: add [-s rawdevice] [-d device]" >&2
	echo $"   or: add [-s symlink] [-u UUID]" >&2
	echo $"   or: add [-s rawdevice] [-u UUID]" >&2
	exit 1
    fi

    # Determine the $SYM_TYPE
    if [ `echo "$cli_symlink" | grep -c "/dev/raw/raw"` -gt 0 ]; then
	SYM_TYPE="rawdevice"
    elif [ -n "$cli_multipath" ]; then
	SYM_TYPE="multipath"
    else
	SYM_TYPE="symlink"
    fi

    # Make sure symlink begins with "/"
    if [ `echo "$cli_symlink" | grep -c "^/"` -eq 0 ]; then
	echo $"" >&2
	echo $"Error! Symlink does not begin with '/'". >&2
	echo $"Symlinks in devlabel must be absolute pathnames." >&2
	exit 11
    fi

    # Make sure symlink does not contain "_multipath"
    if [ `echo "$cli_symlink" | grep -c "_multipath"` -gt 0 ]; then
	echo $"" >&2
	echo $"Error! Symlink name contains a restricted phrase." >&2
	echo $"Symlink contains the phrase '_multipath'." >&2
	exit 14
    fi

    # Make sure they don't have a rawdevice with multipath
    if [ "$SYM_TYPE" == "rawdevice" ] && [ -n "$cli_multipath" ]; then
	echo $"" >&2
	echo $"Error! Rawdevice and multipath in same entry." >&2
	echo $"Rawdevices cannot be set to multipath in this manner." >&2
	exit 15
    fi
    
    # Check that $cli_symlink does not exist
    if [ -e "$cli_symlink" ] && [ "$SYM_TYPE" != "rawdevice" ]; then
	echo $"" >&2
	echo $"The file $cli_symlink already exists." >&2
	echo $"Failure. Could not create a $SYM_TYPE." >&2
	exit 3
    fi

    # Check that a symlink named $cli_symlink is not already in CONFIG_FILE
    if [ `grep -c "^$cli_symlink " $CONFIG_FILE` -gt 0 ]; then
	echo $"" >&2
	echo $"The $SYM_TYPE $cli_symlink is already in $CONFIG_FILE." >&2
	echo $"Failure. Could not add $SYM_TYPE." >&2
	exit 10
    fi

    # Check that -d and -u aren't simultaneously set
    if [ -n "$cli_uuid" ] && [ -n "$cli_device" ]; then
	echo $"" >&2
	echo $"You cannot add by uuid and by device during the same add action." >&2
	echo $"Failure.  Could not process add command." >&2
	exit 13
    fi

    # If $cli_uuid is set, find out what device it references
    if [ -n "$cli_uuid" ]; then
	check_uniqueness "$cli_uuid" "$SYM_TYPE"
	if [ "$?" -ne 0 ]; then
	    echo $"" >&2
	    echo $"Could not add a symlink to the device with this UUID." >&2
	    echo $"Either no device could be found or multiple devices have this ID." >&2
	    echo $"" >&2
	    echo $"If multiple devices have this ID because it is part of a multipath set," >&2
	    echo $"then you can add with the --multipath option to ensure device naming" >&2
	    echo $"consistency with multipath devices.  Don't use --multipath otherwise." >&2
	    exit 12
	fi
	IFS='
'
	cli_device=`echo "${device_mappings[*]}" | grep $cli_uuid | sed 's/=.*// ; 1q'`
	unset IFS
    fi
   
    # Check that $cli_device returns a UUID
    device_prefix=`echo $cli_device | sed 's/\(.*[hs]d[a-z]\+\)[0-9]*/\1/'`
    get_id $cli_device

    # Check that the device even exists
    if [ `fdisk -l $device_prefix 2>/dev/null | awk '{print $1}' | grep -c "^$cli_device$"` -eq 0 ] && [ `fdisk -l $device_prefix 2>/dev/null | awk '{print $2}' | grep -c "^$cli_device:$"` -eq 0 ]; then
	echo $"" >&2
	echo $"$cli_device does not exist." >&2
	echo $"Failure.  Since this device does not exist, it did not return an identifier." >&2
	exit 4
    fi

    # Check for a valid ID
    if [ -z "$ID" ]; then
	echo $"" >&2
	echo $"$cli_device did not return a UUID." >&2
	echo $"Failure.  Could not find a UUID for $cli_device." >&2
	exit 5
    else
	# Check that device gives a unique ID
	check_uniqueness "$ID" "$SYM_TYPE"
	return_code="$?"
	if [ "$return_code" -eq 1 ]; then
	    echo $"" >&2
	    echo $"Failure." >&2
	    echo $"The device you are trying to add to devlabel does not show up in" >&2
	    echo $"/proc/partitions." >&2
	    exit 9
	elif [ "$return_code" -gt 1 ]; then
	    echo $"" >&2
	    echo $"Failure." >&2
	    echo $"The device UUID for $cli_device is identical to other devices on your system." >&2
	    echo $"Because of this, you cannot use devlabel with this device." >&2
	    exit 6
	else
	    # Call make_link to do the linking
	    SYMLINK="$cli_symlink"
	    DEVICE="$cli_device"
	    D_UID="$cli_uid"
	    D_GID="$cli_gid"
	    PERMS="$cli_perms"
	    UNIQUE_ID="$ID"
	    make_link
	    
	    # Format the options, if any
	    options_to_write=""
	    if [ -n "$cli_automount" ]; then
		options_to_write="automount"
	    fi
	    if [ -n "$cli_multipath" ]; then
		options_to_write=`[ -n "$options_to_write" ] && echo "$options_to_write,"`"multipath"
	    fi
	    if [ -n "$cli_uid" ]; then
		options_to_write=`[ -n "$options_to_write" ] && echo "$options_to_write,"`uid=$cli_uid
	    fi
	    if [ -n "$cli_gid" ]; then
		options_to_write=`[ -n "$options_to_write" ] && echo "$options_to_write,"`gid=$cli_gid
	    fi
	    if [ -n "$cli_perms" ]; then
		options_to_write=`[ -n "$options_to_write" ] && echo "$options_to_write,"`perms=$cli_perms
	    fi
	    echo "$cli_symlink $cli_device $ID $options_to_write" >> $CONFIG_FILE
	    echo $"Added $cli_symlink to $CONFIG_FILE"
	fi
    fi
}

function startup()
{
    # Declare necessary variables
    declare -a newfile
    write_file=""

    # Parse up the CONFIG_FILE and do symlinkings
    while read line; do

	dont_link=""
 	parse_config_data "$line"

	# Check if its a comment or blank line
	if [ -n "$IGNORE_LINE" ]; then
	    newfile[${#newfile[*]}]=$line

	else
	    # Get the ID
	    device_prefix=`echo $DEVICE | sed 's/\(.*[hs]d[a-z]\+\)[0-9]*/\1/'`
	    device_suffix=`echo $DEVICE | sed 's/.*[hs]d[a-z]\+\([0-9]*\)/\1/'`
	    get_id $DEVICE

	    # Make sure that the UUID is unique before making the symlink
	    check_uniqueness "$UNIQUE_ID" "$SYM_TYPE"
	    return_code="$?"
	    if [ "$return_code" -eq 1 ]; then
		newfile[${#newfile[*]}]="$line"
		dont_link="TRUE"
		mountpoint=`grep "^$SYMLINK\b" /etc/fstab | awk '{ print $2 ; nextfile }'`
		if [ -n "$mountpoint" ]; then
		    umount $mountpoint 2>/dev/null
		fi
		if [ "$SYM_TYPE" == "symlink" ]; then
		    rm -f $SYMLINK
		fi
		[ -n "`type -p logger`" ] && logger -t devlabel -p syslog.warning "The $SYM_TYPE $SYMLINK -> $DEVICE is being ignored in $CONFIG_FILE because the correct device cannot be found."
		echo $""
		echo $"The device $DEVICE no longer seems to exist.  Because of this, the"
		echo $"$SYM_TYPE $SYMLINK -> $DEVICE will not be available. The reference"
		echo $"to this $SYM_TYPE in $CONFIG_FILE will be ignored."

	    elif [ "$return_code" -gt 1 ]; then
		newfile[${#newfile[*]}]=$line
		dont_link="TRUE"
		if [ "$SYM_TYPE" == "symlink" ]; then
		    rm -f $SYMLINK
		fi
		[ -n "`type -p logger`" ] && logger -t devlabel -p syslog.warning "The $SYM_TYPE $SYMLINK -> $DEVICE is being ignored in $CONFIG_FILE due to uniqueness issues."
		echo $""
		echo $"The UUID associated with $SYMLINK is no longer absolutely unique."
		echo $"The $SYM_TYPE $SYMLINK -> $DEVICE will not be available."
		echo $"The reference to this $SYM_TYPE in $CONFIG_FILE will be ignored."

	    elif [ "$ID" != "$UNIQUE_ID" ]; then

		# Determine the new DEVICE name by searching for the UNIQUE_ID amongst all devices
		IFS='
'
		new_device="`echo "${device_mappings[*]}" | grep "=$UNIQUE_ID$" | sed 's/=.*// ; 1q'`"
		unset IFS

		# Determine if partition has just been deleted or if its been moved
	        if [ -z "$new_device" ]; then
		    newfile[${#newfile[*]}]="$line"
		    dont_link="TRUE"
		    mountpoint=`grep "^$SYMLINK\b" /etc/fstab | awk '{print $2 ; nextfile}'`
		    if [ -n "$mountpoint" ]; then
			umount $mountpoint 2>/dev/null
		    fi
		    if [ "$SYM_TYPE" == "symlink" ]; then
			rm -f $SYMLINK
		    fi
		    [ -n "`type -p logger`" ] && logger -t devlabel -p syslog.warning "The $SYM_TYPE $SYMLINK -> $DEVICE is being ignored in $CONFIG_FILE because it cannot be found."
		    echo $""
		    echo $"The device $DEVICE no longer seems to exist.  Because of this, the"
		    echo $"$SYM_TYPE $SYMLINK -> $DEVICE will not be available. The reference"
		    echo $"to this $SYM_TYPE in $CONFIG_FILE will be ignored."
		else
		    write_file="TRUE"
		    [ -n "`type -p logger`" ] && logger -t devlabel -p syslog.notice "The device $DEVICE is now known as $new_device. $SYMLINK now points to the new name."
		    echo $""
		    echo $"Device name inconsistency detected for $SYM_TYPE $SYMLINK!"
		    echo $"The device $DEVICE is now $new_device."
		    echo $"The $SYM_TYPE $SYMLINK will now point to the new device name."
		    DEVICE="$new_device"
		fi

	    fi
	    
	    # If dont_link has not been set, call make_link to do your symlinking/raw
	    if [ -z "$dont_link" ]; then  
		make_link

	        # Write to the newfile
		newfile[${#newfile[*]}]="$SYMLINK $DEVICE $UNIQUE_ID $OPTIONS"
	    fi
	fi
    done < $CONFIG_FILE

    # If any device names changed, rewrite the CONFIG_FILE, otherwise, don't touch
    if [ -n "$write_file" ]; then
	cp -f $CONFIG_FILE $CONFIG_FILE.old 2>/dev/null
	rotate_old_files
	IFS='
'
	# Save the changes
	echo -e "${newfile[*]}" > $CONFIG_FILE 2>/dev/null
	unset IFS
    fi
}

function do_reverseremap()
{
    if [ -z "$cli_force" ]; then
	echo $"" >&2
	echo $"Warning! Warning! Warning!" >&2
	echo $"To use the reverseremap function, you must also specify --force" >&2
	echo $"" >&2
	echo $"ReverseRemap can be a dangerous action to your devlabel configuration." >&2
	echo $"It wipes out the UUID values (column 3) from /etc/sysconfig/devlabel" >&2
	echo $"and using the last known device name (column 2), determines the UUID" >&2
	echo $"for that device and from then on devlabel will use that UUID to assure" >&2
	echo $"consistent access." >&2
	echo $"" >&2
	echo $"You should only use this action if you know what you are doing!  If a" >&2
	echo $"device renaming event has occurred on your system, using reverseremap will" >&2
	echo $"make devlabel ignore it and then think that no renaming event ever happened!" >&2
	exit 1
    fi

    # Declare necessary variables
    declare -a newfile

    # Parse up the CONFIG_FILE and do symlinkings
    while read line; do
	comment_out_line=""
 	parse_config_data "$line"
	get_id $DEVICE
	device_prefix=`echo $DEVICE | sed 's/\(.*[hs]d[a-z]\+\)[0-9]*/\1/'`
	device_suffix=`echo $DEVICE | sed 's/.*[hs]d[a-z]\+\([0-9]*\)/\1/'`

	# Check if its a comment or blank line
	if [ -n "$IGNORE_LINE" ]; then
	    newfile[${#newfile[*]}]=$line

	else
            # Check that the device even exists
	    if [ `fdisk -l $device_prefix 2>/dev/null | awk '{print $1}' | grep -c "^$DEVICE$"` -eq 0 ] && [ `fdisk -l $device_prefix 2>/dev/null | awk '{print $2}' | grep -c "^$DEVICE:$"` -eq 0 ]; then
		comment_out_line="(non-existant device)"
	    fi

	    # Make sure that the UUID is unique and exists
	    if [ -n "$ID" ]; then
		check_uniqueness "$ID" "$SYM_TYPE"
		return_code="$?"
		if [ "$return_code" -ne 0 ]; then
		    comment_out_line="(bad uuid returned)"
		fi
	    else
		comment_out_line="(no uuid returned)"
	    fi

	    if [ -z "$comment_out_line" ]; then
		newfile[${#newfile[*]}]="$SYMLINK $DEVICE $ID $OPTIONS"
	    else
		newfile[${#newfile[*]}]="##Commented out by reverseremap $comment_out_line: $SYMLINK $DEVICE $UNIQUE_ID $OPTIONS"
		[ -n "`type -p logger`" ] && logger -t devlabel -p syslog.warning "devlabel reverseremap has commented out the entry for $DEVICE in $CONFIG_FILE $comment_out_line."
		echo $"Warning! Reverseremap has commented out the entry for $DEVICE $comment_out_line."
	    fi

	fi

    done < $CONFIG_FILE

    # Rotate files
    cp -f $CONFIG_FILE $CONFIG_FILE.old 2>/dev/null
    rotate_old_files
    IFS='
'
    # Save the changes
    echo -e "${newfile[*]}" > $CONFIG_FILE 2>/dev/null
    unset IFS
    
    echo $"REVERSE REMAP complete!"
    echo $"Run devlabel restart for the changes to take effect."
}

function show_status()
{
    # Set raw -qa variable
    raw_listing=`raw -qa`

    # Parse up the CONFIG_FILE
    while read line; do
 	parse_config_data "$line"
 
 	if [ -z "$IGNORE_LINE" ]; then
	    dev_prefix=`echo "$DEVICE" | sed 's/.*\([hs]d[a-z]\+\).*/\1/'`
	    dev_suffix=`echo "$DEVICE" | sed 's/.*[hs]d[a-z]\+//'`
	    check_uniqueness "$UNIQUE_ID" "$SYM_TYPE"
	    if [ "$?" -ne 0 ] ||  [ `echo "$proc_partitions" | awk '{print $4}' | grep -c "^$dev_prefix$dev_suffix$"` -eq 0 ]; then
		echo $"$SYMLINK->$DEVICE ignored. Cannot confirm UUID. No link will exist!" >&2
		[ "$SYM_TYPE" == "symlink" ] && rm -f $SYMLINK
	    else
		if [ "$SYM_TYPE" == "symlink" ]; then
		    readlink "$SYMLINK"
		    readlink_dev="$read_link"
		    if [ -e $SYMLINK ] && [ "$DEVICE" == "$readlink_dev" ]; then
			echo -e $"`ls -l $readlink_dev | awk '{print $1"\t"$3"\t"$4}'`\t$SYMLINK -> $readlink_dev"
		    else
			echo $"The $SYM_TYPE $SYMLINK does not exist.  Restart devlabel to create it!"
		    fi
		elif [ "$SYM_TYPE" == "rawdevice" ]; then
		    bound_to=`echo "$raw_listing" | grep "$SYMLINK:" | sed 's/.*bound to \(.*\)/\1/'`
		    dev_major="`ls -l $DEVICE | awk {'print $5'}`"
		    dev_minor="`ls -l $DEVICE | awk {'print $6'}`"
		    if [ `echo "$raw_listing" | grep -c "$SYMLINK:"` -gt 0 ] && [ "$bound_to" == "major $dev_major minor $dev_minor" ]; then
			echo -e $"`ls -l $DEVICE | awk '{print $1"\t"$3"\t"$4}'`\t$SYMLINK --[RAW]--> $DEVICE"
		    else
			echo $"The $SYM_TYPE $SYMLINK is currently unbound.  Restart devlabel to bind it!"
		    fi
		elif [ "$SYM_TYPE" == "multipath" ]; then
		    for multipath_symlink in `ls ${SYMLINK}_multipath* | grep "${SYMLINK}_multipath[0-9]\+$"`; do
			if [ -h "$multipath_symlink" ]; then
			    readlink "$multipath_symlink"
			    readlink_dev="$read_link"
			    if [ -e $multipath_symlink ]; then
				echo -e $"`ls -l $readlink_dev | awk '{print $1"\t"$3"\t"$4}'`\t$multipath_symlink --[MP]--> $readlink_dev"
			    else
				echo $"The $SYM_TYPE $multipath_symlink does not exist.  Restart devlabel to create it!"
			    fi
			fi
		    done
		fi
	    fi
	fi
	   
    done < $CONFIG_FILE
}

####
#### Program Starts Here
####

unset IFS

TEXTDOMAIN=initscripts
CONFIG_FILE=/etc/sysconfig/devlabel
IGNORE_LIST=/etc/sysconfig/devlabel.d/ignore_list
LAST_LIST=/etc/sysconfig/devlabel.d/proc_partitions

[ -f $CONFIG_FILE ] || exit 0

PATH=/usr/bin:/bin:/usr/sbin:/sbin

# Check for root
if [ -n "`type -p id`" ] && [ `id -u` -ne 0 ]; then
    echo $"You must be root to use this command." >&2
    exit 1
fi

cli_device=""
cli_uuid=""
cli_symlink=""
cli_automount=""
cli_uid=""
cli_gid=""
cli_perms=""
cli_multipath=""
cli_force=""
proc_partitions="`cat /proc/partitions | awk {'print $1" "$2" "$3" "$4'}`"

# Parse command line arguments
action_flag=""
while [ $# -gt 0 ]; do
    case $1 in
	--device*|-d)
	    if echo $1 | grep '=' >/dev/null ; then
		cli_device=`echo $1 | sed 's/^.*=//'`
            else
                cli_device="$2"
                shift
            fi
            ;;
	--symlink*|-s)
	    if echo $1 | grep '=' >/dev/null ; then
		cli_symlink=`echo $1 | sed 's/^.*=//'`
            else
                cli_symlink="$2"
                shift
            fi
            ;;
	-u)
	    if echo $1 | grep '=' >/dev/null ; then
		cli_uuid=`echo $1 | sed 's/^.*=//'`
            else
                cli_uuid="$2"
                shift
            fi
            ;;
	--uid*)
	    if echo $1 | grep '=' >/dev/null ; then
		cli_uid=`echo $1 | sed 's/^.*=//'`
            else
                cli_uid="$2"
                shift
            fi
            ;;
	--gid*)
	    if echo $1 | grep '=' >/dev/null ; then
		cli_gid=`echo $1 | sed 's/^.*=//'`
            else
                cli_gid="$2"
                shift
            fi
            ;;
	--perms*)
	    if echo $1 | grep '=' >/dev/null ; then
		cli_perms=`echo $1 | sed 's/^.*=//'`
            else
                cli_perms="$2"
                shift
            fi
            ;;
	--automount)
	    cli_automount="automount"
	    ;;
	--multipath)
	    cli_multipath="multipath"
	    ;;
	--quiet|-q)
	    exec >/dev/null 2>&1
	    ;;
	--version|-v)
	    echo $"devlabel: 0.48.03"
	    exit 0
	    ;;
	--force)
	    cli_force="force"
	    ;;
	-*|--*)
	    echo $"" >&2
	    echo $"Error!  Unknown option: $1" >&2
	    show_usage
	    exit 3
	    ;;
	*)
	    if [ -n "$action_flag" ]; then
		echo $"" >&2
		echo $"Error!  Multiple actions specified: $action, $1" >&2
		show_usage 
		exit 4
	    fi
	    action_flag="set"
	    action="$1"
	    ;;
    esac
    shift
done

# If /proc/partitions has changed, get rid of $IGNORE_LIST
cleared_ignore_list=""
if ! echo "$proc_partitions" | diff $LAST_LIST - >/dev/null 2>&1; then
    echo -n "" >$IGNORE_LIST
    echo "$proc_partitions" >$LAST_LIST
    [ -n "`type -p logger`" ] && logger -t devlabel -p syslog.warning "devlabel's temporary ignore list $IGNORE_LIST has been emptied due to a change in device configuration."
    cleared_ignore_list="true"
fi

# If we're doing a start/restart, get rid of $IGNORE_LIST
case "$action" in
    start|restart|reload)
	if [ -n "`type -p id`" ] && [ `id -u` -ne 0 ]; then
	    echo $"You must be root to use this command." >&2
	    exit 1
	fi
	if [ -z "$cleared_ignore_list" ]; then
	    echo -n "" > $IGNORE_LIST
	    [ -n "`type -p logger`" ] && logger -t devlabel -p syslog.warning "devlabel's temporary ignore list $IGNORE_LIST has been emptied due to a change in device configuration."	    
	fi
	;;
esac

# Create an array of sfdisk -V outputs so we can avoid annoying errors to the screen
declare -a sfdisk_outputs
for device_to_check  in `echo "$proc_partitions" | awk '{print $4}' | grep "[hs]d[a-z]*\b"`; do
    sfdisk_status="ok"
    if [ `grep -c "/dev/$device_to_check$" $IGNORE_LIST` -eq 0 ]; then
	sfdisk_output=`LANG=C sfdisk -V /dev/$device_to_check 2>&1`

	# If it's one of these errors, then ignore all its partitions
	for error in "overlap" "not contained" "will destroy" "extends past end" "no medium"; do
	    if [ `echo "$sfdisk_output" | grep -ic "$error"` -gt 0 ]; then
		for bad_part in `echo "$proc_partitions" | awk {'print $4'} | grep "$device_to_check[0-9]"`; do
		    if [ `grep -c "/dev/$bad_part$" $IGNORE_LIST` -eq 0 ]; then
			echo "/dev/$bad_part" >> $IGNORE_LIST
			[ -n "`type -p logger`" ] && logger -t devlabel -p syslog.warning "The device /dev/$bad_part is being put in devlabel's temporary ignore list $IGNORE_LIST to avoid errors."
		    fi
		done
		sfdisk_status="bad"
	    fi
	done

	# If it's one of these errors, ignore the device itself
	for error in "no medium"; do
	    if [ `echo "$sfdisk_output" | grep -ic "$error"` -gt 0 ]; then
		echo "/dev/$device_to_check" >> $IGNORE_LIST
		[ -n "`type -p logger`" ] && logger -t devlabel -p syslog.warning "The device /dev/$device_to_check is being put in devlabel's temporary ignore list $IGNORE_LIST to avoid errors."
		sfdisk_status="bad"
	    fi
	done
    else
	sfdisk_output="bad"
    fi
    sfdisk_outputs[${#sfdisk_outputs[*]}]="/dev/$device_to_check=$sfdisk_status"	
done

# Post sfdisk, see if /proc/partitions changed already (yeah, this is possible)
proc_partitions2="`cat /proc/partitions | awk {'print $1" "$2" "$3" "$4'}`"
if [ "$proc_partitions" != "$proc_partitions2" ]; then
    proc_partitions="$proc_partitions2"
    echo "$proc_partitions" > $LAST_LIST
fi

# Create an array of current device ids
# Note that the only thing devlabel can currently handle are devices in /proc/partition of format hd[a-z]* or sd[a-z]*
declare -a device_mappings
for device_to_check  in `echo "$proc_partitions" | awk '{print $4}' | grep "[hs]d[a-z]"`; do
    get_id /dev/$device_to_check
    device_mappings[${#device_mappings[*]}]="/dev/$device_to_check=$ID"	
done


# Run the specified action
case "$action" in
    start|restart|reload)
	[ -n "`type -p logger`" ] && logger -t devlabel "devlabel service started/restarted"
        startup
	;;
    status)
	show_status
	;;
    add)
	add_entry
	;;
    remove)
	remove_entry
	;;
    printid)
	print_id
	;;
    reverseremap)
        do_reverseremap
	;;
    "")
	echo $"" >&2
	echo $"Error! No action was specified.">&2
	show_usage
	;;
    *)
	echo $"" >&2
	echo $"Error! Unknown action specified: $action" >&2
	show_usage
	;;
esac




