summaryrefslogblamecommitdiffstats
path: root/builder/build-initramfs.sh
blob: 846663a44ff4a74a58f5de4126f27217554c654c (plain) (tree)
1
2
3
4
5
6
7
8

                       
               
                                                                  
                                                              


         


                                                                         






                                                                        
                          



                                                                               
                                                
              

                                                              
                                                               
                       
                                                                                                                                                                  
      
                                                          


                                                           
                                           

                           

                                                                                      

                                 
        
                    


                                                                  

              
            
                                                                           
                      
                   

                   
                       
           
                   
                                                                                          




                                        
            
                 
                             
                              
                            
                             
                                                                             


                                                                 
                                                             
                                                                    
                                     
                                  
                             
                                                                                              
                                  
                                                                             

                                                                                                                                    
                                                     
                                                       
                                                          
                                       

                                                                                                                      
           
                  
                                
                       




                                                       

                                                                         
          
   
 
                        




                                                                             
                      

                        
   
 
                                         




                                                                 

                                  
 
                                                              
 
                                                             
                        
 
                                                 
                            
 

                                                              

                                                                                                                      

                                                                             
                         
 

                                                                      
                                           
 
                                                                           
                                                             
                                                                             
               
   
 
                      




                                            
                                           
                            
                                 
                             
                                               
                                              

                 
                      




















































































                                                                  



                      
                                       



                         
                             


                       
                           
                  

                           

                                                 
                                      
                                                                                                            



                            

                         
                             
                  



                                  

                                          
                                              
                  

                        


                                              
                                                                                                       



                            


                                            
                                           



                         

               


                     
                                                                                                


                        
                                    
                              
      
                                  
                               
      
                                                                               
            



                                                                         

            
 
            
                
                    
                   




                                                                      
                                                                       
     
                  
 
                                                        



                                                                           
                                        

                                           

                                          






                                                                                           
                                             
                    

                     
         


                     
                   



                                  
                       
     



















                                                                                

                                                                       

             
           
                   



                                                  
             
     


                                                                    
         
             
 
            
           
                   
        


                                                                                                          

                                                                      
                                                           


                                                                             
                                  
 
                                
                          
 

                                  

          
 

                                                                          
                                                               
                                                                       
                                                

                                                         
                                                         




                                                         
                                                                         


                                                  
                                                 
      









                                                                              
                
                                    
                
                                                      

              












                                                                               



                                                                 








                                                                          




                                                                 
                               
                                                                          
                                                         
                     
  
 



                                                                                       
    
                  


                                                                                                            
                      
                                                                                                                                                                                        
                                                      

                                                                                    
      
  

                                               


                                
  



                                      
  

                                                              
  
                                       


                                                                   
                                    
                                                        
           

                                   


                                                                             


                                                                              

                                            

                                                               
                                                                            

                                                            
                                          
                                       


                                                               
                       
  
                     
            
           
                    

                                                     
           
#!/usr/bin/env bash
# -*- coding: utf-8 -*-
# region header
# Copyright Torben Sickert (info["~at~"]torben.website) 29.10.2015
#           Janosch Dobler (info["~at~"]jandob.com) 29.10.2015

# License
# -------
# This library written by Torben Sickert and Janosch Dobler stand under a
# creative commons naming 3.0 unported license.
# see http://creativecommons.org/licenses/by/3.0/deed.de
# This tool provides a generic way to install systemd based remote linux
# initramfs.

# Examples
# --------

# Start install progress command (Assuming internet is available):
# >>> ./build-initramfs.sh

# Note that you only get very necessary output until you provide "--verbose" as
# commandline options.

## region ensure presence of needed dependencies
set -o errexit
_needed_location="$(dirname "${BASH_SOURCE[0]}")/dnbd3-rootfs"
if ! [[ -d "$_needed_location" ]]; then
    echo "The dnbd3 dracut plugin isn't available, loading it."
    if ! hash git; then
        echo "Needed dependency \"git\" isn't available. Please install \"git\" or provide the repositories data structure in \"$(dirname "${BASH_SOURCE[0]}")\"."
    fi
    _temporary_repository_location="$(mktemp --directory)"
    git clone --depth 1 --no-single-branch \
        git://git.openslx.org/openslx-ng/systemd-init.git \
        "$_temporary_repository_location"
    pushd "$_temporary_repository_location"
    git submodule init
    git submodule foreach '
        branch="$(git config --file "$toplevel/.gitmodules" "submodule.$name.branch")"
        git clone --depth 1 --branch "$branch"
    '
    git submodule update --remote
    popd
    cp --recursive \
        "${_temporary_repository_location}/builder/dnbd3-rootfs" \
        "$_needed_location"
    rm --recursive --force "$_temporary_repository_location"
