# Sun xVM VirtualBox
# VirtualBox installer shell routines
#

# Copyright (C) 2007 Sun Microsystems, Inc.
#
# Sun Microsystems, Inc. confidential
# All rights reserved
#

ro_LOG_FILE=""
ro_X11_AUTOSTART="/etc/xdg/autostart"

# Aborts the script and prints an error message to stderr.
#
# syntax: abort message

abort() {
    echo 1>&2 "$1"
    exit 1
}

# Creates an empty log file and remembers the name for future logging
# operations
#
# syntax: create_log logfile

create_log() {
    ro_LOG_FILE="$1"
    if [ "$ro_LOG_FILE" = "" ]; then
        abort "create_log called without an argument!  Aborting..."
    fi
    # Create an empty file
    echo > "$ro_LOG_FILE" 2> /dev/null
    if [ ! -f "$ro_LOG_FILE" -o "`cat "$ro_LOG_FILE"`" != "" ]; then
        abort "Error creating log file!  Aborting..."
    fi
}

# Writes text to standard error
#
# Syntax: info text

info() {
    echo 1>&2 "$1"
}

# Writes text to the log file
#
# Syntax: log text

log() {
    if [ "$ro_LOG_FILE" = "" ]; then
        abort "Error!  Logging has not been set up yet!  Aborting..."
    fi
    echo "$1" >> $ro_LOG_FILE
}

# Checks whether a module is loaded with a given string in its name.
#
# syntax: module_loaded string

module_loaded() {
    if [ "$1" = "" ]; then
        log "module_loaded called without an argument.  Aborting..."
        abort "Error in installer.  Aborting..."
    fi
    lsmod | grep -q $1
}

# Abort if we are not running as root

check_root() {
    if [ `id -u` -ne 0 ]; then
        abort "This program must be run with administrator privileges.  Aborting"
    fi
}

# Abort if a copy of VirtualBox is already running

check_running() {
    if pidof VBoxSVC > /dev/null 2>&1; then
        echo 1>&2 "A copy of VirtualBox is currently running.  Please close it and try again."
        abort "Please note that it can take up to ten seconds for VirtualBox to finish running."
    fi
}

# Do we have bzip2?

check_bzip2() {
    if ! ls /bin/bzip2 /usr/bin/bzip2 /usr/local/bin/bzip2 2> /dev/null | grep bzip2 > /dev/null; then
        echo 1>&2 "Please install the bzip2 utility."
        log "Please install bzip2."
        return 1
    fi
    return 0
}

# Do we have GNU make?

check_gmake() {
make --version 2>&1 | grep GNU > /dev/null
    if [ ! $? = 0 ]; then
        echo 1>&2 "Please install GNU make."
        log "Please install GNU make."
        return 1
    fi
    return 0
}

# Can we find the kernel source?

check_ksource() {
    ro_KBUILD_DIR=/lib/modules/`uname -r`/build
    if [ ! -d $ro_KBUILD_DIR/include ]; then
        ro_KBUILD_DIR=/usr/src/linux
        if [ ! -d $ro_KBUILD_DIR/include ]; then
            echo 1>&2 "Please install the build and header files for your current Linux kernel."
            echo 1>&2 "The current kernel version is `uname -r`"
            ro_KBUILD_DIR=""
            log "Could not find the Linux kernel header files - the directories"
            log "  /lib/modules/`uname -r`/build/include and /usr/src/linux/include"
            log "  do not exist."
            return 1
        fi
    fi
    return 0
}

# Is GCC installed?

check_gcc() {
    ro_gcc_version=`gcc --version 2> /dev/null | head -n 1`
    if [ "$ro_gcc_version" = "" ]; then
        echo 1>&2 "Please install the GNU compiler."
        log "Please install the GNU compiler."
        return 1
    fi # GCC installed
    return 0
}

# Is bash installed?  You never know...

check_bash() {
    if [ ! -x /bin/bash ]; then
        echo 1>&2 "Please install GNU bash."
        log "Please install GNU bash."
        return 1
    fi
    return 0
}

# Is perl installed?

