でびあんの initrd (4)

昨晩の続きを。
次は get_prereqs() 手続き。

get_prereqs()
{
	set_initlist
	for gp_x in ${initlist}; do
		tmp=$(${initdir}/${gp_x} prereqs)
		eval array_${gp_x}=\"${tmp}\"
	done
}

ええと、set_initlist は

set_initlist()
{
	unset initlist
	for si_x in ${initdir}/*; do
		if [ ! -x ${si_x} ]; then
			continue
		fi
		initlist="${initlist} ${si_x#${initdir}/}"
	done
}

なカンジで initdir の中にある実行属性が付いてるファイル名のリストを initlist にセットする手続きになっている模様。initlist を初期化した後、何をしているか、というと

  • initlist から要素を一つづつ取り出して gp_x に格納しつつ以下
    • tmp に initdir/gp_x prereqs の実行結果 (というか標準出力) を格納
    • array_* (ファイル名) に tmp の値を格納

という事は initdir の中にあるファイル名な変数がそれだけ分できるのか。例えばデフォルトの local-top だと

scripts/local-top/udev_helper
scripts/local-top/mdrun
scripts/local-top/lvm

みたいなカンジになっていますが、作成されるシェル変数は

array_udev_helper
array_mdrun
array_lvm

なソレ達ができて、それぞれの値は prereqs 付けて実行した出力が入ってる、と。
あと、eval の使い方も参考になります。例えば

		eval array_${gp_x}=\"${tmp}\"

だと先に変数が値に展開されてナニ。lvm を例に考えたらまず

		eval array_lvm=\"mdadm mdrun lvm2\"

になって eval されるはず。なかなか凄い。

ぢつは

ここまでイメージできた時点で何しようとしてるのか、がようやくイメージできてたり。呼び出しの優先順位を設定できるんですな。なんて賢いんでしょ。全部シェルスクリプトでヤッツケてしまっているあたりもなかなか。

閑話休題

ちなみに scripts/local-top 配下で試した結果が以下です。

$ ./scripts/local-top/udev_helper prereqs

$ ./scripts/local-top/mdrun prereqs
udev_helper
$ ./scripts/local-top/lvm prereqs
mdadm mdrun lvm2
$

どっちが先か、は reduce を確認しないと分かりません。ので早速見ていくことに。とりあえず reduce_rereqs() 手続きが以下。

# This function generates the runlist, so we clear it first.
reduce_prereqs()
{
	unset runlist
	set_initlist
	set -- ${initlist}
	i=$#
	# Loop until there's no more in the queue to loop through
	while [ ${i} -ne 0 ]; do
		oldi=${i}
		for rp_x in ${initlist}; do
			reduce_satisfied ${rp_x}
			count_unsatisfied $(render array_${rp_x})
			cnt=${?}
			if [ ${cnt} -eq 0 ]; then
				runlist="${runlist} ${rp_x}"
				pop_list_item ${rp_x} ${initlist}
				initlist=${tmppop}
				i=$((${i} - 1))
			fi
		done
		if [ ${i} -eq ${oldi} ]; then
			panic "PANIC: Circular dependancy.  Exiting."
		fi
	done
}

こいつが call_scripts() 手続きで使っている runlist を作る模様。set_initlist は略しますが再度 initlist 作り直しているみたい。で、次の

	set -- ${initlist}
	i=$#

もなかなか凄い。man bash によると

オプションが処理された後に残っている引数があれば、これは位置パラメータの値として扱われ、$1, $2, ... $n の順に代入されます。

とある。知りませなんだ。しかし具体的にどうやって優先順位を解決しているんでしょうか。ちょっと集中力切れたんで頼まれもののお使いに行ってまいります。

続き

帰宅。昼飯の支度をする準備完了。同居人が病院に行っているので戻るまででどこまで掘れるか。ちょっと reduce_prereqs() 手続き長いんでちょっとづつ引用しながらメモな方式で。
まず、先頭部分ですが

reduce_prereqs()
{
	unset runlist
	set_initlist
	set -- ${initlist}
	i=$#

set_initlist して_位置パラメータ_にセット。セットされたパラメータの個数を i に代入してますな。
で、次、後全部引用。

	# Loop until there's no more in the queue to loop through
	while [ ${i} -ne 0 ]; do
		oldi=${i}
		for rp_x in ${initlist}; do
			reduce_satisfied ${rp_x}
			count_unsatisfied $(render array_${rp_x})
			cnt=${?}
			if [ ${cnt} -eq 0 ]; then
				runlist="${runlist} ${rp_x}"
				pop_list_item ${rp_x} ${initlist}
				initlist=${tmppop}
				i=$((${i} - 1))
			fi
		done
		if [ ${i} -eq ${oldi} ]; then
			panic "PANIC: Circular dependancy.  Exiting."
		fi
	done
  • i が 0 になるまで繰り返し。
    • oldi に i を退避
    • initlist の要素を rp_x に順に取り出しつつ以下
      • reduce_satisfied を rp_x を引数に呼び出し
      • count_unsatisfied を render array_${rp_x} の出力を引数に呼び出し
      • cnt に count_unsatisfied の終了ステイタスをセット
      • cnt が 0 なら云々
    • i と oldi が同じ値なら panic

というカンジですな。ちなみに略した部分は

  • cnt が 0 なら以下
    • runlist に rp_x を追加
    • pop_list_item を rp_x と initlist を引数に呼び出し
    • initlist に tmppop を代入
    • i に i - 1 を代入

上記は runlist への追加と initlist の再設定 (追加された要素の除去) ですな。ちょっとまだ i が減る仕組みと panic が出力する "Circular dependancy" というメセジのナニが微妙。
# panic が出力するナニはなんとなく想像できますが

もう少し

reduce_satisfied() 手続きは以下。

reduce_satisfied()
{
	deplist="$(render array_${1})"
	unset tmpdeplist
	for rs_y in ${deplist}; do
		if [ ! -f ${initdir}/${rs_y} ]; then
			continue
		fi
		tmpdeplist="${tmpdeplist} ${rs_y}"
	done
	deplist=${tmpdeplist}
	for rs_x in ${runlist}; do
		pop_list_item ${rs_x} ${deplist}
		deplist=${tmppop}
	done
	eval array_${1}=\"${deplist}\"
}

render は渡された引数を echo するソレですので、deplist には array_* の中身が格納される格好。リストの要素が initdir に存在するかを確認してリストを再設定 (あるもののみが残る) している模様。
その上で、runlist を走査して云々してますが微妙に読みづらい。
ええと整理してみるに reduce_prereqs() 手続きは

	while [ ${i} -ne 0 ]; do
	        # 略
		for rp_x in ${initlist}; do
                        #略
                done
		# 略
        done

みたいな二重ループになってて、内側の for で最低一つは i の値が減らなければならない、のが前提に見える。i の値が減るのは

  • reduce_satisfied ${rp_x} して
  • count_unsatisfied $(render array_${rp_x}) して
  • cnt=${?} で count_unsatisfied の戻りを保存して
  • cnt が 0 の場合

になります。cnt が 0 になるのは array_* が空リスト (って言ってよいのかどうか微妙) になった場合、という事になるんでしょうが、そのあたりの根拠は以下で。
てーかも一度 reduce_satisfied() 手続きのポイントを以下に。

  • render array_* で戻されたリストから initdir に存在しない要素を除く
  • runlist の要素を取り出しつつ 上記のリスト (deplist) にその要素があった場合はそれを除く
  • array_* に再設定

てーコトはやはり prereqs なソレはそのスクリプトが依存する (自分より先に実行されている必要のある) スクリプト、という事になりますな。あと、お互いに依存しあってたらいつまで経っても array_* は空にならないな。Circular dependancy はそーゆー意味ッスか。
とりあえずメシ食って再度でかける用件があるんで、ここで再投入。

さらに続き

count_satisfied() 手続きの定義は以下。

count_unsatisfied()
{
	set -- ${@}
	return ${#}
}

これは何をしているか、というとリストで指定された引数の要素数が戻る。空リストであればゼロですな。ちなみに呼び出しは以下な形。

			count_unsatisfied $(render array_${rp_x})

array_* のナニが渡される。return しておいて直後で ${?} を検査、というのはなかなかウマいな、と。
あと、スルーしてたんですが、pop_list_item() 手続きも確認。

# Removes $1 from initlist
pop_list_item()
{
	item=${1}
	shift
	set -- ${@}
	unset tmppop
	# Iterate
	for pop in ${@}; do
		if [ ${pop} = ${item} ]; then
			continue
		fi
		tmppop="${tmppop} ${pop}"
	done

}

呼び出しな例が以下。

				pop_list_item ${rp_x} ${initlist}
				initlist=${tmppop}

tmppop に ${1} 以外の要素なリストがセットされるので、呼び出し後にリセットする必要がある。こうして見てるに functions にある手続き達は引数というか位置パラメータというヤツを上手に使ってますな。
とりあえず load_modules() 手続きと parse_numeric() 手続きは別途で。