fi
set +o errexit
## endregion
source "$(dirname "${BASH_SOURCE[0]}")/dnbd3-rootfs/scripts/rebash/core.sh"
core.import exceptions
exceptions.activate
core.import logging
core.import utils
core.import change_root
# endregion
# region properties
dracut_resource_url='https://www.kernel.org/pub/linux/utils/boot/dracut/dracut-043.tar.gz'
file_path='/boot/initramfs.img'
dracut_parameter='--force --no-hostonly'
verbose='no'
debug='no'
target=''
cleanup='no'
full_cleanup='no'
use_systemd_in_initramfs='no'
declare -A core_dependencies=(
    [cat]='print messages' \
    [cpio]='pack initramfs' \
    ['dhclient arping']='support network connection in resulting initramfs' \
    [dirname]='core logic' \
    [dmsetup]='create a (temporary) writable layer during boot' \
    [grep]='retrieve right boot partition during boot' \
    [mktemp]='create save temporary files and dictionaries' \
    [readlink]="connect dracut module with dracut's module system" \
    [rm]='remove (temporary) files' \
    [shift]='parse command line' \
    [sed]='process strings' \
    ['cmake gcc make']='dynamically compile needed resources against current or given kernel')
declare -A optional_dependencies=(
    [chroot]='build against a distribution other than this program runs in' \
    ['curl git gzip tar']='dynamically retrieve and unpack missing application which will be compiled for current or given kernel' \
    [qemu-img]='support template systems in container (usually used by virtual runtime environments')
declare -A core_shared_library_pattern_dependencies=(
    [libz]='compile dnbd3 for given or current kernel')
declare -A optional_shared_library_pattern_dependencies=()
declare -A core_package_dependencies=()
declare -A optional_package_dependencies=(
    ['fuse glib-2.0 pixman-1']='support template systems in container (usually used by virtual runtime environments)')