check_perl() {
    if [ ! `perl -e 'print "test"' 2> /dev/null` = "test" ]; then
        echo 1>&2 "Please install perl."
        echo "Please install perl."
        return 1
    fi
    return 0
}

# What type of system are we running on?
# NOTE: The test for PCLinuxOS has to come _prior_ to the Redhat test!

check_system_type() {
    if [ ! "$ro_SYS_TYPE" = "" ]; then
        return 0
    elif [ -f /etc/pclinuxos-release ]; then
        ro_SYS_TYPE=redhat
        ro_INIT_TYPE=sysv
    elif [ -f /etc/redhat-release ]; then
        ro_SYS_TYPE=redhat
        ro_INIT_TYPE=sysv
    elif [ -f /etc/SuSE-release ]; then
        ro_SYS_TYPE=suse
        ro_INIT_TYPE=sysv
    elif [ -f /etc/debian_version ]; then
        ro_SYS_TYPE=debian
        ro_INIT_TYPE=sysv
    elif [ -f /etc/gentoo-release ]; then
        ro_SYS_TYPE=gentoo
        ro_INIT_TYPE=sysv
    elif [ -f /etc/lfs-release -a -d /etc/rc.d/init.d ]; then
        ro_SYS_TYPE=lfs
        ro_INIT_TYPE=lfs
    elif [ -f /etc/rc.d/rc.local ]; then
        ro_SYS_TYPE=unknown
        ro_INIT_TYPE=bsd
        ro_RC_LOCAL=/etc/rc.d/rc.local
    elif [ -f /etc/rc.local ]; then
        ro_SYS_TYPE=unknown
        ro_INIT_TYPE=bsd
        ro_RC_LOCAL=/etc/rc.local
    elif [ -d /etc/init.d ]; then
        ro_SYS_TYPE=unknown
        ro_INIT_TYPE=sysv
    else  # Perhaps we can determine what we need to know anyway though?
        echo 1>&2 "Unable to determine your Linux distribution"
        log "Unable to determine the Linux distribution"
        return 1
    fi
    return 0
}

# Installs a file containing a shell script as an init script
#
# syntax: install_init_script file name
#
# where file is the file to be installed and
# name is the name of the new init script

install_init_script() {
    check_system_type
    test $? -ne 1 || return 1
    test -r "$1" || return 1
    test ! "$2" = "" || \
        { log "install_init_script: invalid arguments" && return 1; }
    if [ "$ro_INIT_TYPE" = "sysv" ]; then
        cp "$1" "/etc/init.d/$2" 2> /dev/null
        chmod 755 "/etc/init.d/$2" 2> /dev/null
    elif [ "$ro_INIT_TYPE" = "bsd" ]; then
        cp "$1" "/etc/rc.d/rc.$2" 2> /dev/null
        chmod 755 "/etc/rc.d/rc.$2" 2> /dev/null
    elif [ "$ro_INIT_TYPE" = "lfs" ]; then
        cp "$1" "/etc/rc.d/init.d/$2" 2> /dev/null
        chmod 755 "/etc/rc.d/init.d/$2" 2> /dev/null
    else
        log "install_init_script: error: unknown init type"
        return 1
    fi
    return 0
}

# Remove the init script "name"
#
# syntax: remove_init_script name

remove_init_script() {
    check_system_type
    test $? -ne 1 || return 1
    test ! "$1" = "" || \
        { log "remove_init_script: missing argument" && return 1; }
    if [ "$ro_INIT_TYPE" = "sysv" ]; then
        rm -f "/etc/init.d/$1" > /dev/null 2>&1
    elif [ "$ro_INIT_TYPE" = "bsd" ]; then
        rm -f "/etc/rc.d/rc.$1" > /dev/null 2>&1
    elif [ "$ro_INIT_TYPE" = "lfs" ]; then
        rm -f "/etc/rc.d/init.d/$1" > /dev/null 2>&1
    else
        log "remove_init_script: error: unknown init type"
        return 1
    fi
    return 0
}

# Start the init script "name"
#
# syntax: start_init_script name

