summaryrefslogblamecommitdiffstats
path: root/build-initramfs.sh
blob: 4c280661c3c04161cad6fea8231a9c4170209a0e (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                       
               
                                                                  


                                                                                     
 

         


                                                                         
            
 

                                                                          



                                      
                                                                       
 
                     
                               
                                        


            
            
                 
                             
 

                                                 
 







                                                                                         





                                                               
 


                          
                                                                                                 
 
 






                                                                
 





















                                                                             
 









                                                                                  
                                                     
                                                  



                                                                    




                                                                     


                                                




                                                                                    








                                                                             
                                                
                                            

                                               
                                             
 
                                                        
                                            






                                                   
                                               
                                                                                 


                                                                                                
                    
                              



               



                                                                               






                         
                                     


                                               



                                                           









                                                                 
                               

                          
                                                                         





                                                                  


                                              
                      


                



                                                                                          




                               
                       












                                                          


                                                                    


 
                      
                   
 
 
                      




















                                                                 

                                                                                                      





















                                                              

                                                                                                 













                                                                 

                                                                                                                









                                                                 

                                                                                                   









                                                               

                                                                                                       







                                                
                                             

















                                                                                                          















                                                                                                         
 
                
 
            
                
                     
                                        










                                                                               
                      


                                                    
 
 


                                                           
           





                                                         
 
 
        



                                          

                                                        
                 













                                                                        









                                                                     

                                                          

                                                 



                                                      
 
                                      
                                              
                        
                              
          
 
                                 

                                                             
                                 
          
 

                                                                
                                                                  




                                                                                    



                                                                                            
                                      






                                                                                                              


                          
 
                                
                                        
                                              




                                                   

                                         
                                                        

                                                                 


                                                                            



                                                                            






                                                
                                                                    





                                                                       
          

                                                                   
 
 
         
#!/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
#			Jonathan Bauer (jonathan.bauer@rz.uni-freiburg.de) 19.09.2019
#			Thiago Abdo    (tjabdo@inf.ufpr.br) 06.11.2019

# 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
## endregion

# Afaik the idea here was to have the root dir where build-initramfs.sh
# is called from and the repo dir where the systemd-init.git is cloned to:
#	<root_dir>
#		 |- build-initramfs.sh
#		 |- dracut
#		 |- systemd-init
declare -rg _root_dir="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")")"

# TODO clean these up
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 -rg _repo_dir="${_root_dir}/systemd-init"
declare -rg _dracut_dir="${_root_dir}/dracut"

# Autodetect the kmod version present on the system to decide which dracut version to get
#  * v47 requires kmod >= 23 (Available in Ubuntu 18.04)
#  * v46 works with kmod == 20 (CentOS 7.5 only provides kmod v20)
declare -g _dracut_version="046"
if [ "$(pkg-config --modversion libkmod)" -ge 23 ]; then
	_dracut_version="047"
fi

declare -A core_repo=(
  [handler]="git"
     [path]="$_repo_dir"
      [url]="git://git.openslx.org/openslx-ng/systemd-init.git"
   [branch]="downloader"
)

declare -A core_dracut=(
  [handler]="http"
     [path]="$_dracut_dir"
      [url]="https://www.kernel.org/pub/linux/utils/boot/dracut/dracut-${_dracut_version}.tar.gz"
)

declare -A module_dnbd3=(
  [handler]="git"
     [path]="${_repo_dir}/modules.d/dnbd3-rootfs/binaries/dnbd3"
      [url]="git://git.openslx.org/dnbd3.git"
   [branch]="master"
   [commit]="c881c79"
)

declare -A module_qemu_xmount=(
  [handler]="git"
     [path]="${_repo_dir}/modules.d/dnbd3-rootfs/binaries/qemu-xmount"
      [url]="git://github.com/eaas-framework/qemu.git"
   [branch]="libxmount_input"
   [commit]="4873cd023da8511ed9792a318d1456c949046123"
)

declare -A module_xmount=(
  [handler]="git"
     [path]="${_repo_dir}/modules.d/dnbd3-rootfs/binaries/xmount"
      [url]="git://github.com/eaas-framework/xmount.git"
   [branch]="master"
   [commit]="0151375"
)