# endregion
# region functions
## region command line interface
print_usage_message() {
    local __doc__='
    Prints a description about how to use this program.

    >>> print_usage_message &>/dev/null
    '
    logging.cat << EOF
This program provides a generic way to install systemd based remote linux
initramfs.
EOF
}
print_usage_examples() {
    local __doc__='
    Prints a description about how to use this program by providing examples.

    >>> print_usage_examples &>/dev/null
    '
    logging.cat << EOF
Start install progress:
>>> ./build_initramfs.sh
EOF
}
print_command_line_option_description() {
    local __doc__='
    Prints descriptions about each available command line option.

    >>> print_command_line_option_description &>/dev/null
    '
    logging.cat << EOF
-h --help Shows this help message.

-v --verbose Tells you what is going on (default: "$verbose").

-d --debug Gives you any output from all tools which are used
    (default: "$debug").

-p --file-path Target location for initramfs file
    (default: "$file_path").

-c --cleanup Removes all distribution specific compiled files.

-f --full-cleanup Removes all retrieved and compiled files (usefull to retrieve latest version of all nested modules).

-t --target Creates an image against given target template filesystem. If not
    explicitly speicifed current system will be used as template system
    (default: "$target").

-s --use-systemd-in-initramfs Use Systemd as init process in initramfs
    (improved performance but less features)
    (default: "$use_systemd_in_initramfs").

Additional dracut parameter and normal parameter can be added by delimiting
them via a single dash (-) (default: "$dracut_parameter" with
"--modules" and dynamically determined modules. Additional custom modules are
concatenated.).
EOF
}
print_help_message() {
    local __doc__='
    Provides a help message for this module.

    >>> print_help_message &>/dev/null
    '
    logging.plain "\nUsage: $0 [options]\n"
    print_usage_message "$@"
    logging.plain '\nExamples:\n'
    print_usage_examples "$@"
    logging.plain -e '\nOption descriptions:\n'
    print_command_line_option_description "$@"
    logging.plain
}
parse_command_line() {
    local __doc__='
    Provides the command line interface and interactive questions.

    >>> parse_command_line -h &>/dev/null

    >>> echo "$verbose"
    no
    >>> logging.get_level
    critical
    >>> parse_command_line -v
    >>> echo "$verbose"
    >>> logging.get_level
    yes
    info

    >>> echo "$debug"
    no
    >>> parse_command_line --debug
    >>> echo "$debug"
    >>> logging.get_level
    yes
    debug

    >>> parse_command_line -p; echo $?
    +doc_test_contains
    Error with given option "-p":
    +doc_test_ellipsis
    Traceback (most recent call first):
    ...

    >>> echo "$file_path"
    /boot/initramfs.img
    >>> parse_command_line -p /tmp/test.img
    >>> echo "$file_path"
    /tmp/test.img

    >>> echo "$cleanup"
    no
    >>> parse_command_line --cleanup
    >>> echo "$cleanup"
    yes

    >>> echo "$full_cleanup"
    no
    >>> parse_command_line --full-cleanup
    >>> echo "$full_cleanup"
    yes

    >>> echo "$full_cleanup"
    no
    >>> parse_command_line --full-cleanup
    >>> echo "$full_cleanup"
    yes

    >>> echo "$use_systemd_in_initramfs"
    no
    >>> parse_command_line -s
    >>> echo "$use_systemd_in_initramfs"
    yes

    >>> parse_command_line -t; echo $?
    +doc_test_contains
    Error with given option "-t":
    +doc_test_ellipsis
    Traceback (most recent call first):
    ...

    >>> [[ "$target" = "" ]]
    >>> parse_command_line -t /tmp/
    >>> echo "$target"
    /tmp/

    >>> echo "$dracut_parameter"
    --force --no-hostonly
    >>> parse_command_line - --test
    >>> echo "$dracut_parameter"
    --force --no-hostonly --test

    >>> parse_command_line --no-available-option; echo $?
    +doc_test_contains
    Error with given option "--no-available-option":
    +doc_test_ellipsis
    Traceback (most recent call first):
    ...
    '
    while true; do
        case "$1" in
            -h|--help)
                shift
                print_help_message "$0"
                exit 0
                ;;
            -v|--verbose)
                shift
                verbose='yes'
                ;;
            -d|--debug)
                shift
                debug='yes'
                ;;
            -p|--file-path)
                shift
                file_path="$1"
                if [[ "$file_path" == '' ]]; then
                    logging.critical \
                        "Error with given option "$1": This option needs a path to save initramfs image to."
                    return 1
                fi
                shift
                ;;
            -c|--cleanup)
                shift
                cleanup='yes'
                ;;
            -f|--full-cleanup)
                shift
                full_cleanup='yes'
                ;;
            -s|--use-systemd-in-initramfs)
                shift
                use_systemd_in_initramfs='yes'
                ;;
            -t|--target)
                shift
                target="$1"
                if [[ "$target" == '' ]]; then
                    logging.critical \
                        "Error with given option "$1": This option needs a path create initramfs from."
                    return 1
                fi
                shift
                ;;
            -)
                shift
                while [[ "$1" =~ ^.+$ ]]; do
                    dracut_parameter+=" $1"
                    shift
                done
                shift
                ;;

            '')
                break
                ;;
            *)
                logging.critical "Error with given option "$1": This argument is not available."
                return 1
        esac
    done
    if [ "$verbose" == 'yes' ]; then
        logging.set_level info
    fi
    if [ "$debug" == 'yes' ]; then
        logging.set_level debug
    fi
    # NOTE: Remove the following line if this sanity check should be performed.
    return 0
    if [[ "$UID" != '0' ]]; then
        logging.critical \
            "You have to run this script as \"root\" not as \"${USER}\"."
        exit 2
    fi
    return 0
}
## endregion
## region helper
dependency_check() {
    local __doc__='
    Check for given dependencies with given dependency checker and log
    corresponding messages.

    Example:

    `dependency_check core dependencies utils_dependency_check program`
    '
    local result=0

    eval 'for dependency_group in "${!'$1'_'$2'[@]}"; do
        # NOTE: If "dependency_check_result" would be marked as local it is
        # empty later.
        dependency_check_result="$($3 $dependency_group)" || \
            local return_code=$?
        if [[ $return_code == 1 ]]; then
            echo "$dependency_check_result"
            return $return_code
        elif [[ $return_code == 2 ]]; then
            while read dependency; do
                eval "local reason=\${${1}_${2}[\"\$dependency_group\"]}"
                local message="Missing $1 $4 dependency \"$dependency\" needed to $reason."
                if [[ $1 == core ]]; then
                    logging.critical "$message"
                else
                    logging.warn "$message"
                fi
            done <<< $dependency_check_result
            result=2
        fi
        return_code=0
    done'
    return $result
}
initialize_dracut() {
    local __doc__='
    Downloads and compiles dracut.

    Example:

    `initialize_dracut`
    '
    if [[ ! -f "$(dirname "${BASH_SOURCE[0]}")/dracut/install/dracut-install" ]]
    then
        mkdir --parents "$(dirname "${BASH_SOURCE[0]}")/dracut"
        logging.info 'Download and extract dracut.'
        curl --location "$dracut_resource_url" | tar --extract --gzip \
            --directory "$(dirname "${BASH_SOURCE[0]}")/dracut" \
            --strip-components 1
        pushd "$(dirname "${BASH_SOURCE[0]}")/dracut"
        # NOTE: On virtualbox shared folder symlinks are not allowed.
        # NOTE: make the dracut-install binary (dracut-install resolves
        # dependencies etc.)
        logging.info 'Compiling dracut.'
        make install/dracut-install
        # NOTE: We have to copy the binary to current instead of symlinking
        # them since this feature isn't supported in shared virtual box machine
        # folders.
        # If symlinks would be available we could simply use:
        # >>> make dracut-install
        popd
    fi
    cp "$(dirname "${BASH_SOURCE[0]}")/dracut/install/dracut-install" \
        "$(dirname "${BASH_SOURCE[0]}")/dracut/dracut-install"
    return $?
}
cleanup() {
    local __doc__='
    Removes distribution specific generated files.

    Example:

    `cleanup`
    '
    local plugin_path="$(dirname "${BASH_SOURCE[0]}")/dnbd3-rootfs/"
    source "${plugin_path}module-setup.sh"
    moddir="$(cd "$plugin_path" &>/dev/null && pwd)"
    clean
    return $?
}
## endregion
# endregion
# region controller
result=0
if ! [ -f "/lib/modules/$(uname --kernel-release)/build/Makefile" ];then
    logging.critical 'Missing core dependency "linux-headers" to compile against given or current kernel.'