start_init_script() {
    check_system_type
    test $? -ne 1 || return 1
    test ! -z "$1" || \
        { log "start_init_script: missing argument" && return 1; }
    if [ "$ro_INIT_TYPE" = "sysv" ]; then
        "/etc/init.d/$1" start >> $ro_LOG_FILE 2>&1
    elif [ "$ro_INIT_TYPE" = "bsd" ]; then
        "/etc/rc.d/rc.$1" start >> $ro_LOG_FILE  2>&1
    elif [ "$ro_INIT_TYPE" = "lfs" ]; then
        "/etc/rc.d/init.d/$1" start >> $ro_LOG_FILE 2>&1
    else
        log "start_init_script: error: unknown init type"
        return 1
    fi
}

# Stop the init script "name"
#
# syntax: start_init_script name

stop_init_script() {
    check_system_type
    test $? -ne 1 || return 1
    test ! -z "$1" || \
        { log "stop_init_script: missing argument" && return 1; }
    if [ "$ro_INIT_TYPE" = "sysv" ]; then
        "/etc/init.d/$1" stop >> $ro_LOG_FILE 2>&1
    elif [ "$ro_INIT_TYPE" = "bsd" ]; then
        "/etc/rc.d/rc.$1" stop >> $ro_LOG_FILE 2>&1
    elif [ "$ro_INIT_TYPE" = "lfs" ]; then
        "/etc/rc.d/init.d/$1" stop >> $ro_LOG_FILE 2>&1
    else
        log "stop_init_script: error: unknown init type"
        return 1
    fi
    return 0
}

# Add a service to a runlevel
#
# syntax: addrunlevel name start_order stop_order

