summaryrefslogblamecommitdiffstats
path: root/build-initramfs.sh
blob: 188369e494c4572561711c6c4823ee08718c6e62 (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'
verbose='no'
debug='no'
cleanup='no'
full_cleanup='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
#  * v59 requires kmod >= 23 (Available in Ubuntu 18.04)
#  * v46 works with kmod == 20 (CentOS 7.5 only provides kmod v20)
declare -g _dracut_version="059"
if [ "$(pkg-config --modversion libkmod)" -lt 23 ]; then
	_dracut_version="046"
fi

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

declare -A core_dracut=(
  [handler]="http"
     [path]="$_dracut_dir"
      [url]="https://github.com/dracutdevs/dracut/archive/refs/tags/${_dracut_version}.tar.gz"
)

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

declare -A module_xloop=(
  [handler]="git"
     [path]="${_repo_dir}/modules.d/dnbd3-rootfs/binaries/xloop"
      [url]="git://git.openslx.org/openslx-ng/xloop.git"
)

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

declare -A module_xmount=(
  [handler]="http"
     [path]="${_repo_dir}/modules.d/dnbd3-rootfs/binaries/xmount"
      [url]="https://github.com/eaas-framework/xmount/tarball/015137556fce1e21273f198ae0b9158157f74f74"
)


declare -A override

bootstrap() {
	for module in "core_repo" "core_dracut" "${!module_@}"; do
		declare -n _ref="$module"
		if [ $? -ne 0 ]; 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
		pushd "${_ref[path]}" || exit 1
		for patch in "${_repo_dir}/patches/${_ref[path]##*/}/"*.patch; do
			[ -s "$patch" ] || continue
			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=()
	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 60 \
			--connect-timeout 5 \
			--retry 3 \
			--retry-max-time 120 \
			"$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)
				print_help_message "$0"
				exit 0
				;;
			-v|--verbose)
				verbose='yes'
				;;
			-d|--debug)
				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
				;;
			-c|--cleanup)
				cleanup='yes'
				;;
			-f|--full-cleanup)
				full_cleanup='yes'
				;;
			-s|--use-systemd-in-initramfs)
				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
				;;
			-i|--init)
				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
				;;
			-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
				;;
			-q|--qcow-handler)
				local given_argument="$1"
				shift
				qcow_handler="$1"
				;;
			-u|--update)
				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
				dracut_parameter+=( "$@" )
				break
				;;
			*)
				if [ -n "$1" ]; then
					echo \
						"Error with given option \"$1\": This argument is not available."
					return 1
				else
					break
				fi
				;;
		esac
		shift
	done

	if [ "$qcow_handler" = "xmount" ]; then
		unset module_xloop
		echo "Error: xmount support has been removed."
		return 1
	elif [ "$qcow_handler" = "xloop" ]; then
		unset module_xmount
	else
		echo \
			"Error with given option '--qcow-handler/-q':" \
			"This option needs to be 'xloop', given: '$qcow_handler'."
		return 1
	fi

	return 0
}
## endregion
## region helper
initialize_dracut() {
	pushd "${_dracut_dir}" || exit 1
	echo 'Compiling dracut.'
	./configure || exit 1
	make dracut-install || exit 1
	make dracut-util
	# add a few handy functions to dracut
	if ! [ -s dracut-init.sh ]; then
		echo "no dracut-init.sh"
		exit 1
	fi
	# One tab then 4 spaces, tab will get removed by -EOF
	cat >> dracut-init.sh <<-"EOF"

	slx_service() {
	    local _name _desc _tmpfile
	    declare -a _before=() _after=() _requires=() _wants=()
	    _name="$1"
	    _desc="$2"
	    shift 2
	    while (( $# > 0 )); do
	        case "$1" in
	            --wbefore)
	                _wants+=("$2")
	                ;&
	            --before)
	                _before+=("$2")
	                ;;
	            --wafter)
	                _wants+=("$2")
	                ;&
	            --after)
	                _after+=("$2")
	                ;;
	            --wants)
	                _wants+=("$2")
	                ;;
	            --requires)
	                _requires+=("$2")
	                ;;
	            *)
	                dfatal "Invalid option: '$1'"
	                exit 10
	        esac
	        shift 2
	    done
	    if [ -z "$_desc" ]; then
	        dfatal "Cannot install service without description"
	        exit 1
	    fi
	    mkdir --parents "${initdir}/usr/local/slx-services"
	    _tmpfile="/tmp/dracut-service-$RANDOM"
	    inst "$moddir/hooks/${_name}.sh" \
	            "/usr/local/slx-services/${_name}.sh" || exit 10
	    {
	        echo "[Unit]"
	        echo "Description=${_desc}"
	        echo "DefaultDependencies=no"
	        echo "Before=initrd-switch-root.target initrd-cleanup.service"
	        [ -n "${_wants}" ] && echo "Wants=${_wants[*]}"
	        [ -n "${_requires}" ] && echo "Requires=${_requires[*]}"
	        [ -n "${_before}" ] && echo "Before=${_before[*]}"
	        [ -n "${_after}" ] && echo "After=${_after[*]}"
	        echo ""
	        echo "[Service]"
	        echo "Type=oneshot"
	        echo "RemainAfterExit=yes"
	        echo "ExecStart=/usr/local/slx-services/${_name}.sh"
	        echo "KillMode=process"
	        echo "KillSignal=SIGHUP"
	    } > "${_tmpfile}"
	    inst_simple "${_tmpfile}" \
	            "${systemdsystemunitdir}/${_name}.service" || exit 10
	    rm -f -- "${_tmpfile}"
	    mkdir --parents \
	            "${initdir}/${systemdsystemunitdir}/initrd.target.wants" || exit 10
	    ln_r "${systemdsystemunitdir}/${_name}.service" \
	            "${systemdsystemunitdir}/initrd.target.wants/${_name}.service" || exit 10
	}
	EOF
	popd || exit 1
	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
	# Newer versions want these to exist
	mkdir -p /etc/dracut.conf.d
	touch /etc/dracut.conf

	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 parameters and modules
	dracut_parameter+=(
		--force
		--no-hostonly
	)
	dracut_modules=(
		busybox
		dnbd3-rootfs
		conf-tgz
		haveged
		kexec-reboot
		systemd
		systemd-initrd
		dracut-systemd
	)
	echo "Default modules: ${dracut_modules[*]}"
	if [ "$verbose" = "yes" ]; then
		dracut_parameters+=("--verbose")
	fi
	if [ "$debug" = "yes" ]; then
		dracut_parameters+=("--stdlog" "4")
		dracut_modules+=(i18n terminfo copy-initrd)
	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

	# ask password shice wegkloppen
	sed -r -i 's/\S*systemd\S*-ask-password[.a-z0-9_/-]*//g' "${_dracut_dir}/modules.d/00systemd/module-setup.sh"

	echo 'Building initramfs...'
	"${_dracut_dir}/dracut.sh" --local \
		--modules "${dracut_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 "$@"