*/etc/preinit is the init program after Linux kernel booted. It is briefly introduced in OpenWRT/LEDE: System Boot Sequence. This time I will meet it again and dive into it to search some internals.
As a reference, the code of preinit is shown below. And it is assumed that $PREINIT is already initialized.
#!/bin/sh # Copyright (C) 2006-2016 OpenWrt.org # Copyright (C) 2010 Vertical Communications [ -z "$PREINIT" ] && exec /sbin/init export PATH="%PATH%" pi_ifname= pi_ip=192.168.1.1 pi_broadcast=192.168.1.255 pi_netmask=255.255.255.0 fs_failsafe_ifname= fs_failsafe_ip=192.168.1.1 fs_failsafe_broadcast=192.168.1.255 fs_failsafe_netmask=255.255.255.0 fs_failsafe_wait_timeout=2 pi_suppress_stderr="y" pi_init_suppress_stderr="y" pi_init_path="%PATH%" pi_init_cmd="/sbin/init" . /lib/functions.sh . /lib/functions/preinit.sh . /lib/functions/system.sh boot_hook_init preinit_essential boot_hook_init preinit_main boot_hook_init failsafe boot_hook_init initramfs boot_hook_init preinit_mount_root for pi_source_file in /lib/preinit/*; do . $pi_source_file done boot_run_hook preinit_essential pi_mount_skip_next=false pi_jffs2_mount_success=false pi_failsafe_net_message=false boot_run_hook preinit_main
The first part includes three group of parameters. They are initilized for the functions which are located in hook list.
Then we come to five hook lists which are initilized by boot_hook_init which is located in /lib/function/boot.sh. The code of boot_hook_init is show below.
boot_hook_init() { local hook="${1}_hook" # add suffix _hook to every hook lister name export -n "PI_STACK_LIST=${PI_STACK_LIST:+$PI_STACK_LIST }$hook" export -n "$hook=" } The point of this function is the command export -n. The option -n means remove the export property from each NAME. It marks each NAME not export to the environment and child-env, but other functions in this shell can still use this variable.
for pi_source_file in /lib/preinit/*; do . $pi_source_file done }
preinit is then looking for and excute every files located /lib/preinit/. The name of such file is starting with a number and then the name of function. Each file has similar structure as
function_name1() { #STATEMENTS } ... function_name2() { #STATEMENTS } boot_hook_add hook_name function_name1 boot_hook_add hook_name function_name2 There can be multiple functions and later using boot_hook_add to add into a certain hook linker. So what happens in boot_hook_add is shown below
boot_hook_add() { local hook="${1}_hook${PI_HOOK_SPLICE:+_splice}" local func="${2}" [ -n "$func" ] && { local v; eval "v=\$$hook" export -n "$hook=${v:+$v }$func" # add function to responding hook list } } After adding functions, preinit excutes the functions located in preinit_essential by
boot_run_hook preinit_essential
here the definition of boot_run_hook is shown below
boot_run_hook() { local hook="$1" local func while boot_hook_shift "$hook" func; do local ran; eval "ran=\$PI_RAN_$func" [ -n "$ran" ] || { export -n "PI_RAN_$func=1" $func "$1" "$2" # excute the function } done } Here boot_hook_shift loops every functions in “$hook” until the last one. It returns 0 as true when there is still functions in “$hook” and returns 1 as false when reaching the end. The code of boot_hook_shift is shown below:
boot_hook_shift() { local hook="${1}_hook" local rvar="${2}" local v; eval "v=\$$hook" # $$hook includes all the registered functions [ -n "$v" ] && { local first="${v%% *}" # take the first function every time [ "$v" != "${v#* }" ] && \ export -n "$hook=${v#* }" || \ export -n "$hook=" export -n "$rvar=$first" # export the first function for other ones return 0 } return 1 }