addrunlevel() {
    test ! -z "$1" || \
        { log "addrunlevel: missing argument(s)" && return 1; }
    check_system_type
    # Redhat based systems
    if [ "$ro_SYS_TYPE" = "redhat" ]
    then
        test -x "/sbin/chkconfig" || \
            { log "addrunlevel: /sbin/chkconfig not found" && return 1; }
        /sbin/chkconfig --del $1 > /dev/null 2>&1

        if /sbin/chkconfig -v > /dev/null 2>&1; then
            /sbin/chkconfig --level 35 $1 on || {
                log "Cannot add $1 to run levels: 35" && return 1
            }
        else
            /sbin/chkconfig $1 35 || {
                log "Cannot add $1 to run levels: 35" && return 1
            }
        fi
    # SUSE-base systems
    elif [ "$ro_SYS_TYPE" = "suse" ]
    then
        test -x /sbin/insserv || {
            log "addrunlevel: insserv not found" && return 1;
        }
        /sbin/insserv -r $1 > /dev/null 2>&1
        /sbin/insserv $1 > /dev/null
    # Debian/Ubuntu-based systems
    elif [ "$ro_SYS_TYPE" = "debian" ]; then
        test -x `which update-rc.d` || \
            { log "addrunlevel: update-rc.d not found" && return 1; }
        test ! -z "$2" || \
            { log "addrunlevel: missing second argument" && return 1; }
        test ! -z "$3" || \
            { log "addrunlevel: missing third argument" && return 1; }
        # Debian does not support dependencies currently -- use argument $2
        # for start sequence number and argument $3 for stop sequence number
        update-rc.d -f $1 remove > /dev/null 2>&1
        update-rc.d $1 defaults $2 $3 > /dev/null 2>&1
    # Gentoo Linux
    elif [ "$ro_SYS_TYPE" = "gentoo" ]; then
        test -x `which rc-update` || \
            { log "addrunlevel: update-rc.d not found" && return 1; }
        rc-update del $1 > /dev/null 2>&1
        rc-update add $1 default > /dev/null 2>&1
    # Linux from scratch, by the book
    elif [ "$ro_SYS_TYPE" = "lfs" ]; then
        test -x /etc/rc.d/init.d/$1 || \
            { log "addrunlevel: name argument must be a script in /etc/rc.d/init.d" && return 1; }
        expr "$2" + 0 > /dev/null 2>&1 && expr 0 \<= "$2" > /dev/null && \
            [ `expr length "$2"` -eq 2 ] || \
            { log "addrunlevel: start sequence number must be between 00 and 99" && return 1; }
        expr "$3" + 0 > /dev/null 2>&1 && expr 0 \<= "$3" > /dev/null && \
            [ `expr length "$3"` -eq 2 ] || \
            { log "addrunlevel: stop sequence number must be between 00 and 99" && return 1; }
        ln -fs "/etc/rc.d/init.d/$1" "/etc/rc.d/rc0.d/K`expr $3`$1" > /dev/null 2>&1
        ln -fs "/etc/rc.d/init.d/$1" "/etc/rc.d/rc1.d/K`expr $3`$1" > /dev/null 2>&1
        ln -fs "/etc/rc.d/init.d/$1" "/etc/rc.d/rc2.d/S`expr $2`$1" > /dev/null 2>&1
        ln -fs "/etc/rc.d/init.d/$1" "/etc/rc.d/rc3.d/S`expr $2`$1" > /dev/null 2>&1
        ln -fs "/etc/rc.d/init.d/$1" "/etc/rc.d/rc4.d/S`expr $2`$1" > /dev/null 2>&1
        ln -fs "/etc/rc.d/init.d/$1" "/etc/rc.d/rc5.d/S`expr $2`$1" > /dev/null 2>&1
        ln -fs "/etc/rc.d/init.d/$1" "/etc/rc.d/rc6.d/K`expr $3`$1" > /dev/null 2>&1
    # BSD-based systems require changing the rc.local file to start a new service.
    elif [ "$ro_INIT_TYPE" = "bsd" ]; then
        if ! grep $1 $ro_RC_LOCAL
        then
            echo "# Start $1" >> $ro_RC_LOCAL
            echo "# If you do not wish this to be executed here then comment it out," >> $ro_RC_LOCAL
            echo "# and the installer will skip it next time." >> $ro_RC_LOCAL
            echo "if [ -x /etc/rc.d/rc.$1 ]; then" >> $ro_RC_LOCAL
            echo "    /etc/rc.d/rc.$1 start" >> $ro_RC_LOCAL
            echo "fi" >> $ro_RC_LOCAL
            echo "" >> $ro_RC_LOCAL
        fi
    # Probably most unknown Linux systems will be sysv type ones.  These can theoretically
    # be handled automatically if people give us information about them.
    elif [ "$ro_INIT_TYPE" = "sysv" ]; then
        echo 1>&2 "As our installer does not recognize your Linux distribution, we were unable to"
        echo 1>&2 "set up the initialization script $1 correctly.  The script has been copied"
        echo 1>&2 "copied to the /etc/init.d directory.  You should set up your system to start"
        echo 1>&2 "it at system start, or start it manually before using VirtualBox."
        echo 1>&2 ""
        echo 1>&2 "If you would like to help us add support for your distribution, please open a"
        echo 1>&2 "new ticket on http://www.virtualbox.org/wiki/Bugtracker."
    fi
    return 0
}


# Delete a service from a runlevel
#
# syntax: delrunlevel name