fi
dependency_check core dependencies utils_dependency_check program || \
    result=$?
dependency_check core shared_library_pattern_dependencies \
    utils_dependency_check_shared_library 'shared library' || result=$?
dependency_check core package_dependencies utils_dependency_check_pkgconfig \
    package || result=$?
[[ $result == 0 ]] || exit $result

logging.set_commands_level debug
logging.set_level critical

if ! parse_command_line "$@"; then
    print_help_message "$0"
    exit 1
fi

dependency_check optional dependencies utils_dependency_check program || \
    result=$?
dependency_check optional shared_library_pattern_dependencies \
    utils_dependency_check_shared_library 'shared library' || result=$?
dependency_check optional package_dependencies \
    utils_dependency_check_pkgconfig package || result=$?
[[ $result == 1 ]] && exit $result
## region handle delegated operations to specified target
if [[ "$target" != '' ]]; then
    _target="$target"
    if [[ -f "$target" ]]; then
        _target="$(mktemp --directory)"
        _xmount_mountpoint_target="$(mktemp --directory)"
        xmount --in qemu "$target" --out raw "$_xmount_mountpoint_target"
        _xmount_device_target="$(losetup --find)"
        losetup "$_xmount_device_target" loopdev \
            "${_xmount_mountpoint_target}/"*.dd
        mount "$_xmount_device_target" "$_target"
    fi
    if [[ -d "$_target" ]]; then
        _temporary_working_directory="$(chroot "$_target" mktemp --directory)"
        mount --bind "$(pwd)" "${_target}${_temporary_working_directory}"
        _parameter_skip=false
        _parameter_to_forward=()
        for _parameter; do
            if $_parameter_skip; then
                _parameter_skip=false
            elif [[ "$_parameter" == '-t' ]] || \
                 [[ "$_parameter" == '--target' ]]
            then
                _parameter_skip=true
            else
                _parameter_to_forward+=("$_parameter")
            fi
        done
        # NOTE: We would have to temporary patch dracut to avoid removing the
        # environment variables "LD_LIBRARY_PATH" and "LD_PRELOAD" to get
        # "fakechroot" working with dracut. So we should remove this variables
        # before running the patched dracut version to follow the initial
        # intention. You should first do:
        # >>> unset LD_LIBRARY_PATH
        # >>> unset LD_PRELOAD
        # and patch "dracut.sh" temporary to comment out:
        # >>> unset LD_LIBRARY_PATH
        # >>> unset LD_PRELOAD
        # To avoid to broke the "fakechroot" environment pipe the ldconfig call
        # to the native one:
        # >>> FAKECHROOT_CMD_SUBST=/usr/bin/ldconfig=/usr/bin/ldconfig
        change_root "${_target}" \
            "${_temporary_working_directory}/${BASH_SOURCE[0]}" \
            ${_parameter_to_forward[*]}
        mv "${_target}/$file_path" "$file_path"
        # Do cleanup procedures.
        umount "${_target}${_temporary_working_directory}"
        rm --recursive --force "${_target}${_temporary_working_directory}"
        if [[ -f "$target" ]]; then
            umount "$_target"
            losetup --detach "$_xmount_device_target"
            umount "$_xmount_mountpoint_target"
            rm --recursive --force "$_target" "$_xmount_mountpoint_target"
        fi
    fi
    exit 0
