Meet /etc/preinit again

*/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.

# Copyright (C) 2006-2016
# Copyright (C) 2010 Vertical Communications

[ -z "$PREINIT" ] && exec /sbin/init

export PATH="%PATH%"





. /lib/
. /lib/functions/
. /lib/functions/

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

boot_run_hook preinit_essential


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/ 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

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() {
function_name2() {
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

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
This entry was posted in Ci40, LEDE, OpenWRT. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s