### Written for the LFS community by DJ Lucas <dj@linuxfromscratch.org>
### 20071012

#!/bin/bash
# Begin /lib/lsb/install_initd

. /lib/lsb/initd_functions

# This script is grossly over commented, but is somewhat complex.
# It is exactly to spec and meets the LSB requirements, and LFS's 
# requirements as I have defined them.  It takes advantage of the 
# GNU make utility to handle dependency tracking, so that note
# might help explain the numerous comments pointing to these
# magical makefiles.

# First, the program should take exactly one argument, the full path of the
# script only! Check to make sure only one argument is passed, if not exit 2
# This is not defined by LSB, but is consistent with the exit code for
# invalid or excess arguments in init scripts.
if test ! -n "${1}" -o -n "${2}"
then
    echo ""
    echo "Invalid or excess arguments!"
    echo "Usage: ${0} /etc/init.d/<scriptname>"
    echo ""
    exit 2
fi

case "${1}" in
    -*)
        echo ""
        echo "Usage: ${0} /etc/init.d/<scriptname>"
        echo ""
        exit 0
    ;;
esac

# Next check to make sure that the script really exists
# If it does not, exit 5. Again this is not defined by the LSB, however, is
# consistent with the exit values for init scripts when the program is not 
# installed.

if test ! -f "${1}"
then
    echo ""
    echo "Script does not exist!"
    echo "Usage: ${0} /etc/init.d/<scriptname>"
    echo ""
    exit 5
fi

# Go ahead and get all the information that is needed from disk in one shot
get_headers

# Now get the array index number for the specified script.
# Also note that the 'filename' variable is set (basename of script), and
# the script is verified that it is LSB compliant.
get_index "${1}"
# filename and index will be overwritten if we have to cycle through deps, so
# assign those values to alternate variables
install_filename="${filename}"
install_index="${index}"

# Check to see if script is already enabled
grep "${filename}" /lib/lsb/scriptlist 2>&1 >/dev/null
if test ${?} -eq 0; then
    echo "Script is already enabled!"
    echo "Check /lib/lsb/scriptlist if you believe this is in error."
    exit 0
else
    checkfind=`find /etc/rc*.d -name ???${filename}`
    if test ! -z ${checkfind}; then
        echo "Script is manually enabled!"
        echo "You will need to remove manually created symlinks prior to running this"
        echo "script.  Unable to continue...Exiting!"
        exit 2
    fi
    unset checkfind
fi

# Give the user something other than dead time
echo -n "Resolving dependencies (this may take a bit)..."

# Cleanup any previous leftovers and create all of our temporary files
rm -rf /tmp/lsbinitd
mkdir /tmp/lsbinitd
touch /tmp/lsbinitd/sysinitstart
for runlevel in 0 1 2 3 4 5 6
do
    touch /tmp/lsbinitd/${runlevel}{start,stop}
done
cp /lib/lsb/scriptlist /tmp/lsbinitd/scriptlist
echo `echo "${1}" | sed 's@/etc/init.d/@@'` >> /tmp/lsbinitd/scriptlist