declare -A module_kernel_qcow2_linux=(
  [handler]="git"
      [url]="git://git.openslx.org/openslx/kernel-qcow2-linux.git"
     [path]="${_repo_dir}/modules.d/dnbd3-rootfs/binaries/kernel-qcow2-linux"
   [branch]="kernel-qcow2-linux-4.19.y"
)

declare -A module_kernel_qcow2_util_linux=(
  [handler]="git"
      [url]="git://git.openslx.org/openslx/kernel-qcow2-util-linux.git"
     [path]="${_repo_dir}/modules.d/dnbd3-rootfs/binaries/kernel-qcow2-util-linux"
   [branch]="kernel-qcow2"
)

declare -A override

bootstrap() {
	for module in "${!core_@}" "${!module_@}"; do
		if declare -n _ref="$module"; then
			echo "Only bash >= 4.3 supports namerefs." \
				"You are running ${BASH_VERSION}." \
				"Falling back to using evil eval..."
			eval 'declare -A _ref=(' \
				'[handler]=${'"$module"'[handler]}' \
				   '[path]=${'"$module"'[path]}' \
				    '[url]=${'"$module"'[url]}' \
				 '[branch]=${'"$module"'[branch]}' \
				 '[commit]=${'"$module"'[commit]}' \
				')'
			# phew that was ugly....
		fi
		if [ -n "$(ls -A "${_ref[path]}" 2> /dev/null)" ]; then
			echo "'${_ref[path]}' not empty, skipping..."
			continue
		fi
		echo "#########################  $module  #########################"
		if [ -n "${override["$module"]}" ]; then
			echo "Module overriden: ${override["$module"]}"
			IFS='|' read -r -a pairs <<< "${override["$module"]}"
			declare -p pairs
			for pair in "${pairs[@]}"; do
				IFS='=' read -r key value <<< "$pair"
				_ref["$key"]="$value"
			done
		fi
		echo "Handler: ${_ref[handler]}"
		echo "    URL: ${_ref[url]}"
		echo " Branch: ${_ref[branch]}"
		echo " Commit: ${_ref[commit]}"
		echo "   Path: ${_ref[path]}"

		# handlers deal with errors on their own
		handler_"${_ref[handler]}" \
			"${_ref[path]}" \
			"${_ref[url]}" \
			"${_ref[branch]}" \
			"${_ref[commit]}"

		# apply patches if any are required
		shopt -s nullglob
		pushd "${_ref[path]}" || exit 1
		for patch in "${_repo_dir}/patches/${_ref[path]##*/}/"*.patch; do
			if ! patch -p1  < "$patch"; then
				echo "Applying '$patch' failed, expecting errors. Continuing..."
			fi
		done
		popd || exit 1
	done
}

handler_git() {
	if ! hash git 2>/dev/null; then
		echo "'git' binary not found, please install it and try again."
		exit 1
	fi
	local path="$1"
	local url="$2"
	local branch="$3"
	local commit="$4"

	mkdir -p "$path"

	local gitargs=("--depth" "1")
	if [ -n "$branch" ]; then
		gitargs+=("--branch" "$branch")
	fi
	if ! git clone "${gitargs[@]}" "$url" "$path"; then
		echo "Error cloning '$url' to '$path'."
		exit 1
	fi

	[ -z "$commit" ] && return 0
	
	# make sure given commit in in the fetched history
	local revision="$branch"
	if [ -n "$commit" ]; then
		revision="$commit"
	fi

	# manual "shallow clone" since not all server allow it...
	pushd "$path" || exit 1
	local i=0
	local last_count=0
	while ! git rev-parse --quiet --verify "${revision}^{commit}"; do
		current_count="$(git rev-list --count --all)"
		if [ "$current_count" -eq "$last_count" ]; then
			echo "Failed to find '$revision' in '$2'."
			exit 1
		fi
		last_count="$current_count"
		git fetch --depth=$(( i+=50 ))
	done
	git reset --hard "$revision"
	popd || exit 1
}

