]> git.ipfire.org Git - thirdparty/dracut.git/blob - dracut-functions.sh
fix(bluetooth): include it if Appearance matches the value assigned for keyboard
[thirdparty/dracut.git] / dracut-functions.sh
1 #!/bin/bash
2 #
3 # functions used by dracut and other tools.
4 #
5 # Copyright 2005-2009 Red Hat, Inc. All rights reserved.
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #
20 export LC_MESSAGES=C
21
22 # is_func <command>
23 # Check whether $1 is a function.
24 is_func() {
25 [[ "$(type -t "$1")" == "function" ]]
26 }
27
28 # Generic substring function. If $2 is in $1, return 0.
29 strstr() { [[ $1 == *"$2"* ]]; }
30 # Generic glob matching function. If glob pattern $2 matches anywhere in $1, OK
31 strglobin() { [[ $1 == *$2* ]]; }
32 # Generic glob matching function. If glob pattern $2 matches all of $1, OK
33 # shellcheck disable=SC2053
34 strglob() { [[ $1 == $2 ]]; }
35 # returns OK if $1 contains literal string $2 at the beginning, and isn't empty
36 str_starts() { [ "${1#"$2"*}" != "$1" ]; }
37 # returns OK if $1 contains literal string $2 at the end, and isn't empty
38 str_ends() { [ "${1%*"$2"}" != "$1" ]; }
39
40 trim() {
41 local var="$*"
42 var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters
43 var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters
44 printf "%s" "$var"
45 }
46
47 # find a binary. If we were not passed the full path directly,
48 # search in the usual places to find the binary.
49 find_binary() {
50 local _delim
51 local _path
52 local l
53 local p
54 [[ -z ${1##/*} ]] || _delim="/"
55
56 if [[ $1 == *.so* ]]; then
57 # shellcheck disable=SC2154
58 for l in $libdirs; do
59 _path="${l}${_delim}${1}"
60 if { $DRACUT_LDD "${dracutsysrootdir}${_path}" &> /dev/null; }; then
61 printf "%s\n" "${_path}"
62 return 0
63 fi
64 done
65 _path="${_delim}${1}"
66 if { $DRACUT_LDD "${dracutsysrootdir}${_path}" &> /dev/null; }; then
67 printf "%s\n" "${_path}"
68 return 0
69 fi
70 fi
71 if [[ $1 == */* ]]; then
72 _path="${_delim}${1}"
73 if [[ -L ${dracutsysrootdir}${_path} ]] || [[ -x ${dracutsysrootdir}${_path} ]]; then
74 printf "%s\n" "${_path}"
75 return 0
76 fi
77 fi
78 for p in $DRACUT_PATH; do
79 _path="${p}${_delim}${1}"
80 if [[ -L ${dracutsysrootdir}${_path} ]] || [[ -x ${dracutsysrootdir}${_path} ]]; then
81 printf "%s\n" "${_path}"
82 return 0
83 fi
84 done
85
86 [[ -n $dracutsysrootdir ]] && return 1
87 type -P "${1##*/}"
88 }
89
90 ldconfig_paths() {
91 $DRACUT_LDCONFIG ${dracutsysrootdir:+-r ${dracutsysrootdir} -f /etc/ld.so.conf} -pN 2> /dev/null | grep -E -v '/(lib|lib64|usr/lib|usr/lib64)/[^/]*$' | sed -n 's,.* => \(.*\)/.*,\1,p' | sort | uniq
92 }
93
94 # Version comparision function. Assumes Linux style version scheme.
95 # $1 = version a
96 # $2 = comparision op (gt, ge, eq, le, lt, ne)
97 # $3 = version b
98 vercmp() {
99 local _n1
100 read -r -a _n1 <<< "${1//./ }"
101 local _op=$2
102 local _n2
103 read -r -a _n2 <<< "${3//./ }"
104 local _i _res
105
106 for ((_i = 0; ; _i++)); do
107 if [[ ! ${_n1[_i]}${_n2[_i]} ]]; then
108 _res=0
109 elif ((${_n1[_i]:-0} > ${_n2[_i]:-0})); then
110 _res=1
111 elif ((${_n1[_i]:-0} < ${_n2[_i]:-0})); then
112 _res=2
113 else
114 continue
115 fi
116 break
117 done
118
119 case $_op in
120 gt) ((_res == 1)) ;;
121 ge) ((_res != 2)) ;;
122 eq) ((_res == 0)) ;;
123 le) ((_res != 1)) ;;
124 lt) ((_res == 2)) ;;
125 ne) ((_res != 0)) ;;
126 esac
127 }
128
129 # Create all subdirectories for given path without creating the last element.
130 # $1 = path
131 mksubdirs() {
132 # shellcheck disable=SC2174
133 [[ -e ${1%/*} ]] || mkdir -m 0755 -p -- "${1%/*}"
134 }
135
136 # Function prints global variables in format name=value line by line.
137 # $@ = list of global variables' name
138 print_vars() {
139 local _var _value
140
141 for _var in "$@"; do
142 eval printf -v _value "%s" \""\$$_var"\"
143 [[ ${_value} ]] && printf '%s="%s"\n' "$_var" "$_value"
144 done
145 }
146
147 # normalize_path <path>
148 # Prints the normalized path, where it removes any duplicated
149 # and trailing slashes.
150 # Example:
151 # $ normalize_path ///test/test//
152 # /test/test
153 normalize_path() {
154 # shellcheck disable=SC2064
155 trap "$(shopt -p extglob)" RETURN
156 shopt -q -s extglob
157 local p=${1//+(\/)//}
158 printf "%s\n" "${p%/}"
159 }
160
161 # convert_abs_rel <from> <to>
162 # Prints the relative path, when creating a symlink to <to> from <from>.
163 # Example:
164 # $ convert_abs_rel /usr/bin/test /bin/test-2
165 # ../../bin/test-2
166 # $ ln -s $(convert_abs_rel /usr/bin/test /bin/test-2) /usr/bin/test
167 convert_abs_rel() {
168 local __current __absolute __abssize __cursize __newpath
169 local -i __i __level
170
171 set -- "$(normalize_path "$1")" "$(normalize_path "$2")"
172
173 # corner case #1 - self looping link
174 [[ $1 == "$2" ]] && {
175 printf "%s\n" "${1##*/}"
176 return
177 }
178
179 # corner case #2 - own dir link
180 [[ ${1%/*} == "$2" ]] && {
181 printf ".\n"
182 return
183 }
184
185 IFS=/ read -r -a __current <<< "$1"
186 IFS=/ read -r -a __absolute <<< "$2"
187
188 __abssize=${#__absolute[@]}
189 __cursize=${#__current[@]}
190
191 while [[ ${__absolute[__level]} == "${__current[__level]}" ]]; do
192 ((__level++))
193 if ((__level > __abssize || __level > __cursize)); then
194 break
195 fi
196 done
197
198 for ((__i = __level; __i < __cursize - 1; __i++)); do
199 if ((__i > __level)); then
200 __newpath=$__newpath"/"
201 fi
202 __newpath=$__newpath".."
203 done
204
205 for ((__i = __level; __i < __abssize; __i++)); do
206 if [[ -n $__newpath ]]; then
207 __newpath=$__newpath"/"
208 fi
209 __newpath=$__newpath${__absolute[__i]}
210 done
211
212 printf -- "%s\n" "$__newpath"
213 }
214
215 # get_fs_env <device>
216 # Get and the ID_FS_TYPE variable from udev for a device.
217 # Example:
218 # $ get_fs_env /dev/sda2
219 # ext4
220 get_fs_env() {
221 [[ $1 ]] || return
222 unset ID_FS_TYPE
223 ID_FS_TYPE=$(blkid -u filesystem -o export -- "$1" \
224 | while read -r line || [ -n "$line" ]; do
225 if [[ $line == "TYPE="* ]]; then
226 printf "%s" "${line#TYPE=}"
227 exit 0
228 fi
229 done)
230 if [[ $ID_FS_TYPE ]]; then
231 printf "%s" "$ID_FS_TYPE"
232 return 0
233 fi
234 return 1
235 }
236
237 # get_maj_min <device>
238 # Prints the major and minor of a device node.
239 # Example:
240 # $ get_maj_min /dev/sda2
241 # 8:2
242 get_maj_min() {
243 local _majmin
244 local _out
245
246 if [[ $get_maj_min_cache_file ]]; then
247 _out="$(grep -m1 -oE "^$1 \S+$" "$get_maj_min_cache_file" | awk '{print $NF}')"
248 fi
249
250 if ! [[ "$_out" ]]; then
251 _majmin="$(stat -L -c '%t:%T' "$1" 2> /dev/null)"
252 _out="$(printf "%s" "$((0x${_majmin%:*})):$((0x${_majmin#*:}))")"
253 if [[ $get_maj_min_cache_file ]]; then
254 echo "$1 $_out" >> "$get_maj_min_cache_file"
255 fi
256 fi
257 echo -n "$_out"
258 }
259
260 # get_devpath_block <device>
261 # get the DEVPATH in /sys of a block device
262 get_devpath_block() {
263 local _majmin _i
264 _majmin=$(get_maj_min "$1")
265
266 for _i in /sys/block/*/dev /sys/block/*/*/dev; do
267 [[ -e $_i ]] || continue
268 if [[ $_majmin == "$(< "$_i")" ]]; then
269 printf "%s" "${_i%/dev}"
270 return 0
271 fi
272 done
273 return 1
274 }
275
276 # get a persistent path from a device
277 get_persistent_dev() {
278 local i _tmp _dev _pol
279
280 _dev=$(get_maj_min "$1")
281 [ -z "$_dev" ] && return
282
283 if [[ -n $persistent_policy ]]; then
284 _pol="/dev/disk/${persistent_policy}/*"
285 else
286 _pol=
287 fi
288
289 for i in \
290 $_pol \
291 /dev/mapper/* \
292 /dev/disk/by-uuid/* \
293 /dev/disk/by-label/* \
294 /dev/disk/by-partuuid/* \
295 /dev/disk/by-partlabel/* \
296 /dev/disk/by-id/* \
297 /dev/disk/by-path/*; do
298 [[ -e $i ]] || continue
299 [[ $i == /dev/mapper/control ]] && continue
300 [[ $i == /dev/mapper/mpath* ]] && continue
301 _tmp=$(get_maj_min "$i")
302 if [ "$_tmp" = "$_dev" ]; then
303 printf -- "%s" "$i"
304 return
305 fi
306 done
307 printf -- "%s" "$1"
308 }
309
310 expand_persistent_dev() {
311 local _dev=$1
312
313 case "$_dev" in
314 LABEL=*)
315 _dev="/dev/disk/by-label/${_dev#LABEL=}"
316 ;;
317 UUID=*)
318 _dev="${_dev#UUID=}"
319 _dev="${_dev,,}"
320 _dev="/dev/disk/by-uuid/${_dev}"
321 ;;
322 PARTUUID=*)
323 _dev="${_dev#PARTUUID=}"
324 _dev="${_dev,,}"
325 _dev="/dev/disk/by-partuuid/${_dev}"
326 ;;
327 PARTLABEL=*)
328 _dev="/dev/disk/by-partlabel/${_dev#PARTLABEL=}"
329 ;;
330 esac
331 printf "%s" "$_dev"
332 }
333
334 shorten_persistent_dev() {
335 local _dev="$1"
336 case "$_dev" in
337 /dev/disk/by-uuid/*)
338 printf "%s" "UUID=${_dev##*/}"
339 ;;
340 /dev/disk/by-label/*)
341 printf "%s" "LABEL=${_dev##*/}"
342 ;;
343 /dev/disk/by-partuuid/*)
344 printf "%s" "PARTUUID=${_dev##*/}"
345 ;;
346 /dev/disk/by-partlabel/*)
347 printf "%s" "PARTLABEL=${_dev##*/}"
348 ;;
349 *)
350 printf "%s" "$_dev"
351 ;;
352 esac
353 }
354
355 # find_block_device <mountpoint>
356 # Prints the major and minor number of the block device
357 # for a given mountpoint.
358 # Unless $use_fstab is set to "yes" the functions
359 # uses /proc/self/mountinfo as the primary source of the
360 # information and only falls back to /etc/fstab, if the mountpoint
361 # is not found there.
362 # Example:
363 # $ find_block_device /usr
364 # 8:4
365 find_block_device() {
366 local _dev _majmin _find_mpt
367 _find_mpt="$1"
368
369 if [[ $use_fstab != yes ]]; then
370 [[ -d $_find_mpt/. ]]
371 findmnt -e -v -n -o 'MAJ:MIN,SOURCE' --target "$_find_mpt" | {
372 while read -r _majmin _dev || [ -n "$_dev" ]; do
373 if [[ -b $_dev ]]; then
374 if ! [[ $_majmin ]] || [[ $_majmin == 0:* ]]; then
375 _majmin=$(get_maj_min "$_dev")
376 fi
377 if [[ $_majmin ]]; then
378 printf "%s\n" "$_majmin"
379 else
380 printf "%s\n" "$_dev"
381 fi
382 return 0
383 fi
384 if [[ $_dev == *:* ]]; then
385 printf "%s\n" "$_dev"
386 return 0
387 fi
388 done
389 return 1
390 } && return 0
391 fi
392 # fall back to /etc/fstab
393 [[ ! -f "$dracutsysrootdir"/etc/fstab ]] && return 1
394
395 findmnt -e --fstab -v -n -o 'MAJ:MIN,SOURCE' --target "$_find_mpt" | {
396 while read -r _majmin _dev || [ -n "$_dev" ]; do
397 if ! [[ $_dev ]]; then
398 _dev="$_majmin"
399 unset _majmin
400 fi
401 if [[ -b $_dev ]]; then
402 [[ $_majmin ]] || _majmin=$(get_maj_min "$_dev")
403 if [[ $_majmin ]]; then
404 printf "%s\n" "$_majmin"
405 else
406 printf "%s\n" "$_dev"
407 fi
408 return 0
409 fi
410 if [[ $_dev == *:* ]]; then
411 printf "%s\n" "$_dev"
412 return 0
413 fi
414 done
415 return 1
416 } && return 0
417
418 return 1
419 }
420
421 # find_mp_fstype <mountpoint>
422 # Echo the filesystem type for a given mountpoint.
423 # /proc/self/mountinfo is taken as the primary source of information
424 # and /etc/fstab is used as a fallback.
425 # No newline is appended!
426 # Example:
427 # $ find_mp_fstype /;echo
428 # ext4
429 find_mp_fstype() {
430 local _fs
431
432 if [[ $use_fstab != yes ]]; then
433 findmnt -e -v -n -o 'FSTYPE' --target "$1" | {
434 while read -r _fs || [ -n "$_fs" ]; do
435 [[ $_fs ]] || continue
436 [[ $_fs == "autofs" ]] && continue
437 printf "%s" "$_fs"
438 return 0
439 done
440 return 1
441 } && return 0
442 fi
443
444 [[ ! -f "$dracutsysrootdir"/etc/fstab ]] && return 1
445
446 findmnt --fstab -e -v -n -o 'FSTYPE' --target "$1" | {
447 while read -r _fs || [ -n "$_fs" ]; do
448 [[ $_fs ]] || continue
449 [[ $_fs == "autofs" ]] && continue
450 printf "%s" "$_fs"
451 return 0
452 done
453 return 1
454 } && return 0
455
456 return 1
457 }
458
459 # find_dev_fstype <device>
460 # Echo the filesystem type for a given device.
461 # /proc/self/mountinfo is taken as the primary source of information
462 # and /etc/fstab is used as a fallback.
463 # No newline is appended!
464 # Example:
465 # $ find_dev_fstype /dev/sda2;echo
466 # ext4
467 find_dev_fstype() {
468 local _find_dev _fs
469 _find_dev="$1"
470 if ! [[ $_find_dev == /dev* ]]; then
471 [[ -b "/dev/block/$_find_dev" ]] && _find_dev="/dev/block/$_find_dev"
472 fi
473
474 if [[ $use_fstab != yes ]]; then
475 findmnt -e -v -n -o 'FSTYPE' --source "$_find_dev" | {
476 while read -r _fs || [ -n "$_fs" ]; do
477 [[ $_fs ]] || continue
478 [[ $_fs == "autofs" ]] && continue
479 printf "%s" "$_fs"
480 return 0
481 done
482 return 1
483 } && return 0
484 fi
485
486 [[ ! -f "$dracutsysrootdir"/etc/fstab ]] && return 1
487
488 findmnt --fstab -e -v -n -o 'FSTYPE' --source "$_find_dev" | {
489 while read -r _fs || [ -n "$_fs" ]; do
490 [[ $_fs ]] || continue
491 [[ $_fs == "autofs" ]] && continue
492 printf "%s" "$_fs"
493 return 0
494 done
495 return 1
496 } && return 0
497
498 return 1
499 }
500
501 # find_mp_fsopts <mountpoint>
502 # Echo the filesystem options for a given mountpoint.
503 # /proc/self/mountinfo is taken as the primary source of information
504 # and /etc/fstab is used as a fallback.
505 # No newline is appended!
506 # Example:
507 # $ find_mp_fsopts /;echo
508 # rw,relatime,discard,data=ordered
509 find_mp_fsopts() {
510 if [[ $use_fstab != yes ]]; then
511 findmnt -e -v -n -o 'OPTIONS' --target "$1" 2> /dev/null && return 0
512 fi
513
514 [[ ! -f "$dracutsysrootdir"/etc/fstab ]] && return 1
515
516 findmnt --fstab -e -v -n -o 'OPTIONS' --target "$1"
517 }
518
519 # find_dev_fsopts <device>
520 # Echo the filesystem options for a given device.
521 # /proc/self/mountinfo is taken as the primary source of information
522 # and /etc/fstab is used as a fallback.
523 # if `use_fstab == yes`, then only `/etc/fstab` is used.
524 #
525 # Example:
526 # $ find_dev_fsopts /dev/sda2
527 # rw,relatime,discard,data=ordered
528 find_dev_fsopts() {
529 local _find_dev
530 _find_dev="$1"
531 if ! [[ $_find_dev == /dev* ]]; then
532 [[ -b "/dev/block/$_find_dev" ]] && _find_dev="/dev/block/$_find_dev"
533 fi
534
535 if [[ $use_fstab != yes ]]; then
536 findmnt -e -v -n -o 'OPTIONS' --source "$_find_dev" 2> /dev/null && return 0
537 fi
538
539 [[ ! -f "$dracutsysrootdir"/etc/fstab ]] && return 1
540
541 findmnt --fstab -e -v -n -o 'OPTIONS' --source "$_find_dev"
542 }
543
544 # finds the major:minor of the block device backing the root filesystem.
545 find_root_block_device() { find_block_device /; }
546
547 # for_each_host_dev_fs <func>
548 # Execute "<func> <dev> <filesystem>" for every "<dev> <fs>" pair found
549 # in ${host_fs_types[@]}
550 for_each_host_dev_fs() {
551 local _func="$1"
552 local _dev
553 local _ret=1
554
555 [[ "${#host_fs_types[@]}" ]] || return 2
556
557 for _dev in "${!host_fs_types[@]}"; do
558 $_func "$_dev" "${host_fs_types[$_dev]}" && _ret=0
559 done
560 return $_ret
561 }
562
563 host_fs_all() {
564 printf "%s\n" "${host_fs_types[@]}"
565 }
566
567 # Walk all the slave relationships for a given block device.
568 # Stop when our helper function returns success
569 # $1 = function to call on every found block device
570 # $2 = block device in major:minor format
571 check_block_and_slaves() {
572 local _x
573 [[ -b /dev/block/$2 ]] || return 1 # Not a block device? So sorry.
574 if ! lvm_internal_dev "$2"; then "$1" "$2" && return; fi
575 check_vol_slaves "$@" && return 0
576 if [[ -f /sys/dev/block/$2/../dev ]] && [[ /sys/dev/block/$2/../subsystem -ef /sys/class/block ]]; then
577 check_block_and_slaves "$1" "$(< "/sys/dev/block/$2/../dev")" && return 0
578 fi
579 for _x in /sys/dev/block/"$2"/slaves/*; do
580 [[ -f $_x/dev ]] || continue
581 [[ $_x/subsystem -ef /sys/class/block ]] || continue
582 check_block_and_slaves "$1" "$(< "$_x/dev")" && return 0
583 done
584 return 1
585 }
586
587 check_block_and_slaves_all() {
588 local _x _ret=1
589 [[ -b /dev/block/$2 ]] || return 1 # Not a block device? So sorry.
590 if ! lvm_internal_dev "$2" && "$1" "$2"; then
591 _ret=0
592 fi
593 check_vol_slaves_all "$@" && return 0
594 if [[ -f /sys/dev/block/$2/../dev ]] && [[ /sys/dev/block/$2/../subsystem -ef /sys/class/block ]]; then
595 check_block_and_slaves_all "$1" "$(< "/sys/dev/block/$2/../dev")" && _ret=0
596 fi
597 for _x in /sys/dev/block/"$2"/slaves/*; do
598 [[ -f $_x/dev ]] || continue
599 [[ $_x/subsystem -ef /sys/class/block ]] || continue
600 check_block_and_slaves_all "$1" "$(< "$_x/dev")" && _ret=0
601 done
602 return $_ret
603 }
604 # for_each_host_dev_and_slaves <func>
605 # Execute "<func> <dev>" for every "<dev>" found
606 # in ${host_devs[@]} and their slaves
607 for_each_host_dev_and_slaves_all() {
608 local _func="$1"
609 local _dev
610 local _ret=1
611
612 [[ "${host_devs[*]}" ]] || return 2
613
614 for _dev in "${host_devs[@]}"; do
615 [[ -b $_dev ]] || continue
616 if check_block_and_slaves_all "$_func" "$(get_maj_min "$_dev")"; then
617 _ret=0
618 fi
619 done
620 return $_ret
621 }
622
623 for_each_host_dev_and_slaves() {
624 local _func="$1"
625 local _dev
626
627 [[ "${host_devs[*]}" ]] || return 2
628
629 for _dev in "${host_devs[@]}"; do
630 [[ -b $_dev ]] || continue
631 check_block_and_slaves "$_func" "$(get_maj_min "$_dev")" && return 0
632 done
633 return 1
634 }
635
636 # /sys/dev/block/major:minor is symbol link to real hardware device
637 # go downstream $(realpath /sys/dev/block/major:minor) to detect driver
638 get_blockdev_drv_through_sys() {
639 local _block_mods=""
640 local _path
641
642 _path=$(realpath "$1")
643 while true; do
644 if [[ -L "$_path"/driver/module ]]; then
645 _mod=$(realpath "$_path"/driver/module)
646 _mod=$(basename "$_mod")
647 _block_mods="$_block_mods $_mod"
648 fi
649 _path=$(dirname "$_path")
650 if [[ $_path == '/sys/devices' ]] || [[ $_path == '/' ]]; then
651 break
652 fi
653 done
654 echo "$_block_mods"
655 }
656
657 # ugly workaround for the lvm design
658 # There is no volume group device,
659 # so, there are no slave devices for volume groups.
660 # Logical volumes only have the slave devices they really live on,
661 # but you cannot create the logical volume without the volume group.
662 # And the volume group might be bigger than the devices the LV needs.
663 check_vol_slaves() {
664 local _vg _pv _dm _majmin
665 _majmin="$2"
666 _dm=/sys/dev/block/$_majmin/dm
667 [[ -f $_dm/uuid && $(< "$_dm"/uuid) =~ LVM-* ]] || return 1
668 _vg=$(dmsetup splitname --noheadings -o vg_name "$(< "$_dm/name")")
669 # strip space
670 _vg="${_vg//[[:space:]]/}"
671 if [[ $_vg ]]; then
672 for _pv in $(lvm vgs --noheadings -o pv_name "$_vg" 2> /dev/null); do
673 check_block_and_slaves "$1" "$(get_maj_min "$_pv")" && return 0
674 done
675 fi
676 return 1
677 }
678
679 check_vol_slaves_all() {
680 local _vg _pv _majmin
681 _majmin="$2"
682 _dm="/sys/dev/block/$_majmin/dm"
683 [[ -f $_dm/uuid && $(< "$_dm"/uuid) =~ LVM-* ]] || return 1
684 _vg=$(dmsetup splitname --noheadings -o vg_name "$(< "$_dm/name")")
685 # strip space
686 _vg="${_vg//[[:space:]]/}"
687 if [[ $_vg ]]; then
688 # when filter/global_filter is set, lvm may be failed
689 if ! lvm lvs --noheadings -o vg_name "$_vg" 2> /dev/null 1> /dev/null; then
690 return 1
691 fi
692
693 for _pv in $(lvm vgs --noheadings -o pv_name "$_vg" 2> /dev/null); do
694 check_block_and_slaves_all "$1" "$(get_maj_min "$_pv")"
695 done
696 return 0
697 fi
698 return 1
699 }
700
701 # fs_get_option <filesystem options> <search for option>
702 # search for a specific option in a bunch of filesystem options
703 # and return the value
704 fs_get_option() {
705 local _fsopts=$1
706 local _option=$2
707 local OLDIFS="$IFS"
708 IFS=,
709 # shellcheck disable=SC2086
710 set -- $_fsopts
711 IFS="$OLDIFS"
712 while [ $# -gt 0 ]; do
713 case $1 in
714 $_option=*)
715 echo "${1#"${_option}"=}"
716 break
717 ;;
718 esac
719 shift
720 done
721 }
722
723 check_kernel_config() {
724 local _config_opt="$1"
725 local _config_file
726 [[ -f $dracutsysrootdir/boot/config-$kernel ]] \
727 && _config_file="/boot/config-$kernel"
728 [[ -f $dracutsysrootdir/lib/modules/$kernel/config ]] \
729 && _config_file="/lib/modules/$kernel/config"
730
731 # no kernel config file, so return true
732 [[ $_config_file ]] || return 0
733
734 grep -q -F "${_config_opt}=" "$dracutsysrootdir$_config_file" && return 0
735 return 1
736 }
737
738 # 0 if the kernel module is either built-in or available
739 # 1 if the kernel module is not enabled
740 check_kernel_module() {
741 modprobe -d "$dracutsysrootdir" -S "$kernel" --dry-run "$1" &> /dev/null || return 1
742 }
743
744 # get_cpu_vendor
745 # Only two values are returned: AMD or Intel
746 get_cpu_vendor() {
747 if grep -qE AMD /proc/cpuinfo; then
748 printf "AMD"
749 fi
750 if grep -qE Intel /proc/cpuinfo; then
751 printf "Intel"
752 fi
753 }
754
755 # get_host_ucode
756 # Get the hosts' ucode file based on the /proc/cpuinfo
757 get_ucode_file() {
758 local family
759 local model
760 local stepping
761 family=$(grep -E "cpu family" /proc/cpuinfo | head -1 | sed "s/.*:\ //")
762 model=$(grep -E "model" /proc/cpuinfo | grep -v name | head -1 | sed "s/.*:\ //")
763 stepping=$(grep -E "stepping" /proc/cpuinfo | head -1 | sed "s/.*:\ //")
764
765 if [[ "$(get_cpu_vendor)" == "AMD" ]]; then
766 if [[ $family -ge 21 ]]; then
767 printf "microcode_amd_fam%xh.bin" "$family"
768 else
769 printf "microcode_amd.bin"
770 fi
771 fi
772 if [[ "$(get_cpu_vendor)" == "Intel" ]]; then
773 # The /proc/cpuinfo are in decimal.
774 printf "%02x-%02x-%02x" "${family}" "${model}" "${stepping}"
775 fi
776 }
777
778 # Not every device in /dev/mapper should be examined.
779 # If it is an LVM device, touch only devices which have /dev/VG/LV symlink.
780 lvm_internal_dev() {
781 local dev_dm_dir=/sys/dev/block/$1/dm
782 [[ ! -f $dev_dm_dir/uuid || $(< "$dev_dm_dir"/uuid) != LVM-* ]] && return 1 # Not an LVM device
783 local DM_VG_NAME DM_LV_NAME DM_LV_LAYER
784 eval "$(dmsetup splitname --nameprefixes --noheadings --rows "$(< "$dev_dm_dir"/name)" 2> /dev/null)"
785 [[ ${DM_VG_NAME} ]] && [[ ${DM_LV_NAME} ]] || return 0 # Better skip this!
786 [[ ${DM_LV_LAYER} ]] || [[ ! -L /dev/${DM_VG_NAME}/${DM_LV_NAME} ]]
787 }
788
789 btrfs_devs() {
790 local _mp="$1"
791 btrfs device usage "$_mp" \
792 | while read -r _dev _; do
793 str_starts "$_dev" "/" || continue
794 _dev=${_dev%,}
795 printf -- "%s\n" "$_dev"
796 done
797 }
798
799 zfs_devs() {
800 local _mp="$1"
801 zpool list -H -v -P "${_mp%%/*}" | awk -F$'\t' '$2 ~ /^\// {print $2}' \
802 | while read -r _dev; do
803 realpath "${_dev}"
804 done
805 }
806
807 iface_for_remote_addr() {
808 # shellcheck disable=SC2046
809 set -- $(ip -o route get to "$1")
810 while [ $# -gt 0 ]; do
811 case $1 in
812 dev)
813 echo "$2"
814 return
815 ;;
816 esac
817 shift
818 done
819 }
820
821 local_addr_for_remote_addr() {
822 # shellcheck disable=SC2046
823 set -- $(ip -o route get to "$1")
824 while [ $# -gt 0 ]; do
825 case $1 in
826 src)
827 echo "$2"
828 return
829 ;;
830 esac
831 shift
832 done
833 }
834
835 peer_for_addr() {
836 local addr=$1
837 local qtd
838
839 # quote periods in IPv4 address
840 qtd=${addr//./\\.}
841 ip -o addr show \
842 | sed -n 's%^.* '"$qtd"' peer \([0-9a-f.:]\{1,\}\(/[0-9]*\)\?\).*$%\1%p'
843 }
844
845 netmask_for_addr() {
846 local addr=$1
847 local qtd
848
849 # quote periods in IPv4 address
850 qtd=${addr//./\\.}
851 ip -o addr show | sed -n 's,^.* '"$qtd"'/\([0-9]*\) .*$,\1,p'
852 }
853
854 gateway_for_iface() {
855 local ifname=$1 addr=$2
856
857 case $addr in
858 *.*) proto=4 ;;
859 *:*) proto=6 ;;
860 *) return ;;
861 esac
862 ip -o -$proto route show \
863 | sed -n "s/^default via \([0-9a-z.:]\{1,\}\) dev $ifname .*\$/\1/p"
864 }
865
866 # This works only for ifcfg-style network configuration!
867 bootproto_for_iface() {
868 local ifname=$1
869 local dir
870
871 # follow ifcfg settings for boot protocol
872 for dir in network-scripts network; do
873 [ -f "/etc/sysconfig/$dir/ifcfg-$ifname" ] && {
874 sed -n "s/BOOTPROTO=[\"']\?\([[:alnum:]]\{1,\}\)[\"']\?.*\$/\1/p" \
875 "/etc/sysconfig/$dir/ifcfg-$ifname"
876 return
877 }
878 done
879 }
880
881 is_unbracketed_ipv6_address() {
882 strglob "$1" '*:*' && ! strglob "$1" '\[*:*\]'
883 }
884
885 # Create an ip= string to set up networking such that the given
886 # remote address can be reached
887 ip_params_for_remote_addr() {
888 local remote_addr=$1
889 local ifname local_addr peer netmask gateway ifmac
890
891 [[ $remote_addr ]] || return 1
892 ifname=$(iface_for_remote_addr "$remote_addr")
893 [[ $ifname ]] || {
894 berror "failed to determine interface to connect to $remote_addr"
895 return 1
896 }
897
898 # ifname clause to bind the interface name to a MAC address
899 if [ -d "/sys/class/net/$ifname/bonding" ]; then
900 dinfo "Found bonded interface '${ifname}'. Make sure to provide an appropriate 'bond=' cmdline."
901 elif [ -e "/sys/class/net/$ifname/address" ]; then
902 ifmac=$(cat "/sys/class/net/$ifname/address")
903 [[ $ifmac ]] && printf 'ifname=%s:%s ' "${ifname}" "${ifmac}"
904 fi
905
906 bootproto=$(bootproto_for_iface "$ifname")
907 case $bootproto in
908 dhcp | dhcp6 | auto6) ;;
909 dhcp4)
910 bootproto=dhcp
911 ;;
912 static* | "")
913 bootproto=
914 ;;
915 *)
916 derror "bootproto \"$bootproto\" is unsupported by dracut, trying static configuration"
917 bootproto=
918 ;;
919 esac
920 if [[ $bootproto ]]; then
921 printf 'ip=%s:%s ' "${ifname}" "${bootproto}"
922 else
923 local_addr=$(local_addr_for_remote_addr "$remote_addr")
924 [[ $local_addr ]] || {
925 berror "failed to determine local address to connect to $remote_addr"
926 return 1
927 }
928 peer=$(peer_for_addr "$local_addr")
929 # Set peer or netmask, but not both
930 [[ $peer ]] || netmask=$(netmask_for_addr "$local_addr")
931 gateway=$(gateway_for_iface "$ifname" "$local_addr")
932 # Quote IPv6 addresses with brackets
933 is_unbracketed_ipv6_address "$local_addr" && local_addr="[$local_addr]"
934 is_unbracketed_ipv6_address "$peer" && peer="[$peer]"
935 is_unbracketed_ipv6_address "$gateway" && gateway="[$gateway]"
936 printf 'ip=%s:%s:%s:%s::%s:none ' \
937 "${local_addr}" "${peer}" "${gateway}" "${netmask}" "${ifname}"
938 fi
939
940 }
941
942 # block_is_nbd <maj:min>
943 # Check whether $1 is an nbd device
944 block_is_nbd() {
945 [[ -b /dev/block/$1 && $1 == 43:* ]]
946 }
947
948 # block_is_iscsi <maj:min>
949 # Check whether $1 is an iSCSI device
950 block_is_iscsi() {
951 local _dir
952 local _dev=$1
953 [[ -L "/sys/dev/block/$_dev" ]] || return
954 _dir="$(readlink -f "/sys/dev/block/$_dev")" || return
955 until [[ -d "$_dir/sys" || -d "$_dir/iscsi_session" ]]; do
956 _dir="$_dir/.."
957 done
958 [[ -d "$_dir/iscsi_session" ]]
959 }
960
961 # block_is_fcoe <maj:min>
962 # Check whether $1 is an FCoE device
963 # Will not work for HBAs that hide the ethernet aspect
964 # completely and present a pure FC device
965 block_is_fcoe() {
966 local _dir
967 local _dev=$1
968 [[ -L "/sys/dev/block/$_dev" ]] || return
969 _dir="$(readlink -f "/sys/dev/block/$_dev")"
970 until [[ -d "$_dir/sys" ]]; do
971 _dir="$_dir/.."
972 if [[ -d "$_dir/subsystem" ]]; then
973 subsystem=$(basename "$(readlink "$_dir"/subsystem)")
974 [[ $subsystem == "fcoe" ]] && return 0
975 fi
976 done
977 return 1
978 }
979
980 # block_is_netdevice <maj:min>
981 # Check whether $1 is a net device
982 block_is_netdevice() {
983 block_is_nbd "$1" || block_is_iscsi "$1" || block_is_fcoe "$1"
984 }
985
986 # convert the driver name given by udevadm to the corresponding kernel module name
987 get_module_name() {
988 local dev_driver
989 while read -r dev_driver; do
990 case "$dev_driver" in
991 mmcblk)
992 echo "mmc_block"
993 ;;
994 *)
995 echo "$dev_driver"
996 ;;
997 esac
998 done
999 }
1000
1001 # get the corresponding kernel modules of a /sys/class/*/* or/dev/* device
1002 get_dev_module() {
1003 local dev_attr_walk
1004 local dev_drivers
1005 local dev_paths
1006 dev_attr_walk=$(udevadm info -a "$1")
1007 dev_drivers=$(echo "$dev_attr_walk" \
1008 | sed -n 's/\s*DRIVERS=="\(\S\+\)"/\1/p' \
1009 | get_module_name)
1010
1011 # also return modalias info from sysfs paths parsed by udevadm
1012 dev_paths=$(echo "$dev_attr_walk" | sed -n 's/.*\(\/devices\/.*\)'\'':/\1/p')
1013 local dev_path
1014 for dev_path in $dev_paths; do
1015 local modalias_file="/sys$dev_path/modalias"
1016 if [ -e "$modalias_file" ]; then
1017 dev_drivers="$(printf "%s\n%s" "$dev_drivers" "$(cat "$modalias_file")")"
1018 fi
1019 done
1020
1021 # if no kernel modules found and device is in a virtual subsystem, follow symlinks
1022 if [[ -z $dev_drivers && $(udevadm info -q path "$1") == "/devices/virtual"* ]]; then
1023 local dev_vkernel
1024 local dev_vsubsystem
1025 local dev_vpath
1026 dev_vkernel=$(echo "$dev_attr_walk" | sed -n 's/\s*KERNELS=="\(\S\+\)"/\1/p' | tail -1)
1027 dev_vsubsystem=$(echo "$dev_attr_walk" | sed -n 's/\s*SUBSYSTEMS=="\(\S\+\)"/\1/p' | tail -1)
1028 dev_vpath="/sys/devices/virtual/$dev_vsubsystem/$dev_vkernel"
1029 if [[ -n $dev_vkernel && -n $dev_vsubsystem && -d $dev_vpath ]]; then
1030 local dev_links
1031 local dev_link
1032 dev_links=$(find "$dev_vpath" -maxdepth 1 -type l ! -name "subsystem" -exec readlink {} \;)
1033 for dev_link in $dev_links; do
1034 [[ -n $dev_drivers && ${dev_drivers: -1} != $'\n' ]] && dev_drivers+=$'\n'
1035 dev_drivers+=$(udevadm info -a "$dev_vpath/$dev_link" \
1036 | sed -n 's/\s*DRIVERS=="\(\S\+\)"/\1/p' \
1037 | get_module_name \
1038 | grep -v -e pcieport)
1039 done
1040 fi
1041 fi
1042 echo "$dev_drivers"
1043 }
1044
1045 # Check if file is in PE format
1046 pe_file_format() {
1047 if [[ $# -eq 1 ]]; then
1048 local magic
1049 magic=$(objdump -p "$1" \
1050 | awk '{if ($1 == "Magic"){print strtonum("0x"$2)}}')
1051 magic=$(printf "0x%x" "$magic")
1052 # 0x10b (PE32), 0x20b (PE32+)
1053 [[ $magic == 0x20b || $magic == 0x10b ]] && return 0
1054 fi
1055 return 1
1056 }
1057
1058 # Get specific data from the PE header
1059 pe_get_header_data() {
1060 local data_header
1061 [[ $# -ne "2" ]] && return 1
1062 [[ $(pe_file_format "$1") -eq 1 ]] && return 1
1063 data_header=$(objdump -p "$1" \
1064 | awk -v data="$2" '{if ($1 == data){print $2}}')
1065 echo "$data_header"
1066 }
1067
1068 # Get the SectionAlignment data from the PE header
1069 pe_get_section_align() {
1070 local align_hex
1071 [[ $# -ne "1" ]] && return 1
1072 align_hex=$(pe_get_header_data "$1" "SectionAlignment")
1073 [[ $? -eq 1 ]] && return 1
1074 echo "$((16#$align_hex))"
1075 }
1076
1077 # Get the ImageBase data from the PE header
1078 pe_get_image_base() {
1079 local base_image
1080 [[ $# -ne "1" ]] && return 1
1081 base_image=$(pe_get_header_data "$1" "ImageBase")
1082 [[ $? -eq 1 ]] && return 1
1083 echo "$((16#$base_image))"
1084 }