# Oracle VM VirtualBox
# VirtualBox installer shell routines
#

# Copyright (C) 2007-2010 Oracle Corporation
#
# This file is part of VirtualBox Open Source Edition (OSE), as
# available from http://www.virtualbox.org. This file is free software;
# you can redistribute it and/or modify it under the terms of the GNU
# General Public License (GPL) as published by the Free Software
# Foundation, in version 2 as it comes in the "COPYING" file of the
# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
#

ro_LOG_FILE=""
ro_X11_AUTOSTART="/etc/xdg/autostart"
ro_KDE_AUTOSTART="/usr/share/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
    return 0
}

# Writes test to standard output and to the log file
#
# Syntax: infolog text

infolog() {
    info "$1"
    log "$1"
}

# 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() {
    VBOXSVC_PID=`pidof VBoxSVC 2> /dev/null`
    if [ -n "$VBOXSVC_PID" ]; then
        if [ -f /etc/init.d/vboxweb-service ]; then
            kill -USR1 $VBOXSVC_PID
        fi
        sleep 1
        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
    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?

check_system_type() {
    if [ ! "$ro_SYS_TYPE" = "" ]; then
        return 0
    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 [ -x /sbin/chkconfig ]; then
        ro_SYS_TYPE=redhat
        ro_INIT_TYPE=sysv
    elif [ -x /sbin/insserv ]; then
        ro_SYS_TYPE=suse
        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/pardus-release ]; then
        ro_SYS_TYPE=pardus
        ro_INIT_TYPE=pardus
    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
}

# Hack to handle Mandriva's speedboot runlevel
copy_install_script() {
    if [ "$ro_INIT_TYPE" = "sysv" -a -r /etc/sysconfig/speedboot ]; then
        cp "$1" "$2" 2>/dev/null
    else
        sed -e 's/^\(#\s*chkconfig:\s*[0-9]*\)7/\1/' "$1" > "$2"
    fi
}