handler_http() {
	if ! hash curl tar 2>/dev/null; then
		echo "'curl'/'tar' binaries not found, please install them and try again."
		exit 1
	fi
	local path="$1"
	local url="$2"

	mkdir --parents "$path"

	set -o pipefail
	if ! curl \
			--location \
			--max-redirs 5 \
			--max-time 7 \
			--connect-timeout 2 \
			--retry 3 \
			--retry-max-time 12 \
			"$url" \
			| tar \
				--extract \
				--gzip \
				--directory "$path" \
				--strip-components 1; then
		echo "Failed to download/extract '$url' to '$path'."
		exit 1
	fi
}


print_help_message() {
	echo "TODO"
}

parse_command_line() {
	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)
				local given_argument="$1"
				shift
				file_path="$1"
				if [[ "$file_path" == '' ]]; then
					echo \
						"Error with given option \"$given_argument\":" \
						"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)
				local given_argument="$1"
				shift
				target="$1"
				if [[ "$target" == '' ]]; then
					echo \
						"Error with given option \"$given_argument\":" \
						"This option needs a path create initramfs from."
					return 1
				fi
				shift
				;;
			-i|--init)
				shift
				initialize='yes'
				;;
			-k|--kernel-version)
				local given_argument="$1"
				shift
				kernel_version="$1"
				if [ -z "$kernel_version" ]; then
					echo \
						"Error with given option \"$given_argument\":" \
						"This option needs a kernel version to build the initramfs for."
					return 1
				fi
				shift
				;;
			-H|--kernel-headers)
				local given_argument="$1"
				shift
				kernel_headers="$1"
				if [ -z "$kernel_headers" ]; then
					echo \
						"Error with given option \"$given_argument\":" \
						"This option needs the path to the kernel headers."
					return 1
				fi
				shift
				;;
			-q|--qcow-handler)
				local given_argument="$1"
				shift
				qcow_handler="$1"
				if [ -z "$qcow_handler" ]; then
					echo \
						"Error with given option \"$given_argument\":" \
						"This options needs to be either 'xmount' or 'kernel'."
					return 1
				fi
				shift
				;;
			-u|--update)
				shift
				update='yes'
				;;
			-O|--override-module)
				local given_argument="$1"
				shift
				# expects in format '<module_name>.<key>=<value>[|<key>=<value>|...]'
				if ! [[ "$1" =~ ^[A-Za-z0-9_]+\.[A-Za-z0-9]+=.+$ ]]; then
					echo \
						"Error with given option \"$given_argument\":" \
						"'$1' not in expected format: <module_name>.<key>=<value>"
					return 1
				fi
				override_module="${1%%.*}"
				override_argument="${1#*.}"
				if [ -n "${override["$override_module"]}" ]; then
					# append to existing value
					override_argument="|${override_argument}"
				fi
				override["$override_module"]+="$override_argument"
				shift
				;;
			-)
				shift
				while [[ "$1" =~ ^.+$ ]]; do
					dracut_parameter+=("$1")
					shift
				done
				;;
			'')
				break
				;;
			*)
				echo \
					"Error with given option \"$1\": This argument is not available."
				return 1
		esac
	done

	return 0
}
## endregion
## region helper
initialize_dracut() {
	pushd "${_dracut_dir}" || exit 1
	# NOTE: On virtualbox shared folder symlinks are not allowed.
	# NOTE: make the dracut-install binary (dracut-install resolves
	# dependencies etc.)
	echo 'Compiling dracut.'
	./configure
	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 || exit 1
	cp "${_dracut_dir}/install/dracut-install" \
		"${_dracut_dir}/dracut-install"
	return $?
}

# The idea here was to source each module-setup.sh from our
# custom dracut modules and call their clean() function.
# TODO: Does this still work?
cleanup() {
	local plugin_path
	plugin_path="${_repo_dir}/modules.d/dnbd3-rootfs"
	source "${plugin_path}/module-setup.sh"
	moddir="$(cd "$plugin_path" &>/dev/null && pwd)"
	clean
	return $?
}

