]> git.ipfire.org Git - thirdparty/dracut.git/blob - dracut-functions.sh
merge "cleanup" and "pre-pivot-cleanup" hooks
[thirdparty/dracut.git] / dracut-functions.sh
1 #!/bin/bash
2 # -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
3 # ex: ts=8 sw=4 sts=4 et filetype=sh
4 #
5 # functions used by dracut and other tools.
6 #
7 # Copyright 2005-2009 Red Hat, Inc. All rights reserved.
8 #
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #
22
23 if ! [[ $dracutbasedir ]]; then
24 dracutbasedir=${BASH_SOURCE[0]%/*}
25 [[ $dracutbasedir = "dracut-functions" ]] && dracutbasedir="."
26 [[ $dracutbasedir ]] || dracutbasedir="."
27 dracutbasedir="$(readlink -f $dracutbasedir)"
28 fi
29
30 if ! type dinfo >/dev/null 2>&1; then
31 . "$dracutbasedir/dracut-logger.sh"
32 dlog_init
33 fi
34
35 # export standard hookdirs
36 [[ $hookdirs ]] || {
37 hookdirs="cmdline pre-udev pre-trigger netroot "
38 hookdirs+="initqueue initqueue/settled initqueue/online initqueue/finished initqueue/timeout "
39 hookdirs+="pre-mount pre-pivot cleanup mount "
40 hookdirs+="emergency shutdown-emergency shutdown "
41 export hookdirs
42 }
43
44 # Generic substring function. If $2 is in $1, return 0.
45 strstr() { [ "${1#*$2*}" != "$1" ]; }
46
47 # Create all subdirectories for given path without creating the last element.
48 # $1 = path
49 mksubdirs() { mkdir -m 0755 -p ${1%/*}; }
50
51 # Version comparision function. Assumes Linux style version scheme.
52 # $1 = version a
53 # $2 = comparision op (gt, ge, eq, le, lt, ne)
54 # $3 = version b
55 vercmp() {
56 local _n1=(${1//./ }) _op=$2 _n2=(${3//./ }) _i _res
57
58 for ((_i=0; ; _i++))
59 do
60 if [[ ! ${_n1[_i]}${_n2[_i]} ]]; then _res=0
61 elif ((${_n1[_i]:-0} > ${_n2[_i]:-0})); then _res=1
62 elif ((${_n1[_i]:-0} < ${_n2[_i]:-0})); then _res=2
63 else continue
64 fi
65 break
66 done
67
68 case $_op in
69 gt) ((_res == 1));;
70 ge) ((_res != 2));;
71 eq) ((_res == 0));;
72 le) ((_res != 1));;
73 lt) ((_res == 2));;
74 ne) ((_res != 0));;
75 esac
76 }
77
78 # is_func <command>
79 # Check whether $1 is a function.
80 is_func() {
81 [[ $(type -t $1) = "function" ]]
82 }
83
84 # Function prints global variables in format name=value line by line.
85 # $@ = list of global variables' name
86 print_vars() {
87 local _var _value
88
89 for _var in $@
90 do
91 _value=$(eval echo \$$_var)
92 [[ ${_value} ]] && echo "${_var}=\"${_value}\""
93 done
94 }
95
96 # normalize_path <path>
97 # Prints the normalized path, where it removes any duplicated
98 # and trailing slashes.
99 # Example:
100 # $ normalize_path ///test/test//
101 # /test/test
102 normalize_path() {
103 shopt -q -s extglob
104 set -- "${1//+(\/)//}"
105 shopt -q -u extglob
106 echo "${1%/}"
107 }
108
109 # convert_abs_rel <from> <to>
110 # Prints the relative path, when creating a symlink to <to> from <from>.
111 # Example:
112 # $ convert_abs_rel /usr/bin/test /bin/test-2
113 # ../../bin/test-2
114 # $ ln -s $(convert_abs_rel /usr/bin/test /bin/test-2) /usr/bin/test
115 convert_abs_rel() {
116 local __current __absolute __abssize __cursize __newpath
117 local -i __i __level
118
119 set -- "$(normalize_path "$1")" "$(normalize_path "$2")"
120
121 # corner case #1 - self looping link
122 [[ "$1" == "$2" ]] && { echo "${1##*/}"; return; }
123
124 # corner case #2 - own dir link
125 [[ "${1%/*}" == "$2" ]] && { echo "."; return; }
126
127 IFS="/" __current=($1)
128 IFS="/" __absolute=($2)
129
130 __abssize=${#__absolute[@]}
131 __cursize=${#__current[@]}
132
133 while [[ ${__absolute[__level]} == ${__current[__level]} ]]
134 do
135 (( __level++ ))
136 if (( __level > __abssize || __level > __cursize ))
137 then
138 break
139 fi
140 done
141
142 for ((__i = __level; __i < __cursize-1; __i++))
143 do
144 if ((__i > __level))
145 then
146 __newpath=$__newpath"/"
147 fi
148 __newpath=$__newpath".."
149 done
150
151 for ((__i = __level; __i < __abssize; __i++))
152 do
153 if [[ -n $__newpath ]]
154 then
155 __newpath=$__newpath"/"
156 fi
157 __newpath=$__newpath${__absolute[__i]}
158 done
159
160 echo "$__newpath"
161 }
162
163 # get_fs_env <device>
164 # Get and set the ID_FS_TYPE and ID_FS_UUID variable from udev for a device.
165 # Example:
166 # $ get_fs_env /dev/sda2; echo $ID_FS_TYPE; echo $ID_FS_UUID
167 # ext4
168 # 551a39aa-4ae9-4e70-a262-ef665cadb574
169 get_fs_env() {
170 local evalstr
171 local found
172
173 [[ $1 ]] || return
174 unset ID_FS_TYPE
175 unset ID_FS_UUID
176 if evalstr=$(udevadm info --query=env --name=$1 \
177 | { while read line; do
178 strstr "$line" "DEVPATH" && found=1;
179 strstr "$line" "ID_FS_TYPE=" && { echo $line; exit 0;}
180 done; [[ $found ]] && exit 0; exit 1; }) ; then
181 eval $evalstr
182 [[ $ID_FS_TYPE ]] && return 0
183 return 1
184 fi
185
186 # Fallback, for the old vol_id
187 if [[ -x /lib/udev/vol_id ]]; then
188 if evalstr=$(/lib/udev/vol_id --export $1 \
189 | while read line; do
190 strstr "$line" "ID_FS_TYPE=" && { echo $line; exit 0;}
191 done;) ; then
192 eval $evalstr
193 [[ $ID_FS_TYPE ]] && return 0
194 fi
195 fi
196
197 # Fallback, if we don't have udev information
198 if find_binary blkid >/dev/null; then
199 eval $(blkid -o udev $1 \
200 | while read line; do
201 strstr "$line" "ID_FS_TYPE=" && echo $line;
202 done)
203 [[ $ID_FS_TYPE ]] && return 0
204 fi
205 return 1
206 }
207
208 # get_maj_min <device>
209 # Prints the major and minor of a device node.
210 # Example:
211 # $ get_maj_min /dev/sda2
212 # 8:2
213 get_maj_min() {
214 local _dev
215 _dev=$(stat -L -c '$((0x%t)):$((0x%T))' "$1" 2>/dev/null)
216 _dev=$(eval "echo $_dev")
217 echo $_dev
218 }
219
220 # find_block_device <mountpoint>
221 # Prints the major and minor number of the block device
222 # for a given mountpoint.
223 # Unless $use_fstab is set to "yes" the functions
224 # uses /proc/self/mountinfo as the primary source of the
225 # information and only falls back to /etc/fstab, if the mountpoint
226 # is not found there.
227 # Example:
228 # $ find_block_device /usr
229 # 8:4
230 find_block_device() {
231 local _x _mpt _majmin _dev _fs _maj _min
232 if [[ $use_fstab != yes ]]; then
233 while read _x _x _majmin _x _mpt _x _x _fs _dev _x; do
234 [[ $_mpt = $1 ]] || continue
235 [[ $_fs = nfs ]] && { echo $_dev; return 0;}
236 [[ $_fs = nfs3 ]] && { echo $_dev; return 0;}
237 [[ $_fs = nfs4 ]] && { echo $_dev; return 0;}
238 [[ $_fs = btrfs ]] && {
239 get_maj_min $_dev
240 return 0;
241 }
242 if [[ ${_majmin#0:} = $_majmin ]]; then
243 echo $_majmin
244 return 0 # we have a winner!
245 fi
246 done < /proc/self/mountinfo
247 fi
248 # fall back to /etc/fstab
249 while read _dev _mpt _fs _x; do
250 [ "${_dev%%#*}" != "$_dev" ] && continue
251
252 if [[ $_mpt = $1 ]]; then
253 [[ $_fs = nfs ]] && { echo $_dev; return 0;}
254 [[ $_fs = nfs3 ]] && { echo $_dev; return 0;}
255 [[ $_fs = nfs4 ]] && { echo $_dev; return 0;}
256 [[ $_dev != ${_dev#UUID=} ]] && _dev=/dev/disk/by-uuid/${_dev#UUID=}
257 [[ $_dev != ${_dev#LABEL=} ]] && _dev=/dev/disk/by-label/${_dev#LABEL=}
258 [[ -b $_dev ]] || return 1 # oops, not a block device.
259 get_maj_min "$_dev" && return 0
260 fi
261 done < /etc/fstab
262
263 return 1
264 }
265
266 # find_dev_fstype <device>
267 # Echo the filesystem type for a given device.
268 # /proc/self/mountinfo is taken as the primary source of information
269 # and /etc/fstab is used as a fallback.
270 # No newline is appended!
271 # Example:
272 # $ find_dev_fstype /dev/sda2;echo
273 # ext4
274 find_dev_fstype() {
275 local _x _mpt _majmin _dev _fs _maj _min
276 while read _x _x _majmin _x _mpt _x _x _fs _dev _x; do
277 [[ $_dev = $1 ]] || continue
278 echo -n $_fs;
279 return 0;
280 done < /proc/self/mountinfo
281
282 # fall back to /etc/fstab
283 while read _dev _mpt _fs _x; do
284 [[ $_dev = $1 ]] || continue
285 echo -n $_fs;
286 return 0;
287 done < /etc/fstab
288
289 return 1
290 }
291
292 # finds the major:minor of the block device backing the root filesystem.
293 find_root_block_device() { find_block_device /; }
294
295 # for_each_host_dev_fs <func>
296 # Execute "<func> <dev> <filesystem>" for every "<dev>|<fs>" pair found
297 # in ${host_fs_types[@]}
298 for_each_host_dev_fs()
299 {
300 local _func="$1"
301 local _dev
302 local _fs
303 local _ret=1
304 for f in ${host_fs_types[@]}; do
305 OLDIFS="$IFS"
306 IFS="|"
307 set -- $f
308 IFS="$OLDIFS"
309 _dev="$1"
310 [[ -b "$_dev" ]] || continue
311 _fs="$2"
312 $_func $_dev $_fs && _ret=0
313 done
314 return $_ret
315 }
316
317 # Walk all the slave relationships for a given block device.
318 # Stop when our helper function returns success
319 # $1 = function to call on every found block device
320 # $2 = block device in major:minor format
321 check_block_and_slaves() {
322 local _x
323 [[ -b /dev/block/$2 ]] || return 1 # Not a block device? So sorry.
324 "$1" $2 && return
325 check_vol_slaves "$@" && return 0
326 if [[ -f /sys/dev/block/$2/../dev ]]; then
327 check_block_and_slaves $1 $(cat "/sys/dev/block/$2/../dev") && return 0
328 fi
329 [[ -d /sys/dev/block/$2/slaves ]] || return 1
330 for _x in /sys/dev/block/$2/slaves/*/dev; do
331 [[ -f $_x ]] || continue
332 check_block_and_slaves $1 $(cat "$_x") && return 0
333 done
334 return 1
335 }
336
337 # ugly workaround for the lvm design
338 # There is no volume group device,
339 # so, there are no slave devices for volume groups.
340 # Logical volumes only have the slave devices they really live on,
341 # but you cannot create the logical volume without the volume group.
342 # And the volume group might be bigger than the devices the LV needs.
343 check_vol_slaves() {
344 local _lv _vg _pv
345 for i in /dev/mapper/*; do
346 _lv=$(get_maj_min $i)
347 if [[ $_lv = $2 ]]; then
348 _vg=$(lvm lvs --noheadings -o vg_name $i 2>/dev/null)
349 # strip space
350 _vg=$(echo $_vg)
351 if [[ $_vg ]]; then
352 for _pv in $(lvm vgs --noheadings -o pv_name "$_vg" 2>/dev/null)
353 do
354 check_block_and_slaves $1 $(get_maj_min $_pv) && return 0
355 done
356 fi
357 fi
358 done
359 return 1
360 }
361
362 # Install a directory, keeping symlinks as on the original system.
363 # Example: if /lib points to /lib64 on the host, "inst_dir /lib/file"
364 # will create ${initdir}/lib64, ${initdir}/lib64/file,
365 # and a symlink ${initdir}/lib -> lib64.
366 inst_dir() {
367 [[ -e ${initdir}/"$1" ]] && return 0 # already there
368
369 local _dir="$1" _part="${1%/*}" _file
370 while [[ "$_part" != "${_part%/*}" ]] && ! [[ -e "${initdir}/${_part}" ]]; do
371 _dir="$_part $_dir"
372 _part=${_part%/*}
373 done
374
375 # iterate over parent directories
376 for _file in $_dir; do
377 [[ -e "${initdir}/$_file" ]] && continue
378 if [[ -L $_file ]]; then
379 inst_symlink "$_file"
380 else
381 # create directory
382 mkdir -m 0755 -p "${initdir}/$_file" || return 1
383 [[ -e "$_file" ]] && chmod --reference="$_file" "${initdir}/$_file"
384 chmod u+w "${initdir}/$_file"
385 fi
386 done
387 }
388
389 # $1 = file to copy to ramdisk
390 # $2 (optional) Name for the file on the ramdisk
391 # Location of the image dir is assumed to be $initdir
392 # We never overwrite the target if it exists.
393 inst_simple() {
394 [[ -f "$1" ]] || return 1
395 strstr "$1" "/" || return 1
396
397 local _src=$1 target="${2:-$1}"
398 if ! [[ -d ${initdir}/$target ]]; then
399 [[ -e ${initdir}/$target ]] && return 0
400 [[ -L ${initdir}/$target ]] && return 0
401 [[ -d "${initdir}/${target%/*}" ]] || inst_dir "${target%/*}"
402 fi
403 # install checksum files also
404 if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then
405 inst "${_src%/*}/.${_src##*/}.hmac" "${target%/*}/.${target##*/}.hmac"
406 fi
407 ddebug "Installing $_src"
408 cp --sparse=always -pfL "$_src" "${initdir}/$target"
409 }
410
411 # find symlinks linked to given library file
412 # $1 = library file
413 # Function searches for symlinks by stripping version numbers appended to
414 # library filename, checks if it points to the same target and finally
415 # prints the list of symlinks to stdout.
416 #
417 # Example:
418 # rev_lib_symlinks libfoo.so.8.1
419 # output: libfoo.so.8 libfoo.so
420 # (Only if libfoo.so.8 and libfoo.so exists on host system.)
421 rev_lib_symlinks() {
422 [[ ! $1 ]] && return 0
423
424 local fn="$1" orig="$(readlink -f "$1")" links=''
425
426 [[ ${fn} =~ .*\.so\..* ]] || return 1
427
428 until [[ ${fn##*.} == so ]]; do
429 fn="${fn%.*}"
430 [[ -L ${fn} && $(readlink -f "${fn}") == ${orig} ]] && links+=" ${fn}"
431 done
432
433 echo "${links}"
434 }
435
436 # Same as above, but specialized to handle dynamic libraries.
437 # It handles making symlinks according to how the original library
438 # is referenced.
439 inst_library() {
440 local _src="$1" _dest=${2:-$1} _lib _reallib _symlink
441 strstr "$1" "/" || return 1
442 [[ -e $initdir/$_dest ]] && return 0
443 if [[ -L $_src ]]; then
444 # install checksum files also
445 if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then
446 inst "${_src%/*}/.${_src##*/}.hmac" "${_dest%/*}/.${_dest##*/}.hmac"
447 fi
448 _reallib=$(readlink -f "$_src")
449 inst_simple "$_reallib" "$_reallib"
450 inst_dir "${_dest%/*}"
451 [[ -d "${_dest%/*}" ]] && _dest=$(readlink -f "${_dest%/*}")/${_dest##*/}
452 ln -sfn $(convert_abs_rel "${_dest}" "${_reallib}") "${initdir}/${_dest}"
453 else
454 inst_simple "$_src" "$_dest"
455 fi
456
457 # Create additional symlinks. See rev_symlinks description.
458 for _symlink in $(rev_lib_symlinks $_src) $(rev_lib_symlinks $_reallib); do
459 [[ ! -e $initdir/$_symlink ]] && {
460 ddebug "Creating extra symlink: $_symlink"
461 inst_symlink $_symlink
462 }
463 done
464 }
465
466 # find a binary. If we were not passed the full path directly,
467 # search in the usual places to find the binary.
468 find_binary() {
469 if [[ -z ${1##/*} ]]; then
470 if [[ -x $1 ]] || { strstr "$1" ".so" && ldd $1 &>/dev/null; }; then
471 echo $1
472 return 0
473 fi
474 fi
475
476 type -P $1
477 }
478
479 # Same as above, but specialized to install binary executables.
480 # Install binary executable, and all shared library dependencies, if any.
481 inst_binary() {
482 local _bin _target
483 _bin=$(find_binary "$1") || return 1
484 _target=${2:-$_bin}
485 [[ -e $initdir/$_target ]] && return 0
486 [[ -L $_bin ]] && inst_symlink $_bin $_target && return 0
487 local _file _line
488 local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
489 # I love bash!
490 LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do
491 [[ $_line = 'not a dynamic executable' ]] && break
492
493 if [[ $_line =~ $_so_regex ]]; then
494 _file=${BASH_REMATCH[1]}
495 [[ -e ${initdir}/$_file ]] && continue
496 inst_library "$_file"
497 continue
498 fi
499
500 if [[ $_line =~ not\ found ]]; then
501 dfatal "Missing a shared library required by $_bin."
502 dfatal "Run \"ldd $_bin\" to find out what it is."
503 dfatal "$_line"
504 dfatal "dracut cannot create an initrd."
505 exit 1
506 fi
507 done
508 inst_simple "$_bin" "$_target"
509 }
510
511 # same as above, except for shell scripts.
512 # If your shell script does not start with shebang, it is not a shell script.
513 inst_script() {
514 local _bin
515 _bin=$(find_binary "$1") || return 1
516 shift
517 local _line _shebang_regex
518 read -r -n 80 _line <"$_bin"
519 # If debug is set, clean unprintable chars to prevent messing up the term
520 [[ $debug ]] && _line=$(echo -n "$_line" | tr -c -d '[:print:][:space:]')
521 _shebang_regex='(#! *)(/[^ ]+).*'
522 [[ $_line =~ $_shebang_regex ]] || return 1
523 inst "${BASH_REMATCH[2]}" && inst_simple "$_bin" "$@"
524 }
525
526 # same as above, but specialized for symlinks
527 inst_symlink() {
528 local _src=$1 _target=${2:-$1} _realsrc
529 strstr "$1" "/" || return 1
530 [[ -L $1 ]] || return 1
531 [[ -L $initdir/$_target ]] && return 0
532 _realsrc=$(readlink -f "$_src")
533 if ! [[ -e $initdir/$_realsrc ]]; then
534 if [[ -d $_realsrc ]]; then
535 inst_dir "$_realsrc"
536 else
537 inst "$_realsrc"
538 fi
539 fi
540 [[ ! -e $initdir/${_target%/*} ]] && inst_dir "${_target%/*}"
541 [[ -d ${_target%/*} ]] && _target=$(readlink -f ${_target%/*})/${_target##*/}
542 ln -sfn $(convert_abs_rel "${_target}" "${_realsrc}") "$initdir/$_target"
543 }
544
545 # attempt to install any programs specified in a udev rule
546 inst_rule_programs() {
547 local _prog _bin
548
549 if grep -qE 'PROGRAM==?"[^ "]+' "$1"; then
550 for _prog in $(grep -E 'PROGRAM==?"[^ "]+' "$1" | sed -r 's/.*PROGRAM==?"([^ "]+).*/\1/'); do
551 if [ -x /lib/udev/$_prog ]; then
552 _bin=/lib/udev/$_prog
553 else
554 _bin=$(find_binary "$_prog") || {
555 dinfo "Skipping program $_prog using in udev rule $(basename $1) as it cannot be found"
556 continue;
557 }
558 fi
559
560 #dinfo "Installing $_bin due to it's use in the udev rule $(basename $1)"
561 dracut_install "$_bin"
562 done
563 fi
564 }
565
566 # udev rules always get installed in the same place, so
567 # create a function to install them to make life simpler.
568 inst_rules() {
569 local _target=/etc/udev/rules.d _rule _found
570
571 inst_dir "/lib/udev/rules.d"
572 inst_dir "$_target"
573 for _rule in "$@"; do
574 if [ "${rule#/}" = "$rule" ]; then
575 for r in /lib/udev/rules.d /etc/udev/rules.d; do
576 if [[ -f $r/$_rule ]]; then
577 _found="$r/$_rule"
578 inst_simple "$_found"
579 inst_rule_programs "$_found"
580 fi
581 done
582 fi
583 for r in '' ./ $dracutbasedir/rules.d/; do
584 if [[ -f ${r}$_rule ]]; then
585 _found="${r}$_rule"
586 inst_simple "$_found" "$_target/${_found##*/}"
587 inst_rule_programs "$_found"
588 fi
589 done
590 [[ $_found ]] || dinfo "Skipping udev rule: $_rule"
591 done
592 }
593
594 # general purpose installation function
595 # Same args as above.
596 inst() {
597 local _x
598
599 case $# in
600 1) ;;
601 2) [[ ! $initdir && -d $2 ]] && export initdir=$2
602 [[ $initdir = $2 ]] && set $1;;
603 3) [[ -z $initdir ]] && export initdir=$2
604 set $1 $3;;
605 *) dfatal "inst only takes 1 or 2 or 3 arguments"
606 exit 1;;
607 esac
608 for _x in inst_symlink inst_script inst_binary inst_simple; do
609 $_x "$@" && return 0
610 done
611 return 1
612 }
613
614 # install function specialized for hooks
615 # $1 = type of hook, $2 = hook priority (lower runs first), $3 = hook
616 # All hooks should be POSIX/SuS compliant, they will be sourced by init.
617 inst_hook() {
618 if ! [[ -f $3 ]]; then
619 dfatal "Cannot install a hook ($3) that does not exist."
620 dfatal "Aborting initrd creation."
621 exit 1
622 elif ! strstr "$hookdirs" "$1"; then
623 dfatal "No such hook type $1. Aborting initrd creation."
624 exit 1
625 fi
626 inst_simple "$3" "/lib/dracut/hooks/${1}/${2}${3##*/}"
627 }
628
629 # install any of listed files
630 #
631 # If first argument is '-d' and second some destination path, first accessible
632 # source is installed into this path, otherwise it will installed in the same
633 # path as source. If none of listed files was installed, function return 1.
634 # On first successful installation it returns with 0 status.
635 #
636 # Example:
637 #
638 # inst_any -d /bin/foo /bin/bar /bin/baz
639 #
640 # Lets assume that /bin/baz exists, so it will be installed as /bin/foo in
641 # initramfs.
642 inst_any() {
643 local to f
644
645 [[ $1 = '-d' ]] && to="$2" && shift 2
646
647 for f in "$@"; do
648 if [[ -e $f ]]; then
649 [[ $to ]] && inst "$f" "$to" && return 0
650 inst "$f" && return 0
651 fi
652 done
653
654 return 1
655 }
656
657 # dracut_install [-o ] <file> [<file> ... ]
658 # Install <file> to the initramfs image
659 # -o optionally install the <file> and don't fail, if it is not there
660 dracut_install() {
661 local _optional=no
662 if [[ $1 = '-o' ]]; then
663 _optional=yes
664 shift
665 fi
666 while (($# > 0)); do
667 if ! inst "$1" ; then
668 if [[ $_optional = yes ]]; then
669 dinfo "Skipping program $1 as it cannot be found and is" \
670 "flagged to be optional"
671 else
672 dfatal "Failed to install $1"
673 exit 1
674 fi
675 fi
676 shift
677 done
678 }
679
680
681 # inst_libdir_file [-n <pattern>] <file> [<file>...]
682 # Install a <file> located on a lib directory to the initramfs image
683 # -n <pattern> install non-matching files
684 inst_libdir_file() {
685 if [[ "$1" == "-n" ]]; then
686 local _pattern=$1
687 shift 2
688 for _dir in $libdirs; do
689 for _i in "$@"; do
690 for _f in "$_dir"/$_i; do
691 [[ "$_i" =~ $_pattern ]] || continue
692 [[ -e "$_i" ]] && dracut_install "$_i"
693 done
694 done
695 done
696 else
697 for _dir in $libdirs; do
698 for _i in "$@"; do
699 for _f in "$_dir"/$_i; do
700 [[ -e "$_f" ]] && dracut_install "$_f"
701 done
702 done
703 done
704 fi
705 }
706
707
708 # install function decompressing the target and handling symlinks
709 # $@ = list of compressed (gz or bz2) files or symlinks pointing to such files
710 #
711 # Function install targets in the same paths inside overlay but decompressed
712 # and without extensions (.gz, .bz2).
713 inst_decompress() {
714 local _src _dst _realsrc _realdst _cmd
715
716 for _src in $@
717 do
718 case ${_src} in
719 *.gz) _cmd='gzip -d' ;;
720 *.bz2) _cmd='bzip2 -d' ;;
721 *) return 1 ;;
722 esac
723
724 if [[ -L ${_src} ]]
725 then
726 _realsrc="$(readlink -f ${_src})" # symlink target with extension
727 _dst="${_src%.*}" # symlink without extension
728 _realdst="${_realsrc%.*}" # symlink target without extension
729 mksubdirs "${initdir}/${_src}"
730 # Create symlink without extension to target without extension.
731 ln -sfn "${_realdst}" "${initdir}/${_dst}"
732 fi
733
734 # If the source is symlink we operate on its target.
735 [[ ${_realsrc} ]] && _src=${_realsrc}
736 inst ${_src}
737 # Decompress with chosen tool. We assume that tool changes name e.g.
738 # from 'name.gz' to 'name'.
739 ${_cmd} "${initdir}${_src}"
740 done
741 }
742
743 # It's similar to above, but if file is not compressed, performs standard
744 # install.
745 # $@ = list of files
746 inst_opt_decompress() {
747 local _src
748
749 for _src in $@
750 do
751 inst_decompress "${_src}" || inst "${_src}"
752 done
753 }
754
755 # module_check <dracut module>
756 # execute the check() function of module-setup.sh of <dracut module>
757 # or the "check" script, if module-setup.sh is not found
758 # "check $hostonly" is called
759 module_check() {
760 local _moddir=$(echo ${dracutbasedir}/modules.d/??${1})
761 local _ret
762 local _forced=0
763 local _hostonly=$hostonly
764 [ $# -eq 2 ] && _forced=$2
765 [[ -d $_moddir ]] || return 1
766 if [[ ! -f $_moddir/module-setup.sh ]]; then
767 # if we do not have a check script, we are unconditionally included
768 [[ -x $_moddir/check ]] || return 0
769 [ $_forced -ne 0 ] && unset hostonly
770 $_moddir/check $hostonly
771 _ret=$?
772 else
773 unset check depends install installkernel
774 . $_moddir/module-setup.sh
775 is_func check || return 0
776 [ $_forced -ne 0 ] && unset hostonly
777 check $hostonly
778 _ret=$?
779 unset check depends install installkernel
780 fi
781 hostonly=$_hostonly
782 return $_ret
783 }
784
785 # module_check_mount <dracut module>
786 # execute the check() function of module-setup.sh of <dracut module>
787 # or the "check" script, if module-setup.sh is not found
788 # "mount_needs=1 check 0" is called
789 module_check_mount() {
790 local _moddir=$(echo ${dracutbasedir}/modules.d/??${1})
791 local _ret
792 mount_needs=1
793 [[ -d $_moddir ]] || return 1
794 if [[ ! -f $_moddir/module-setup.sh ]]; then
795 # if we do not have a check script, we are unconditionally included
796 [[ -x $_moddir/check ]] || return 0
797 mount_needs=1 $_moddir/check 0
798 _ret=$?
799 else
800 unset check depends install installkernel
801 . $_moddir/module-setup.sh
802 is_func check || return 1
803 check 0
804 _ret=$?
805 unset check depends install installkernel
806 fi
807 unset mount_needs
808 return $_ret
809 }
810
811 # module_depends <dracut module>
812 # execute the depends() function of module-setup.sh of <dracut module>
813 # or the "depends" script, if module-setup.sh is not found
814 module_depends() {
815 local _moddir=$(echo ${dracutbasedir}/modules.d/??${1})
816 local _ret
817 [[ -d $_moddir ]] || return 1
818 if [[ ! -f $_moddir/module-setup.sh ]]; then
819 # if we do not have a check script, we have no deps
820 [[ -x $_moddir/check ]] || return 0
821 $_moddir/check -d
822 return $?
823 else
824 unset check depends install installkernel
825 . $_moddir/module-setup.sh
826 is_func depends || return 0
827 depends
828 _ret=$?
829 unset check depends install installkernel
830 return $_ret
831 fi
832 }
833
834 # module_install <dracut module>
835 # execute the install() function of module-setup.sh of <dracut module>
836 # or the "install" script, if module-setup.sh is not found
837 module_install() {
838 local _moddir=$(echo ${dracutbasedir}/modules.d/??${1})
839 local _ret
840 [[ -d $_moddir ]] || return 1
841 if [[ ! -f $_moddir/module-setup.sh ]]; then
842 [[ -x $_moddir/install ]] && . "$_moddir/install"
843 return $?
844 else
845 unset check depends install installkernel
846 . $_moddir/module-setup.sh
847 is_func install || return 0
848 install
849 _ret=$?
850 unset check depends install installkernel
851 return $_ret
852 fi
853 }
854
855 # module_installkernel <dracut module>
856 # execute the installkernel() function of module-setup.sh of <dracut module>
857 # or the "installkernel" script, if module-setup.sh is not found
858 module_installkernel() {
859 local _moddir=$(echo ${dracutbasedir}/modules.d/??${1})
860 local _ret
861 [[ -d $_moddir ]] || return 1
862 if [[ ! -f $_moddir/module-setup.sh ]]; then
863 [[ -x $_moddir/installkernel ]] && . "$_moddir/installkernel"
864 return $?
865 else
866 unset check depends install installkernel
867 . $_moddir/module-setup.sh
868 is_func installkernel || return 0
869 installkernel
870 _ret=$?
871 unset check depends install installkernel
872 return $_ret
873 fi
874 }
875
876 # check_mount <dracut module>
877 # check_mount checks, if a dracut module is needed for the given
878 # device and filesystem types in "${host_fs_types[@]}"
879 check_mount() {
880 local _mod=$1
881 local _moddir=$(echo ${dracutbasedir}/modules.d/??${1})
882 local _ret
883 local _moddep
884 # If we are already scheduled to be loaded, no need to check again.
885 strstr " $mods_to_load " " $_mod " && return 0
886 strstr " $mods_checked_as_dep " " $_mod " && return 1
887
888 # This should never happen, but...
889 [[ -d $_moddir ]] || return 1
890
891 [[ $2 ]] || mods_checked_as_dep+=" $_mod "
892
893 strstr " $omit_dracutmodules " " $_mod " && return 1
894
895 if [ "${#host_fs_types[*]}" -gt 0 ]; then
896 module_check_mount $_mod || return 1
897 else
898 # skip this module
899 return 1
900 fi
901
902 for _moddep in $(module_depends $_mod); do
903 # handle deps as if they were manually added
904 strstr " $add_dracutmodules " " $_moddep " || \
905 add_dracutmodules+=" $_moddep "
906 strstr " $force_add_dracutmodules " " $_moddep " || \
907 force_add_dracutmodules+=" $_moddep "
908 # if a module we depend on fail, fail also
909 check_module $_moddep || return 1
910 done
911
912 strstr " $mods_to_load " " $_mod " || \
913 mods_to_load+=" $_mod "
914
915 return 0
916 }
917
918 # check_module <dracut module> [<use_as_dep>]
919 # check if a dracut module is to be used in the initramfs process
920 # if <use_as_dep> is set, then the process also keeps track
921 # that the modules were checked for the dependency tracking process
922 check_module() {
923 local _mod=$1
924 local _moddir=$(echo ${dracutbasedir}/modules.d/??${1})
925 local _ret
926 local _moddep
927 # If we are already scheduled to be loaded, no need to check again.
928 strstr " $mods_to_load " " $_mod " && return 0
929 strstr " $mods_checked_as_dep " " $_mod " && return 1
930
931 # This should never happen, but...
932 [[ -d $_moddir ]] || return 1
933
934 [[ $2 ]] || mods_checked_as_dep+=" $_mod "
935
936 strstr " $omit_dracutmodules " " $_mod " && return 1
937
938 if strstr " $dracutmodules $add_dracutmodules $force_add_dracutmodules" " $_mod "; then
939 if strstr " $force_add_dracutmodules" " $_mod"; then
940 module_check $_mod 1; ret=$?
941 else
942 module_check $_mod 0; ret=$?
943 fi
944 # explicit module, so also accept ret=255
945 [[ $ret = 0 || $ret = 255 ]] || return 1
946 else
947 # module not in our list
948 if [[ $dracutmodules = all ]]; then
949 # check, if we can and should install this module
950 module_check $_mod || return 1
951 else
952 # skip this module
953 return 1
954 fi
955 fi
956
957 for _moddep in $(module_depends $_mod); do
958 # handle deps as if they were manually added
959 strstr " $add_dracutmodules " " $_moddep " || \
960 add_dracutmodules+=" $_moddep "
961 strstr " $force_add_dracutmodules " " $_moddep " || \
962 force_add_dracutmodules+=" $_moddep "
963 # if a module we depend on fail, fail also
964 check_module $_moddep || return 1
965 done
966
967 strstr " $mods_to_load " " $_mod " || \
968 mods_to_load+=" $_mod "
969
970 return 0
971 }
972
973 # for_each_module_dir <func>
974 # execute "<func> <dracut module> 1"
975 for_each_module_dir() {
976 local _modcheck
977 local _mod
978 local _moddir
979 local _func
980 _func=$1
981 for _moddir in "$dracutbasedir/modules.d"/[0-9][0-9]*; do
982 _mod=${_moddir##*/}; _mod=${_mod#[0-9][0-9]}
983 $_func $_mod 1
984 done
985
986 # Report any missing dracut modules, the user has specified
987 _modcheck="$add_dracutmodules $force_add_dracutmodules"
988 [[ $dracutmodules != all ]] && _modcheck="$m $dracutmodules"
989 for _mod in $_modcheck; do
990 strstr "$mods_to_load" "$_mod" && continue
991 strstr "$omit_dracutmodules" "$_mod" && continue
992 derror "Dracut module \"$_mod\" cannot be found."
993 done
994 }
995
996 # Install a single kernel module along with any firmware it may require.
997 # $1 = full path to kernel module to install
998 install_kmod_with_fw() {
999 # no need to go further if the module is already installed
1000
1001 [[ -e "${initdir}/lib/modules/$kernel/${1##*/lib/modules/$kernel/}" ]] \
1002 && return 0
1003
1004 [[ -e "$initdir/.kernelmodseen/${1##*/}" ]] && return 0
1005
1006 if [[ $omit_drivers ]]; then
1007 local _kmod=${1##*/}
1008 _kmod=${_kmod%.ko}
1009 _kmod=${_kmod/-/_}
1010 if [[ "$_kmod" =~ $omit_drivers ]]; then
1011 dinfo "Omitting driver $_kmod"
1012 return 1
1013 fi
1014 if [[ "${1##*/lib/modules/$kernel/}" =~ $omit_drivers ]]; then
1015 dinfo "Omitting driver $_kmod"
1016 return 1
1017 fi
1018 fi
1019
1020 [ -d "$initdir/.kernelmodseen" ] && \
1021 > "$initdir/.kernelmodseen/${1##*/}"
1022
1023 inst_simple "$1" "/lib/modules/$kernel/${1##*/lib/modules/$kernel/}" \
1024 || return $?
1025
1026 local _modname=${1##*/} _fwdir _found _fw
1027 _modname=${_modname%.ko*}
1028 for _fw in $(modinfo -k $kernel -F firmware $1 2>/dev/null); do
1029 _found=''
1030 for _fwdir in $fw_dir; do
1031 if [[ -d $_fwdir && -f $_fwdir/$_fw ]]; then
1032 inst_simple "$_fwdir/$_fw" "/lib/firmware/$_fw"
1033 _found=yes
1034 fi
1035 done
1036 if [[ $_found != yes ]]; then
1037 if ! grep -qe "\<${_modname//-/_}\>" /proc/modules; then
1038 dinfo "Possible missing firmware \"${_fw}\" for kernel module" \
1039 "\"${_modname}.ko\""
1040 else
1041 dwarn "Possible missing firmware \"${_fw}\" for kernel module" \
1042 "\"${_modname}.ko\""
1043 fi
1044 fi
1045 done
1046 return 0
1047 }
1048
1049 # Do something with all the dependencies of a kernel module.
1050 # Note that kernel modules depend on themselves using the technique we use
1051 # $1 = function to call for each dependency we find
1052 # It will be passed the full path to the found kernel module
1053 # $2 = module to get dependencies for
1054 # rest of args = arguments to modprobe
1055 # _fderr specifies FD passed from surrounding scope
1056 for_each_kmod_dep() {
1057 local _func=$1 _kmod=$2 _cmd _modpath _options _found=0
1058 shift 2
1059 modprobe "$@" --ignore-install --show-depends $_kmod 2>&${_fderr} | (
1060 while read _cmd _modpath _options; do
1061 [[ $_cmd = insmod ]] || continue
1062 $_func ${_modpath} || exit $?
1063 _found=1
1064 done
1065 [[ $_found -eq 0 ]] && exit 1
1066 exit 0
1067 )
1068 }
1069
1070 # filter kernel modules to install certain modules that meet specific
1071 # requirements.
1072 # $1 = search only in subdirectory of /kernel/$1
1073 # $2 = function to call with module name to filter.
1074 # This function will be passed the full path to the module to test.
1075 # The behaviour of this function can vary depending on whether $hostonly is set.
1076 # If it is, we will only look at modules that are already in memory.
1077 # If it is not, we will look at all kernel modules
1078 # This function returns the full filenames of modules that match $1
1079 filter_kernel_modules_by_path () (
1080 local _modname _filtercmd
1081 if ! [[ $hostonly ]]; then
1082 _filtercmd='find "$srcmods/kernel/$1" "$srcmods/extra"'
1083 _filtercmd+=' "$srcmods/weak-updates" -name "*.ko" -o -name "*.ko.gz"'
1084 _filtercmd+=' -o -name "*.ko.xz"'
1085 _filtercmd+=' 2>/dev/null'
1086 else
1087 _filtercmd='cut -d " " -f 1 </proc/modules|xargs modinfo -F filename '
1088 _filtercmd+='-k $kernel 2>/dev/null'
1089 fi
1090 for _modname in $(eval $_filtercmd); do
1091 case $_modname in
1092 *.ko) "$2" "$_modname" && echo "$_modname";;
1093 *.ko.gz) gzip -dc "$_modname" > $initdir/$$.ko
1094 $2 $initdir/$$.ko && echo "$_modname"
1095 rm -f $initdir/$$.ko
1096 ;;
1097 *.ko.xz) xz -dc "$_modname" > $initdir/$$.ko
1098 $2 $initdir/$$.ko && echo "$_modname"
1099 rm -f $initdir/$$.ko
1100 ;;
1101 esac
1102 done
1103 )
1104 find_kernel_modules_by_path () (
1105 if ! [[ $hostonly ]]; then
1106 find "$srcmods/kernel/$1" "$srcmods/extra" "$srcmods/weak-updates" \
1107 -name "*.ko" -o -name "*.ko.gz" -o -name "*.ko.xz" 2>/dev/null
1108 else
1109 cut -d " " -f 1 </proc/modules \
1110 | xargs modinfo -F filename -k $kernel 2>/dev/null
1111 fi
1112 )
1113
1114 filter_kernel_modules () {
1115 filter_kernel_modules_by_path drivers "$1"
1116 }
1117
1118 find_kernel_modules () {
1119 find_kernel_modules_by_path drivers
1120 }
1121
1122 # instmods <kernel module> [<kernel module> ... ]
1123 # instmods <kernel subsystem>
1124 # install kernel modules along with all their dependencies.
1125 # <kernel subsystem> can be e.g. "=block" or "=drivers/usb/storage"
1126 instmods() {
1127 [[ $no_kernel = yes ]] && return
1128 # called [sub]functions inherit _fderr
1129 local _fderr=9
1130
1131 function inst1mod() {
1132 local _mod="$1"
1133 case $_mod in
1134 =*)
1135 if [ -f $srcmods/modules.${_mod#=} ]; then
1136 ( [[ "$_mpargs" ]] && echo $_mpargs
1137 cat "${srcmods}/modules.${_mod#=}" ) \
1138 | instmods
1139 else
1140 ( [[ "$_mpargs" ]] && echo $_mpargs
1141 find "$srcmods" -path "*/${_mod#=}/*" -printf '%f\n' ) \
1142 | instmods
1143 fi
1144 ;;
1145 --*) _mpargs+=" $_mod" ;;
1146 i2o_scsi) return ;; # Do not load this diagnostic-only module
1147 *)
1148 _mod=${_mod##*/}
1149 # if we are already installed, skip this module and go on
1150 # to the next one.
1151 [[ -f "$initdir/.kernelmodseen/${_mod%.ko}.ko" ]] && return
1152
1153 if [[ $omit_drivers ]] && [[ "$1" =~ $omit_drivers ]]; then
1154 dinfo "Omitting driver ${_mod##$srcmods}"
1155 return
1156 fi
1157 # If we are building a host-specific initramfs and this
1158 # module is not already loaded, move on to the next one.
1159 [[ $hostonly ]] && ! grep -qe "\<${_mod//-/_}\>" /proc/modules \
1160 && ! echo $add_drivers | grep -qe "\<${_mod}\>" \
1161 && return
1162
1163 # We use '-d' option in modprobe only if modules prefix path
1164 # differs from default '/'. This allows us to use Dracut with
1165 # old version of modprobe which doesn't have '-d' option.
1166 local _moddirname=${srcmods%%/lib/modules/*}
1167 [[ -n ${_moddirname} ]] && _moddirname="-d ${_moddirname}/"
1168
1169 # ok, load the module, all its dependencies, and any firmware
1170 # it may require
1171 for_each_kmod_dep install_kmod_with_fw $_mod \
1172 --set-version $kernel ${_moddirname} $_mpargs
1173 ((_ret+=$?))
1174 ;;
1175 esac
1176 }
1177
1178 function instmods_1() {
1179 local _ret=0 _mod _mpargs
1180 if (($# == 0)); then # filenames from stdin
1181 while read _mod; do
1182 inst1mod "${_mod%.ko*}"
1183 done
1184 fi
1185 while (($# > 0)); do # filenames as arguments
1186 inst1mod ${1%.ko*}
1187 shift
1188 done
1189 return $_ret
1190 }
1191
1192 local _filter_not_found='FATAL: Module .* not found.'
1193 # Capture all stderr from modprobe to _fderr. We could use {var}>...
1194 # redirections, but that would make dracut require bash4 at least.
1195 eval "( instmods_1 \"\$@\" ) ${_fderr}>&1" \
1196 | while read line; do [[ "$line" =~ $_filter_not_found ]] || echo $line;done | derror
1197 return $?
1198 }