# 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() {
    self="install_init_script"
    script=$1
    name=$2
    pardus_script=$name-pardus.py
    check_system_type
    test $? -ne 1 || return 1
    test -r "$script" || return 1
    test ! "$name" = "" || \
        { log "$self: invalid arguments" && return 1; }
    if [ "$ro_INIT_TYPE" = "pardus" ];then
        test -r "$pardus_script" || \
            { log "$self: Pardus service script missing" && return 1; }
    fi
    if [ "$ro_INIT_TYPE" = "sysv" ]; then
        copy_install_script "$script" "/etc/init.d/$name"
        chmod 755 "/etc/init.d/$name" 2> /dev/null
    elif [ "$ro_INIT_TYPE" = "bsd" ]; then
        copy_install_script "$script" "/etc/rc.d/rc.$name"
        chmod 755 "/etc/rc.d/rc.$name" 2> /dev/null
    elif [ "$ro_INIT_TYPE" = "lfs" ]; then
        copy_install_script "$script" "/etc/rc.d/init.d/$name"
        chmod 755 "/etc/rc.d/init.d/$name" 2> /dev/null
    elif [ "$ro_INIT_TYPE" = "pardus" ]; then
        copy_install_script "$script" "/usr/sbin/$name"
        chmod 755 "/usr/sbin/$name" 2> /dev/null
        hav register $name System.Service $pardus_script
    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() {
    self="remove_init_script"
    name=$1
    check_system_type
    test $? -ne 1 || return 1
    test ! "$name" = "" || \
        { log "$self: missing argument" && return 1; }
    if [ "$ro_INIT_TYPE" = "sysv" ]; then
        rm -f "/etc/init.d/$name" > /dev/null 2>&1
    elif [ "$ro_INIT_TYPE" = "bsd" ]; then
        rm -f "/etc/rc.d/rc.$name" > /dev/null 2>&1
    elif [ "$ro_INIT_TYPE" = "lfs" ]; then
        rm -f "/etc/rc.d/init.d/$name" > /dev/null 2>&1
    elif [ "$ro_INIT_TYPE" = "pardus" ]; then
        hav remove $name
        rm -f "/usr/sbin/$name" > /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() {
    self="start_init_script"
    name=$1
    check_system_type
    test $? -ne 1 || return 1
    test ! -z "$name" || \
        { log "$self: missing argument" && return 1; }
    if [ "$ro_INIT_TYPE" = "sysv" ]; then
        "/etc/init.d/$name" start >> $ro_LOG_FILE 2>&1
    elif [ "$ro_INIT_TYPE" = "bsd" ]; then
        "/etc/rc.d/rc.$name" start >> $ro_LOG_FILE  2>&1
    elif [ "$ro_INIT_TYPE" = "lfs" ]; then
        "/etc/rc.d/init.d/$name" start >> $ro_LOG_FILE 2>&1
    elif [ "$ro_INIT_TYPE" = "pardus" ]; then
        service $name on
    else
        log "$self: error: unknown init type"
        return 1
    fi
}

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

stop_init_script() {
    self=stop_init_script
    name=$1
    check_system_type
    test $? -ne 1 || return 1
    test ! -z "$name" || \
        { log "$self: missing argument" && return 1; }
    if [ "$ro_INIT_TYPE" = "sysv" ]; then
        "/etc/init.d/$name" stop >> $ro_LOG_FILE 2>&1
    elif [ "$ro_INIT_TYPE" = "bsd" ]; then
        "/etc/rc.d/rc.$name" stop >> $ro_LOG_FILE 2>&1
    elif [ "$ro_INIT_TYPE" = "lfs" ]; then
        "/etc/rc.d/init.d/$name" stop >> $ro_LOG_FILE 2>&1
    elif [ "$ro_INIT_TYPE" = "pardus" ]; then
        service $name off
    else
        log "$self: 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; }
        P2=$2
        P3=$3
        # add leading zero
        if [ `expr length "$P2"` -eq 1 ]; then
            P2=`expr 0$P2`
        fi
        if [ `expr length "$P3"` -eq 1 ]; then
            P3=`expr 0$P3`
        fi
        expr "$P2" + 0 > /dev/null 2>&1 && expr 0 \<= "$P2" > /dev/null && \
            [ `expr length "$P2"` -eq 2 ] || \
            { log "addrunlevel: start sequence number must be between 00 and 99" && return 1; }
        expr "$P3" + 0 > /dev/null 2>&1 && expr 0 \<= "$P3" > /dev/null && \
            [ `expr length "$P3"` -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 $P3`$1" > /dev/null 2>&1
        ln -fs "/etc/rc.d/init.d/$1" "/etc/rc.d/rc1.d/K`expr $P3`$1" > /dev/null 2>&1
        ln -fs "/etc/rc.d/init.d/$1" "/etc/rc.d/rc2.d/S`expr $P2`$1" > /dev/null 2>&1
        ln -fs "/etc/rc.d/init.d/$1" "/etc/rc.d/rc3.d/S`expr $P2`$1" > /dev/null 2>&1
        ln -fs "/etc/rc.d/init.d/$1" "/etc/rc.d/rc4.d/S`expr $P2`$1" > /dev/null 2>&1
        ln -fs "/etc/rc.d/init.d/$1" "/etc/rc.d/rc5.d/S`expr $P2`$1" > /dev/null 2>&1
        ln -fs "/etc/rc.d/init.d/$1" "/etc/rc.d/rc6.d/K`expr $P3`$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 -q $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/rc0.d/K??$1" > /dev/null 2>&1
        rm "/etc/rc1.d/K??$1" > /dev/null 2>&1
        rm "/etc/rc2.d/S??$1" > /dev/null 2>&1
        rm "/etc/rc3.d/S??$1" > /dev/null 2>&1
        rm "/etc/rc4.d/S??$1" > /dev/null 2>&1
        rm "/etc/rc5.d/S??$1" > /dev/null 2>&1
        rm "/etc/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
}

# Do initial setup of an installed service
#
# syntax: setup_init_script name

setup_init_script()
{
    self=setup_init_script
    name=$1
    spaces=`printf " %b" "\t"`
    check_system_type
    test $? -ne 1 || return 1
    test ! -z "$name" ||
        { log "$self: missing argument" && return 1; }
    if [ "$ro_INIT_TYPE" = "sysv" ]; then
        scriptname="/etc/init.d/$name"
    elif [ "$ro_INIT_TYPE" = "bsd" ]; then
        scriptname="/etc/rc.d/rc.$name"
    elif [ "$ro_INIT_TYPE" = "lfs" ]; then
        scriptname="/etc/rc.d/init.d/$name"
    elif [ "$ro_INIT_TYPE" = "pardus" ]; then
        scriptname="/usr/sbin/$name"
    else
        log "$self: error: unknown init type"
        return 1
    fi
    # Add the init script to the default runlevel
    # This is complicated by the fact that we need to pass older Debian-based
    # systems the start and stop order numbers, which we extract from the
    # Redhat chkconfig information.
    if test "$ro_INIT_TYPE" = "sysv" -a -r "$scriptname"; then
        orders=`grep '^#['"$spaces"']*chkconfig:' "$scriptname" |
            sed -e 's/^#['"$spaces"']*chkconfig:['"$spaces"']*[0-9]*['"$spaces"']*//'`
        expr "$orders" : '.*[0-9][0-9]*['"$spaces"']['"$spaces"']*[0-9][0-9]*$' > /dev/null ||
            {
                log "$self: bad or missing chkconfig line in init script $scriptname"
                return 1
            }
        # $orders is deliberately not quoted here
        addrunlevel "$name" $orders
    else
        addrunlevel "$name"
    fi
    test -r "$scriptname" &&
        grep -q '^#['"$spaces"']*setup_script['"$spaces"']*$' "$scriptname" &&
        "$scriptname" setup
    return 0
}

# Do pre-removal cleanup of an installed service
#
# syntax: cleanup_init_script name

cleanup_init()
{
    self=cleanup_init_script
    name=$1
    spaces=`printf " %b" "\t"`
    check_system_type
    test $? -ne 1 || return 1
    test ! -z "$name" || \
        { log "$self: missing argument" && return 1; }
    if [ "$ro_INIT_TYPE" = "sysv" ]; then
        scriptname="/etc/init.d/$name"
    elif [ "$ro_INIT_TYPE" = "bsd" ]; then
        scriptname="/etc/rc.d/rc.$name"
    elif [ "$ro_INIT_TYPE" = "lfs" ]; then
        scriptname="/etc/rc.d/init.d/$name"
    elif [ "$ro_INIT_TYPE" = "pardus" ]; then
        scriptname="/usr/sbin/$name"
    else
        log "$self: error: unknown init type"
        return 1
    fi
    test -r "$scriptname" &&
        grep -q '^#['"$spaces"']*cleanup_script['"$spaces"']*$' "$scriptname" &&
        "$scriptname" cleanup >> $ro_LOG_FILE 2>&1
    delrunlevel "$name"
    return 0
}


terminate_proc() {
    PROC_NAME=$1
    SERVER_PID=`pidof $PROC_NAME 2> /dev/null`
    if [ "$SERVER_PID" != "" ]; then
        killall -TERM $PROC_NAME > /dev/null 2>&1
        sleep 2
    fi
}


maybe_run_python_bindings_installer() {
    VBOX_INSTALL_PATH=$1

    PYTHON=python
    if [ ! `python -c 'print "test"' 2> /dev/null` = "test" ]; then
        echo  1>&2 "Python not available, skipping bindings installation."
        return 1
    fi

    echo  1>&2 "Python found: $PYTHON, installing bindings..."
    # Pass install path via environment
    export VBOX_INSTALL_PATH
    $SHELL -c "cd $VBOX_INSTALL_PATH/sdk/installer && $PYTHON vboxapisetup.py install"
    # remove files created during build
    rm -rf $VBOX_INSTALL_PATH/sdk/installer/build

    return 0
}