main() {
	if ! parse_command_line "$@"; then
		print_help_message "$0"
		exit 1
	fi

	# prepare remote modules and set core variables.
	bootstrap

	if [ "$full_cleanup" = "yes" ]; then
		echo 'Removing all modules.'
		rm "${_repo_dir}" "${_dracut_dir}" --recursive --force
		exit $?
	fi

	if [ "$cleanup" = "yes" ]; then
		echo 'Removing distribution specific files.'
		cleanup
		exit $?
	fi

	# Default to the running kernel's version if none were specified
	echo "Building for:"
	if [ -z "$kernel_version" ]; then
	   kernel_version="$(uname -r)"
	fi
	echo " * kernel version: $kernel_version"
	# similar for kernel headers needed to compile dnbd3 against.
	if [ -z "$kernel_headers" ]; then
	   kernel_headers="/lib/modules/${kernel_version}/build"
	fi
	if [ ! -f "${kernel_headers}/Makefile" ]; then
	   echo 'Missing kernel headers for given kernel!'
		 exit 1
	fi
	echo " * kernel headers: $kernel_headers"
	if [ -n "$qcow_handler" ]; then
		echo " * qcow2 handler: $qcow_handler"
		export qcow_handler
	fi

	if [ "$update" = "yes" ]; then
		pushd "${_repo_dir}" || exit 1
		git pull
		popd || exit 1
	fi

	echo 'Checking dracut...'
	if [ ! -f "${_dracut_dir}/dracut-install" ]; then
		echo "Dracut isn't available yet loading it."
		initialize_dracut
	fi

	for _dracut_module_dir in "${_repo_dir}/modules.d/"*; do
		[ -d "${_dracut_module_dir}" ] || continue
		_dracut_module="$(basename "$_dracut_module_dir")"
		# TODO allow module-specific priority
		_dracut_module_target="${_dracut_dir}/modules.d/00${_dracut_module}"
		if [[ ! -L "$_dracut_module_target" || "$(readlink \
			"$_dracut_module_target")" != "$_dracut_module_dir" ]]; then
			echo \
				"Link ${_dracut_module} plugin into dracut modules folder" \
				"($_dracut_module_dir -> $_dracut_module_target)."
			if ! ln --symbolic --force \
					"$_dracut_module_dir" "$_dracut_module_target"; then
				echo \
					"Failed to link: '$_dracut_module_dir' -> '$_dracut_module_target'." \
					"Trying to copy them directly."
				if ! cp --recursive --force --no-target-directory \
						"$_dracut_module_dir" "$_dracut_module_target"; then
					echo "Failed to copy them either, exiting."
					exit 1
				fi
			fi
		fi
	done

	# default dracut modules
	_modules=(dnbd3-rootfs conf-tgz)
	echo "Default modules: ${_modules[*]}"
	if [ "$verbose" = "yes" ]; then
		dracut_parameters+=("--verbose")
	fi
	if [ "$debug" = "yes" ]; then
		dracut_parameters+=("--stdlog" "4")
		_modules+=(i18n terminfo)
	fi
	if [ "$use_systemd_in_initramfs" = "yes" ]; then
		_modules+=(systemd systemd-initrd dracut-systemd)
	fi

	# Initialize and exit if we are only preparing the build environment
	. "${_repo_dir}/modules.d/dnbd3-rootfs/helper/build.inc"
	if ! build_initialize_components; then
		echo 'Failed to build required components for dnbd3-rootfs.'
		exit 1
	fi
	if [ "$initialize" = "yes" ]; then
		echo "Initialization succeeded."
		exit 0
	fi

	echo 'Building initramfs...'
	"${_dracut_dir}/dracut.sh" --local \
		--modules "${_modules[*]}" --conf /etc/dracut.conf \
		--confdir /etc/dracut.conf.d "${dracut_parameter[@]}" \
		--kver "${kernel_version}" "$file_path"
	_return_code=$?
	if [ "$_return_code" != 0 ]; then
		echo 'Building initramfs failed.'
		exit 1
	fi
	# NOTE: dracut generate the initramfs with 0600 permissions
	chmod 0644 "${file_path}"
}

main "$@"