fi
## endregion
## region handle dependencies which can be resolved automatically
logging.info 'Checking dracut.'
if [[ ! -f "$(dirname "${BASH_SOURCE[0]}")/dracut/dracut-install" ]]; then
    logging.info "Dracut isn't available yet loading it."
    initialize_dracut
fi

_dracut_modules_source='../../dnbd3-rootfs'
_dracut_modules_target="$(dirname "${BASH_SOURCE[0]}")/dracut/modules.d/90dnbd3-rootfs"
if [[ ! -L "$_dracut_modules_target" || "$(readlink \
    "$_dracut_modules_target")" != "$_dracut_modules_source" ]]
then
    logging.info \
        "Link dnbd3 plugin into dracut modules folder ($_dracut_modules_source -> $_dracut_modules_target)."
    if ! ln --symbolic --force "$_dracut_modules_source" \
    "$_dracut_modules_target" 2>/dev/null; then
        logging.warn \
            "Link \"$_dracut_modules_source\" to \"$_dracut_modules_target\" fails. We will copy them. So we have to recopy it every time to ensure that recompiled things take effect."
        cp --recursive --force --no-target-directory \
            "$(dirname "${BASH_SOURCE[0]}")/$(basename "$_dracut_modules_source")" \
            "$_dracut_modules_target"
    fi
fi
## endregion
## region prepare and perform final dracut call
_loglevel=''
if [ "$verbose" == 'yes' ]; then
    _loglevel='--verbose'
fi
_modules='dnbd3-rootfs'
if [ "$debug" == 'yes' ]; then
    _loglevel="$_loglevel --stdlog 4"
    _modules="$_modules i18n terminfo"
fi
if [ "$use_systemd_in_initramfs" == 'yes' ]; then
    _modules="$_modules systemd systemd-initrd dracut-systemd"
fi
if [[ "$full_cleanup" == 'yes' ]]; then
    logging.info 'Removing all modules.'
    rm "$(dirname "${BASH_SOURCE[0]}")/dnbd3-rootfs" \
        "$(dirname "${BASH_SOURCE[0]}")/dracut" --recursive --force
elif [[ "$cleanup" == 'yes' ]]; then
    logging.info 'Removing distribution specific files.'
    cleanup
else
    logging.info 'Build initramfs.'
    # NOTE: We deactivate our exception handle since dracut returns "1" if it
    # is launched with help parameter ("-h" or "--help").
    exceptions.deactivate
    # NOTE: We temporary allow dracut to forward all messages since we forward
    # all logging configurations.
    _commands_log_level_backup="$(logging.get_commands_level)"
    _log_level_backup="$(logging.get_level)"
    logging.set_level debug
    logging.set_commands_level debug
    "$(dirname "${BASH_SOURCE[0]}")/dracut/dracut.sh" --local \
        $_loglevel --modules "$_modules" ${dracut_parameter[*]} "$file_path"
    _return_code=$?
    logging.set_commands_level "$_commands_log_level_backup"
    logging.set_level "$_log_level_backup"
    if [[ "$_return_code" != 0 ]]; then
        logging.error 'Building initial ram file system fails.'
        exit 1
    fi
    exceptions.activate
fi
exceptions.deactivate
## endregion
# endregion
# region vim modline
# vim: set tabstop=4 shiftwidth=4 expandtab:
# vim: foldmethod=marker foldmarker=region,endregion:
# endregion