###############################################################################
# reorder_scripts - When a suitable link value cannot be determined, based on #
#                   a sctipt's dependencies, all scripts must be reorderd.    #
###############################################################################
##### reorder_scripts()
##### {
    # Determine what scripts will be run and in what order 
    for runlevel in sysinit 0 1 2 3 4 5 6
    do
        # get the scripts that stop in $runlevel
        # skip for sysinit
        if test "${runlevel}" != "sysinit"; then
            for script in `cat /tmp/lsbinitd/scriptlist`
            do
                get_index "${script}"
                get_default_stop 
                echo "${defaultstop}" | grep "${runlevel}" > /dev/null
                if test ${?} -eq 0; then
                    stoplist="${stoplist}${script} "
                fi
            done
            unset script
        fi

        # get the scripts that start in $runlevel
        for script in `cat /tmp/lsbinitd/scriptlist`
        do
            get_index "${script}"
            get_default_start
            echo "${defaultstart}" | grep "${runlevel}" > /dev/null
            if test ${?} -eq 0; then
                startlist="${startlist}${script} "
            fi
        done
        unset script

        # Now create some temporary lists so we can handle dependency tracking
        # First, find all scripts for each runlevel that should be stoped
        # Skip sysinit for stop links
        if test "${runlevel}" != "sysinit"; then
            for script in ${stoplist}
            do
                # create entry in the ${runlevel}stop file
                echo -n "${script}:	" >> /tmp/lsbinitd/${runlevel}stop
                get_index ${script} 
                # get previous stop facilities, and convert lsb facility names
                # into script names and send to makefile for dependency tracking
                get_required_stop
                convert_lsb_required "${requiredstop}"
                echo -n "${reqprovideslist}" >> /tmp/lsbinitd/${runlevel}stop
                get_should_stop
                convert_lsb_should "${shouldstop}"
                echo "${optprovideslist}" >> /tmp/lsbinitd/${runlevel}stop
            done
        fi

        # Now, all scripts that should be started by runlevel
        for script in ${startlist}
        do
            # create entry in the ${runlevel}start file
            echo -n "${script}: " >> /tmp/lsbinitd/${runlevel}start
            get_index ${script}
            # get previous start facilities, and convert lsb facility names
            # into script names and send to makefile for dependency tracking
            # in runlevels 0 and 6 we should verify stops, not starts
            if test "${runlevel}" = "0" -o "${runlevel}" = "6"; then
                get_required_stop
                convert_lsb_required "${requiredstop}"
            else
                get_required_start 
                convert_lsb_required "${requiredstart}"
            fi
            echo -n "${reqprovideslist}" >> /tmp/lsbinitd/${runlevel}start
            # Again in runlevels 0 and 6 get stop prereqs
            if test "${runlevel}" = "0" -o "${runlevel}" = "6"; then
                get_should_stop
                convert_lsb_should "${shouldstop}"
            else
                get_should_start
                convert_lsb_should "${shouldstart}"
            fi
            echo "${optprovideslist}" >> /tmp/lsbinitd/${runlevel}start
        done
        unset startlist
        unset stoplist
    done

    # Now that dependencies are handled completely for each script, fix the    
    # startup lists for items who are already started during sysinit and items
    # stopped in runlevels 0 and 6.
    for runlevel in 1 2 3 4 5
    do
        # Get a list of scripts started in sysinit
        for script in `sed -e 's/:.*$//' /tmp/lsbinitd/sysinitstart`
        do
            sed -i "s@ ${script}@@" /tmp/lsbinitd/${runlevel}start
        done
    done

    # Modify runlevels 0 and 6 start scripts to remove items already stopped
    for runlevel in 0 6
    do
        # get a list of scripts stopped in $runlevel
        for script in `sed -e 's/:.*$//' /tmp/lsbinitd/${runlevel}stop`
        do
            # and remove them from the deps in the start script
            sed -i "s@ ${script}@@" /tmp/lsbinitd/${runlevel}start
        done
    done

    # Finish our statement above...
    echo "Completed!"

    # cycle through the deps and provide an ordered list
    ## FIXME: There is currently no bail-out logic for reciprocal dependencies
    ### Update.  A suggested solution is put in place, however, it's based on
    ###          an educated guess and has no basis on real scripts in the 
    ###          wild.  See 'FIXME' comment 23 lines below for more details.
    echo -n "Generating symlinks..."
    for scriptlevel in {0,1,2,3,4,5,6}{start,stop} sysinitstart
    do
        unset orderedlist
        count=0
        while test ${count} -lt 12
        do
            unset nodeplist
            nodeplist=`grep -e ":.$" /tmp/lsbinitd/${scriptlevel} | sed 's@:@@g'`
            if test ! -n "${nodeplist}" -a ! -n "$(cat /tmp/lsbinitd/${scriptlevel})"; then
                break
            fi

            for script in `echo "${nodeplist}"`
            do
                orderedlist="${orderedlist} ${script}"
                sed -i -e "s@ ${script}@@g" \
                    -e "s@^${script}:@:@g" \
                    -e "/^:/d" /tmp/lsbinitd/${scriptlevel}
            done
            count=$(( ${count} + 1 ))
        done
        ## FIXME: This is the fix, however, it is an assumption that may prove
        ##        to be incorrect in practice...I don't know so I left the
        ##        'FIXME' comments in place for now.  If my guess does prove
        ##        to be incorrect, simply increase the loop count.
        # assume that no script will have more than 10 dependencies listed
        if test ${count} -eq 12; then
            echo "Error: Reciprocal dependency detected!"
            echo "Either there is an error in the dependencies of your new script."
            echo "or there are dependency problems with an already installed script."
            echo ""
            echo "The offending scripts and dependencies are listed below:"
            cat /tmp/lsbinitd/${scriptlevel}
            echo ""
            echo "Aborting!"
            exit 1 # generic failure
        fi
        echo "${orderedlist}" > /tmp/lsbinitd/${scriptlevel}
    done

    # Process starting symlinks
    for scriptlevel in {sysinit,0,1,2,3,4,5,6}
    do
        count=0
        for script in `cat /tmp/lsbinitd/${scriptlevel}start`
        do
            if test ${count} -lt 10; then
                link="0${count}"
            else
                link="${count}"
            fi

            echo "rm -f /etc/rc${scriptlevel}.d/S??${script}"
            echo "ln -sf ../init.d/${script} /etc/rc${scriptlevel}.d/S${link}${script}"
            count=$(( $count + 5 ))
        done
    done

    # Process stoping symlinks
    for scriptlevel in {0,1,2,3,4,5,6}
    do
        count=0
        for script in `cat /tmp/lsbinitd/${scriptlevel}stop`
        do
            if test ${count} -lt 10; then
                link="0${count}"
            else
                link="${count}"
            fi

            echo "rm -f /etc/rc${scriptlevel}.d/K??${script}"
            echo "ln -sf ../init.d/${script} /etc/rc${scriptlevel}.d/K${link}${script}"
            count=$(( $count + 5 ))
        done
    done

    echo "Completed!"
##### } # End reorder_links()

# Fingure out which runlevels will be modified and only regenerate the link
# order on those runlevels.
##### get_default_start
##### get_default_stop

# Figure out the starting runlevels first
## for each runlevel in $default_start find the provides for the newly
## installed script and use the same temp files used in the reorder function
## to track deps (if necessary)

# First, find the provides for the newly installed script and cycle through
# the should_{start,stop} values in the array to see if we've provided a new
# optional dependency for another script, if we have, then we now have a 
# ceiling/floor value for the numeric link that we may have to take into 
# account a little later on.



# Okay script is valid so it's okay to add it to the list
## Commented out for now
# cp /tmp/lsbinitd/scriptlist /lib/lsb/scriptlist
# chown root:root /lib/lsb/scriptlist > /dev/null
# chmod 644 /lib/lsb/scriptlist > /dev/null

# Clean up our mess
## Commented out for now
# rm -rf /tmp/lsbinitd