delrunlevel() {
    test ! -z "$1" || \
        { log "delrunlevel: missing argument" && return 1; }
    check_system_type
    # Redhat-based systems
    if [ "$ro_SYS_TYPE" = "redhat" ]
    then
        test -x "/sbin/chkconfig" || \
            { log "addrunlevel: /sbin/chkconfig not found" && return 1; }
        if /sbin/chkconfig --list $1 > /dev/null 2>&1; then
            /sbin/chkconfig --del $1 > /dev/null 2>&1 || {
                log "Cannot delete $1 from runlevels" && return 1
            }
        fi
    # SUSE-based systems
    elif [ "$ro_SYS_TYPE" = "suse" ]
    then
        test -x /sbin/insserv || {
            log "addrunlevel: insserv not found" && return 1;
        }
        /sbin/insserv -r $1 > /dev/null 2>&1
    # Debian/Ubuntu-based systems
    elif [ "$ro_SYS_TYPE" = "debian" ]; then
        test -x `which update-rc.d` || \
            { log "addrunlevel: update-rc.d not found" && return 1; }
        update-rc.d -f $1 remove > /dev/null 2>&1
    # Gentoo Linux
    elif [ "$ro_SYS_TYPE" = "gentoo" ]; then
        test -x `which rc-update` || \
            { log "addrunlevel: update-rc.d not found" && return 1; }
        rc-update del "$1" > /dev/null 2>&1
    # Linux from scratch, by the book
    elif [ "$ro_SYS_TYPE" = "lfs" ]; then
        rm "/etc/rc.d/rc0.d/K??$1" > /dev/null 2>&1
        rm "/etc/rc.d/rc1.d/K??$1" > /dev/null 2>&1
        rm "/etc/rc.d/rc2.d/S??$1" > /dev/null 2>&1
        rm "/etc/rc.d/rc3.d/S??$1" > /dev/null 2>&1
        rm "/etc/rc.d/rc4.d/S??$1" > /dev/null 2>&1
        rm "/etc/rc.d/rc5.d/S??$1" > /dev/null 2>&1
        rm "/etc/rc.d/rc6.d/K??$1" > /dev/null 2>&1
    # Unknown sysv-type system
    elif [ "$ro_INIT_TYPE" = "sysv" ]; then
        echo 1>&2 "Please remove remove references to the initialization script"
        echo 1>&2 "/etc/init.d/$1 to complete the uninstallation."
    fi
    # BSD-type systems will just not start the script if it is not there.
    # Assume that BSD users understand their system.
    return 0
}


# Install an X11 desktop startup application.  The application should be a shell script.
# Its name should be purely alphanumeric and should start with a two digit number (preferably
# 98 or thereabouts) to indicate its place in the Debian Xsession startup order.
#
# syntax: install_x11_startup_app app desktop service_name

install_x11_startup_app() {
    test -r $1 || \
        { log "install_x11_startup_app: no script given" && return 1; }
    test -r $2 || \
        { log "install_x11_startup_app: no desktop file given" && return 1; }
    app_dest=`basename $1 sh`
    app_dest_sh=`basename $1 sh`.sh
    desktop_dest=`basename $2`
    redhat_dir=/etc/X11/Xsession.d
    mandriva_dir=/etc/X11/xinit.d
    debian_dir=/etc/X11/xinit/xinitrc.d
    log "install_x11_startup_app(): dest is $desktop_dest"
    if [ -d "$mandriva_dir" -a -w "$mandriva_dir" -a -x "$mandriva_dir" ]
    then
        log "install_x11_startup_app(): executing install -m 0644 $1 $mandriva_dir/$app_dest"
        install -m 0755 $1 "$mandriva_dir/$app_dest"
        return 0
    fi
    if [ -d "$ro_X11_AUTOSTART" -a -w "$ro_X11_AUTOSTART" -a -x "$ro_X11_AUTOSTART" ]
    then
        log "install_x11_startup_app(): executing install -m 0644 $2 $ro_X11_AUTOSTART/$desktop_dest"
        install -m 0644 $2 "$ro_X11_AUTOSTART/$desktop_dest"
        return 0
    fi
    if [ -d "$redhat_dir" -a -w "$redhat_dir" -a -x "$redhat_dir" ]
    then
        log "install_x11_startup_app(): executing install -m 0644 $1 $redhat_dir/$app_dest"
        install -m 0755 $1 "$redhat_dir/$app_dest"
        return 0
    fi
    if [ -d "$debian_dir" -a -w "$debian_dir" -a -x "$debian_dir" ]
    then
        log "install_x11_startup_app(): executing install -m 0644 $1 $debian_dir/$app_dest_sh"
        install -m 0755 $1 "$debian_dir/$app_dest_sh"
        return 0
    fi
    echo 1>&2 "Could not set up the X Window $3 service."
    echo 1>&2 "To start the $3 service at log-in for a given user,"
    echo 1>&2 "add the command $1 to the file .xinitrc in their home"
    echo 1>&2 "directory."
    return 1
}
