init_functions 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. # This file contains common functions used in init and in hooks
  2. # logging targets
  3. _rdlog_file=$(( 1 << 0 ))
  4. _rdlog_kmsg=$(( 1 << 1 ))
  5. _rdlog_cons=$(( 1 << 2 ))
  6. _rdlog_all=$(( (1 << 3) - 1 ))
  7. msg () {
  8. [ "${quiet}" != "y" ] && echo $@
  9. }
  10. err () {
  11. echo "ERROR: $@"
  12. }
  13. log_kmsg() {
  14. local fmt=$1; shift
  15. printf "<31>initramfs: $fmt\n" "$@"
  16. }
  17. poll_device() {
  18. local device=$1 seconds=${2//[!0-9]}
  19. [ "${seconds:-x}" = x ] && seconds=10
  20. deciseconds=$(( seconds * 10 ))
  21. # tenths of a second
  22. sleepinterval=1
  23. [ -b "$device" ] && return 0
  24. if [ "$udevd_running" -eq 1 ]; then
  25. msg "Waiting $seconds seconds for device $device ..." >&2
  26. while [ ! -b "$device" -a "$deciseconds" -gt 0 ]; do
  27. if [ "$sleepinterval" -ge 10 ]; then
  28. sleep 1
  29. deciseconds=$(( deciseconds - 10 ))
  30. else
  31. sleep .$sleepinterval
  32. deciseconds=$(( deciseconds - sleepinterval ))
  33. sleepinterval=$(( sleepinterval * 2 ))
  34. fi
  35. done
  36. fi
  37. [ -b "$device" ]
  38. }
  39. launch_interactive_shell() {
  40. export PS1='[rootfs \W]\$ '
  41. # explicitly redirect to /dev/console in case we're logging. note that
  42. # anything done in the rescue shell will NOT be logged.
  43. {
  44. [ "$1" = "--exec" ] && exec sh -i
  45. sh -i
  46. } 0</dev/console 1>/dev/console 2>/dev/console
  47. }
  48. bitfield_has_bit() {
  49. [ $(( $1 & $2 )) -eq $2 ]
  50. }
  51. major_minor_to_device() {
  52. local dev
  53. [ -e "/sys/dev/block/$1:$2" ] || return 1
  54. if dev=$(readlink -f "/sys/dev/block/$1:$2" 2>/dev/null); then
  55. echo "/dev/${dev##*/}"
  56. return 0
  57. fi
  58. return 1
  59. }
  60. run_hookfunctions() {
  61. local hook fn=$1 desc=$2
  62. shift 2
  63. for hook in "$@"; do
  64. [ -x "/hooks/$hook" ] || continue
  65. unset "$fn"
  66. . "/hooks/$hook"
  67. type "$fn" >/dev/null || continue
  68. msg ":: running $desc [$hook]"
  69. "$fn"
  70. done
  71. }
  72. set_log_option() {
  73. local opt
  74. for opt in ${1//|/ }; do
  75. case $opt in
  76. all)
  77. rd_logmask=$_rdlog_all
  78. ;;
  79. kmsg)
  80. rd_logmask=$(( rd_logmask | _rdlog_kmsg ))
  81. ;;
  82. file)
  83. rd_logmask=$(( rd_logmask | _rdlog_file ))
  84. ;;
  85. console)
  86. rd_logmask=$(( rd_logmask | _rdlog_cons ))
  87. ;;
  88. *)
  89. err "unknown rd.log parameter: '$opt'"
  90. ;;
  91. esac
  92. done
  93. }
  94. parse_cmdline() {
  95. local _w _quoted _lhs _rhs _cmdline
  96. read -r _cmdline
  97. for _w in $_cmdline; do
  98. if [ -z "$_quoted" ]; then
  99. case $_w in
  100. # ignore everything after a # in the commandline
  101. \#*) break ;;
  102. # special cases
  103. rw|ro) rwopt=$_w ;;
  104. fsck.mode=*)
  105. case ${_w#*=} in
  106. force)
  107. forcefsck=y
  108. ;;
  109. skip)
  110. fastboot=y
  111. ;;
  112. *)
  113. err "unknown fsck.mode parameter: '${_w#*=}'"
  114. ;;
  115. esac
  116. ;;
  117. rd.*)
  118. case ${_w#rd.} in
  119. debug)
  120. rd_debug=y
  121. ;;
  122. log)
  123. rd_logmask=$(( _rdlog_kmsg | _rdlog_cons ))
  124. ;;
  125. log=*)
  126. set_log_option "${_w#rd.log=}"
  127. ;;
  128. esac
  129. ;;
  130. # abide by shell variable naming rules
  131. [[:alpha:]_]*=*)
  132. _rhs=${_w#*=}
  133. _lhs=${_w%%=*}
  134. _lhs=${_lhs//[-.]/_}
  135. if [ '"' = "${_rhs:0:1}" ]; then
  136. if [ '"' = "${_rhs:$((${#_rhs}-1))}" ]; then
  137. _rhs="${_rhs:1:$((${#_rhs}-2))}"
  138. else
  139. _rhs=${_rhs:1}
  140. _quoted=1
  141. continue
  142. fi
  143. fi
  144. eval $_lhs=\$_rhs
  145. ;;
  146. [[:alpha:]_]*)
  147. _lhs=${_w//[-.]/_}
  148. eval $_lhs=y
  149. ;;
  150. esac
  151. else
  152. if [ '"' = "${_w:$((${#_w}-1))}" ]; then
  153. _rhs="$_rhs ${_w%\"}"
  154. unset _quoted
  155. eval $_lhs=\$_rhs
  156. else
  157. _rhs="$_rhs $_w"
  158. fi
  159. fi
  160. done
  161. }
  162. fsck_device() {
  163. [ -x /sbin/fsck ] || return 255
  164. if [ ! -b "$1" ]; then
  165. err "device '$1' not found. Skipping fsck."
  166. return 255
  167. fi
  168. if [ -n "$fastboot" ]; then
  169. msg ":: skipping fsck on '$1'"
  170. return
  171. fi
  172. msg ":: performing fsck on '$1'"
  173. fsck -Ta -C"$FSCK_FD" "$1" -- ${forcefsck+-f}
  174. }
  175. fsck_root() {
  176. fsck_device "$root"
  177. fsckret=$?
  178. if [ -n "$fsckret" ] && [ "$fsckret" -ne 255 ]; then
  179. # handle error conditions; do nothing on success.
  180. if bitfield_has_bit "$fsckret" 4; then
  181. err "Bailing out. Run 'fsck $root' manually"
  182. printf '%s\n' \
  183. "********** FILESYSTEM CHECK FAILED **********" \
  184. "* *" \
  185. "* Please run fsck manually. After leaving *" \
  186. "* this maintenance shell, the system will *" \
  187. "* reboot automatically. *" \
  188. "* *" \
  189. "*********************************************"
  190. launch_interactive_shell
  191. echo ":: Automatic reboot in progress"
  192. sleep 2
  193. reboot -f
  194. elif bitfield_has_bit "$fsckret" 2; then
  195. printf '%s\n' \
  196. "************** REBOOT REQUIRED **************" \
  197. "* *" \
  198. "* automatically restarting in 10 seconds *" \
  199. "* *" \
  200. "*********************************************"
  201. sleep 10
  202. reboot -f
  203. elif bitfield_has_bit "$fsckret" 8; then
  204. err "fsck failed on '$root'"
  205. elif bitfield_has_bit "$fsckret" 16; then
  206. err "Failed to invoke fsck: usage or syntax error"
  207. elif bitfield_has_bit "$fsckret" 32; then
  208. echo ":: fsck cancelled on user request"
  209. elif bitfield_has_bit "$fsckret" 128; then
  210. err "fatal error invoking fsck"
  211. fi
  212. # ensure that root is going to be mounted rw. Otherwise, systemd
  213. # might fsck the device again. Annoy the user so that they fix this.
  214. if [ "${rwopt:-ro}" != 'rw' ]; then
  215. echo "********************** WARNING **********************"
  216. echo "* *"
  217. echo "* The root device is not configured to be mounted *"
  218. echo "* read-write! It may be fsck'd again later. *"
  219. echo "* *"
  220. echo "*****************************************************"
  221. fi
  222. fi
  223. }
  224. # TODO: this really needs to follow the logic of systemd's encode_devnode_name
  225. # function more closely.
  226. tag_to_udev_path() {
  227. awk -v "tag=$1" -v "value=$2" '
  228. BEGIN {
  229. gsub(/\//, "\\x2f", value)
  230. printf "/dev/disk/by-%s/%s\n", tolower(tag), value
  231. }'
  232. }
  233. resolve_device() {
  234. local major minor dev tag device=$1
  235. # attempt to resolve devices immediately. if this fails
  236. # and udev is running, fall back on lazy resolution using
  237. # /dev/disk/by-* symlinks. this is flexible enough to support
  238. # usage of tags without udev and "slow" devices like root on
  239. # USB, which might not immediately show up.
  240. case $device in
  241. UUID=*|LABEL=*|PARTUUID=*|PARTLABEL=*)
  242. dev=$(blkid -lt "$device" -o device)
  243. if [ -z "$dev" -a "$udevd_running" -eq 1 ]; then
  244. dev=$(tag_to_udev_path "${device%%=*}" "${device#*=}")
  245. fi
  246. esac
  247. [ -n "$dev" ] && device=$dev
  248. case $device in
  249. # path to kernel named block device
  250. /dev/*)
  251. if poll_device "$device" "$rootdelay"; then
  252. echo "$device"
  253. return 0
  254. fi
  255. ;;
  256. # hex encoded major/minor, such as from LILO
  257. [0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]|[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])
  258. major=$(( 0x0$device >> 8 ))
  259. minor=$(( 0x0$device & 0xff ))
  260. ;;
  261. 0x[0-9a-fA-F][0-9a-fA-F]*)
  262. major=$(( device >> 8 ))
  263. minor=$(( device & 0xff ))
  264. ;;
  265. esac
  266. if [ -n "$major" -a -n "$minor" ]; then
  267. device=$(major_minor_to_device "$major" "$minor" || echo '/dev/root')
  268. if [ ! -b "$device" ]; then
  269. msg "Creating device node with major $major and minor $minor." >&2
  270. mknod "$device" b "$major" "$minor"
  271. fi
  272. echo "$device"
  273. return 0
  274. fi
  275. return 1
  276. }
  277. default_mount_handler() {
  278. if [ ! -b "$root" ]; then
  279. err "Unable to find root device '$root'."
  280. echo "You are being dropped to a recovery shell"
  281. echo " Type 'exit' to try and continue booting"
  282. launch_interactive_shell
  283. msg "Trying to continue (this will most likely fail) ..."
  284. fi
  285. msg ":: mounting '$root' on real root"
  286. if ! mount ${fstype:+-t $fstype} -o ${rwopt:-ro}${rootflags:+,$rootflags} "$root" "$1"; then
  287. echo "You are now being dropped into an emergency shell."
  288. launch_interactive_shell
  289. msg "Trying to continue (this will most likely fail) ..."
  290. fi
  291. }
  292. rdlogger_start() {
  293. # rd.debug implies rd.log=console if rd.log(=.*)? isn't present
  294. if [ "$rd_logmask" -eq 0 ] && [ -n "$rd_debug" ]; then
  295. rd_logmask=$_rdlog_cons
  296. fi
  297. [ "$rd_logmask" -gt 0 ] || return
  298. mkfifo /run/initramfs/rdlogger.pipe
  299. rdlogger </run/initramfs/rdlogger.pipe >/dev/console 2>&1 &
  300. printf %s $! >/run/initramfs/rdlogger.pid
  301. exec >/run/initramfs/rdlogger.pipe 2>&1
  302. [ -n "$rd_debug" ] && set -x
  303. # messages would be otherwise lost if we don't unset quiet. this does,
  304. # however, mean that passing rd.log=console will negate the effects of
  305. # 'quiet' for initramfs console output.
  306. unset quiet
  307. }
  308. rdlogger_stop() {
  309. local i=0 pid
  310. [ -e /run/initramfs/rdlogger.pipe ] || return
  311. [ -n "$rd_debug" ] && { set +x; } 2>/dev/null
  312. # this nudges rdlogger to exit
  313. exec 0<>/dev/console 1<>/dev/console 2<>/dev/console
  314. # wait up to 1 second for a graceful shutdown
  315. until [ ! -e /run/initramfs/rdlogger.pipe ] || [ $i -eq 10 ]; do
  316. sleep 0.1
  317. i=$(( i + 1 ))
  318. done
  319. if [ $i -eq 10 ]; then
  320. # racy! the logger might still go away on its own
  321. read -r pid </run/initramfs/rdlogger.pid 2>/dev/null
  322. if [ -n "$pid" ]; then
  323. kill "$pid" 2>/dev/null
  324. fi
  325. fi
  326. }
  327. rdlogger() {
  328. local line
  329. # establish logging FDs. Either redirect to an appropriate target or to
  330. # /dev/null. This way, log methods can simply write and the associated FD
  331. # will Do The Right Thing™.
  332. # rd.log=console
  333. if bitfield_has_bit "$rd_logmask" "$_rdlog_cons"; then
  334. exec 4>/dev/console
  335. else
  336. exec 4>/dev/null
  337. fi
  338. # rd.log=kmsg
  339. if [ -c /dev/kmsg ] && bitfield_has_bit "$rd_logmask" "$_rdlog_kmsg"; then
  340. exec 5>/dev/kmsg
  341. else
  342. exec 5>/dev/null
  343. fi
  344. # rd.log=file
  345. if bitfield_has_bit "$rd_logmask" "$_rdlog_file"; then
  346. exec 6>/run/initramfs/init.log
  347. else
  348. exec 6>/dev/null
  349. fi
  350. while read -r line; do
  351. # rd.log=console
  352. printf '%s\n' "$line" >&4
  353. # rd.log=kmsg
  354. log_kmsg '%s' "$line" >&5
  355. # rd.log=file
  356. printf '%s\n' "$line" >&6
  357. done
  358. # EOF, shutting down...
  359. exec 4>&- 5>&- 6>&-
  360. rm -f /run/initramfs/rdlogger.pipe /run/initramfs/rdlogger.pid
  361. }
  362. # vim: set ft=sh ts=4 sw=4 et: