]>
Commit | Line | Data |
---|---|---|
898720b7 HH |
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 | PATH=/sbin:/bin:/usr/sbin:/usr/bin | |
5 | export PATH | |
6 | ||
0d6e798a HH |
7 | KERNEL_VER=${KERNEL_VER-$(uname -r)} |
8 | KERNEL_MODS="/lib/modules/$KERNEL_VER/" | |
898720b7 | 9 | |
0d6e798a HH |
10 | setup_basic_dirs() { |
11 | for d in usr/bin usr/sbin bin etc lib "$libdir" sbin tmp usr var var/log dev proc sys sysroot root run run/lock run/initramfs; do | |
898720b7 HH |
12 | if [ -L "/$d" ]; then |
13 | inst_symlink "/$d" | |
14 | else | |
0d6e798a | 15 | inst_dir "/$d" |
898720b7 HH |
16 | fi |
17 | done | |
18 | ||
19 | ln -sfn /run "$initdir/var/run" | |
20 | ln -sfn /run/lock "$initdir/var/lock" | |
21 | } | |
22 | ||
23 | inst_libs() { | |
24 | local _bin=$1 | |
25 | local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)' | |
26 | local _file _line | |
27 | ||
28 | LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do | |
29 | [[ $_line = 'not a dynamic executable' ]] && break | |
30 | ||
31 | if [[ $_line =~ $_so_regex ]]; then | |
32 | _file=${BASH_REMATCH[1]} | |
33 | [[ -e ${initdir}/$_file ]] && continue | |
34 | inst_library "$_file" | |
35 | continue | |
36 | fi | |
37 | ||
38 | if [[ $_line =~ not\ found ]]; then | |
39 | dfatal "Missing a shared library required by $_bin." | |
40 | dfatal "Run \"ldd $_bin\" to find out what it is." | |
41 | dfatal "$_line" | |
42 | dfatal "dracut cannot create an initrd." | |
43 | exit 1 | |
44 | fi | |
45 | done | |
46 | } | |
47 | ||
48 | import_testdir() { | |
49 | STATEFILE=".testdir" | |
50 | [[ -e $STATEFILE ]] && . $STATEFILE | |
51 | if [[ -z "$TESTDIR" ]] || [[ ! -d "$TESTDIR" ]]; then | |
52 | TESTDIR=$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX) | |
53 | echo "TESTDIR=\"$TESTDIR\"" > $STATEFILE | |
54 | export TESTDIR | |
55 | fi | |
56 | } | |
57 | ||
58 | ## @brief Converts numeric logging level to the first letter of level name. | |
59 | # | |
60 | # @param lvl Numeric logging level in range from 1 to 6. | |
61 | # @retval 1 if @a lvl is out of range. | |
62 | # @retval 0 if @a lvl is correct. | |
63 | # @result Echoes first letter of level name. | |
64 | _lvl2char() { | |
65 | case "$1" in | |
66 | 1) echo F;; | |
67 | 2) echo E;; | |
68 | 3) echo W;; | |
69 | 4) echo I;; | |
70 | 5) echo D;; | |
71 | 6) echo T;; | |
72 | *) return 1;; | |
73 | esac | |
74 | } | |
75 | ||
76 | ## @brief Internal helper function for _do_dlog() | |
77 | # | |
78 | # @param lvl Numeric logging level. | |
79 | # @param msg Message. | |
80 | # @retval 0 It's always returned, even if logging failed. | |
81 | # | |
82 | # @note This function is not supposed to be called manually. Please use | |
83 | # dtrace(), ddebug(), or others instead which wrap this one. | |
84 | # | |
85 | # This function calls _do_dlog() either with parameter msg, or if | |
86 | # none is given, it will read standard input and will use every line as | |
87 | # a message. | |
88 | # | |
89 | # This enables: | |
90 | # dwarn "This is a warning" | |
91 | # echo "This is a warning" | dwarn | |
92 | LOG_LEVEL=4 | |
93 | ||
94 | dlog() { | |
95 | [ -z "$LOG_LEVEL" ] && return 0 | |
96 | [ $1 -le $LOG_LEVEL ] || return 0 | |
97 | local lvl="$1"; shift | |
98 | local lvlc=$(_lvl2char "$lvl") || return 0 | |
99 | ||
100 | if [ $# -ge 1 ]; then | |
101 | echo "$lvlc: $*" | |
102 | else | |
103 | while read line; do | |
104 | echo "$lvlc: " "$line" | |
105 | done | |
106 | fi | |
107 | } | |
108 | ||
109 | ## @brief Logs message at TRACE level (6) | |
110 | # | |
111 | # @param msg Message. | |
112 | # @retval 0 It's always returned, even if logging failed. | |
113 | dtrace() { | |
114 | set +x | |
115 | dlog 6 "$@" | |
116 | [ -n "$debug" ] && set -x || : | |
117 | } | |
118 | ||
119 | ## @brief Logs message at DEBUG level (5) | |
120 | # | |
121 | # @param msg Message. | |
122 | # @retval 0 It's always returned, even if logging failed. | |
123 | ddebug() { | |
0d6e798a | 124 | # set +x |
898720b7 | 125 | dlog 5 "$@" |
0d6e798a | 126 | # [ -n "$debug" ] && set -x || : |
898720b7 HH |
127 | } |
128 | ||
129 | ## @brief Logs message at INFO level (4) | |
130 | # | |
131 | # @param msg Message. | |
132 | # @retval 0 It's always returned, even if logging failed. | |
133 | dinfo() { | |
134 | set +x | |
135 | dlog 4 "$@" | |
136 | [ -n "$debug" ] && set -x || : | |
137 | } | |
138 | ||
139 | ## @brief Logs message at WARN level (3) | |
140 | # | |
141 | # @param msg Message. | |
142 | # @retval 0 It's always returned, even if logging failed. | |
143 | dwarn() { | |
144 | set +x | |
145 | dlog 3 "$@" | |
146 | [ -n "$debug" ] && set -x || : | |
147 | } | |
148 | ||
149 | ## @brief Logs message at ERROR level (2) | |
150 | # | |
151 | # @param msg Message. | |
152 | # @retval 0 It's always returned, even if logging failed. | |
153 | derror() { | |
0d6e798a | 154 | # set +x |
898720b7 | 155 | dlog 2 "$@" |
0d6e798a | 156 | # [ -n "$debug" ] && set -x || : |
898720b7 HH |
157 | } |
158 | ||
159 | ## @brief Logs message at FATAL level (1) | |
160 | # | |
161 | # @param msg Message. | |
162 | # @retval 0 It's always returned, even if logging failed. | |
163 | dfatal() { | |
164 | set +x | |
165 | dlog 1 "$@" | |
166 | [ -n "$debug" ] && set -x || : | |
167 | } | |
168 | ||
169 | ||
170 | # Generic substring function. If $2 is in $1, return 0. | |
171 | strstr() { [ "${1#*$2*}" != "$1" ]; } | |
172 | ||
173 | # normalize_path <path> | |
174 | # Prints the normalized path, where it removes any duplicated | |
175 | # and trailing slashes. | |
176 | # Example: | |
177 | # $ normalize_path ///test/test// | |
178 | # /test/test | |
179 | normalize_path() { | |
180 | shopt -q -s extglob | |
181 | set -- "${1//+(\/)//}" | |
182 | shopt -q -u extglob | |
183 | echo "${1%/}" | |
184 | } | |
185 | ||
186 | # convert_abs_rel <from> <to> | |
187 | # Prints the relative path, when creating a symlink to <to> from <from>. | |
188 | # Example: | |
189 | # $ convert_abs_rel /usr/bin/test /bin/test-2 | |
190 | # ../../bin/test-2 | |
191 | # $ ln -s $(convert_abs_rel /usr/bin/test /bin/test-2) /usr/bin/test | |
192 | convert_abs_rel() { | |
193 | local __current __absolute __abssize __cursize __newpath | |
194 | local -i __i __level | |
195 | ||
196 | set -- "$(normalize_path "$1")" "$(normalize_path "$2")" | |
197 | ||
198 | # corner case #1 - self looping link | |
199 | [[ "$1" == "$2" ]] && { echo "${1##*/}"; return; } | |
200 | ||
201 | # corner case #2 - own dir link | |
202 | [[ "${1%/*}" == "$2" ]] && { echo "."; return; } | |
203 | ||
204 | IFS="/" __current=($1) | |
205 | IFS="/" __absolute=($2) | |
206 | ||
207 | __abssize=${#__absolute[@]} | |
208 | __cursize=${#__current[@]} | |
209 | ||
210 | while [[ ${__absolute[__level]} == ${__current[__level]} ]] | |
211 | do | |
212 | (( __level++ )) | |
213 | if (( __level > __abssize || __level > __cursize )) | |
214 | then | |
215 | break | |
216 | fi | |
217 | done | |
218 | ||
219 | for ((__i = __level; __i < __cursize-1; __i++)) | |
220 | do | |
221 | if ((__i > __level)) | |
222 | then | |
223 | __newpath=$__newpath"/" | |
224 | fi | |
225 | __newpath=$__newpath".." | |
226 | done | |
227 | ||
228 | for ((__i = __level; __i < __abssize; __i++)) | |
229 | do | |
230 | if [[ -n $__newpath ]] | |
231 | then | |
232 | __newpath=$__newpath"/" | |
233 | fi | |
234 | __newpath=$__newpath${__absolute[__i]} | |
235 | done | |
236 | ||
237 | echo "$__newpath" | |
238 | } | |
239 | ||
240 | ||
241 | # Install a directory, keeping symlinks as on the original system. | |
242 | # Example: if /lib points to /lib64 on the host, "inst_dir /lib/file" | |
243 | # will create ${initdir}/lib64, ${initdir}/lib64/file, | |
244 | # and a symlink ${initdir}/lib -> lib64. | |
245 | inst_dir() { | |
246 | [[ -e ${initdir}/"$1" ]] && return 0 # already there | |
247 | ||
248 | local _dir="$1" _part="${1%/*}" _file | |
249 | while [[ "$_part" != "${_part%/*}" ]] && ! [[ -e "${initdir}/${_part}" ]]; do | |
250 | _dir="$_part $_dir" | |
251 | _part=${_part%/*} | |
252 | done | |
253 | ||
254 | # iterate over parent directories | |
255 | for _file in $_dir; do | |
256 | [[ -e "${initdir}/$_file" ]] && continue | |
257 | if [[ -L $_file ]]; then | |
258 | inst_symlink "$_file" | |
259 | else | |
260 | # create directory | |
261 | mkdir -m 0755 -p "${initdir}/$_file" || return 1 | |
262 | [[ -e "$_file" ]] && chmod --reference="$_file" "${initdir}/$_file" | |
263 | chmod u+w "${initdir}/$_file" | |
264 | fi | |
265 | done | |
266 | } | |
267 | ||
268 | # $1 = file to copy to ramdisk | |
269 | # $2 (optional) Name for the file on the ramdisk | |
270 | # Location of the image dir is assumed to be $initdir | |
271 | # We never overwrite the target if it exists. | |
272 | inst_simple() { | |
273 | [[ -f "$1" ]] || return 1 | |
274 | strstr "$1" "/" || return 1 | |
275 | ||
276 | local _src=$1 target="${2:-$1}" | |
277 | if ! [[ -d ${initdir}/$target ]]; then | |
278 | [[ -e ${initdir}/$target ]] && return 0 | |
279 | [[ -L ${initdir}/$target ]] && return 0 | |
280 | [[ -d "${initdir}/${target%/*}" ]] || inst_dir "${target%/*}" | |
281 | fi | |
282 | # install checksum files also | |
283 | if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then | |
284 | inst "${_src%/*}/.${_src##*/}.hmac" "${target%/*}/.${target##*/}.hmac" | |
285 | fi | |
286 | ddebug "Installing $_src" | |
287 | cp --sparse=always -pfL "$_src" "${initdir}/$target" | |
288 | } | |
289 | ||
290 | # find symlinks linked to given library file | |
291 | # $1 = library file | |
292 | # Function searches for symlinks by stripping version numbers appended to | |
293 | # library filename, checks if it points to the same target and finally | |
294 | # prints the list of symlinks to stdout. | |
295 | # | |
296 | # Example: | |
297 | # rev_lib_symlinks libfoo.so.8.1 | |
298 | # output: libfoo.so.8 libfoo.so | |
299 | # (Only if libfoo.so.8 and libfoo.so exists on host system.) | |
300 | rev_lib_symlinks() { | |
301 | [[ ! $1 ]] && return 0 | |
302 | ||
303 | local fn="$1" orig="$(readlink -f "$1")" links='' | |
304 | ||
305 | [[ ${fn} =~ .*\.so\..* ]] || return 1 | |
306 | ||
307 | until [[ ${fn##*.} == so ]]; do | |
308 | fn="${fn%.*}" | |
309 | [[ -L ${fn} && $(readlink -f "${fn}") == ${orig} ]] && links+=" ${fn}" | |
310 | done | |
311 | ||
312 | echo "${links}" | |
313 | } | |
314 | ||
315 | # Same as above, but specialized to handle dynamic libraries. | |
316 | # It handles making symlinks according to how the original library | |
317 | # is referenced. | |
318 | inst_library() { | |
319 | local _src="$1" _dest=${2:-$1} _lib _reallib _symlink | |
320 | strstr "$1" "/" || return 1 | |
321 | [[ -e $initdir/$_dest ]] && return 0 | |
322 | if [[ -L $_src ]]; then | |
323 | # install checksum files also | |
324 | if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then | |
325 | inst "${_src%/*}/.${_src##*/}.hmac" "${_dest%/*}/.${_dest##*/}.hmac" | |
326 | fi | |
327 | _reallib=$(readlink -f "$_src") | |
328 | inst_simple "$_reallib" "$_reallib" | |
329 | inst_dir "${_dest%/*}" | |
330 | [[ -d "${_dest%/*}" ]] && _dest=$(readlink -f "${_dest%/*}")/${_dest##*/} | |
331 | ln -sfn $(convert_abs_rel "${_dest}" "${_reallib}") "${initdir}/${_dest}" | |
332 | else | |
333 | inst_simple "$_src" "$_dest" | |
334 | fi | |
335 | ||
336 | # Create additional symlinks. See rev_symlinks description. | |
337 | for _symlink in $(rev_lib_symlinks $_src) $(rev_lib_symlinks $_reallib); do | |
338 | [[ ! -e $initdir/$_symlink ]] && { | |
339 | ddebug "Creating extra symlink: $_symlink" | |
340 | inst_symlink $_symlink | |
341 | } | |
342 | done | |
343 | } | |
344 | ||
345 | # find a binary. If we were not passed the full path directly, | |
346 | # search in the usual places to find the binary. | |
347 | find_binary() { | |
348 | if [[ -z ${1##/*} ]]; then | |
349 | if [[ -x $1 ]] || { strstr "$1" ".so" && ldd $1 &>/dev/null; }; then | |
350 | echo $1 | |
351 | return 0 | |
352 | fi | |
353 | fi | |
354 | ||
355 | type -P $1 | |
356 | } | |
357 | ||
358 | # Same as above, but specialized to install binary executables. | |
359 | # Install binary executable, and all shared library dependencies, if any. | |
360 | inst_binary() { | |
361 | local _bin _target | |
362 | _bin=$(find_binary "$1") || return 1 | |
363 | _target=${2:-$_bin} | |
364 | [[ -e $initdir/$_target ]] && return 0 | |
365 | [[ -L $_bin ]] && inst_symlink $_bin $_target && return 0 | |
366 | local _file _line | |
367 | local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)' | |
368 | # I love bash! | |
369 | LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do | |
370 | [[ $_line = 'not a dynamic executable' ]] && break | |
371 | ||
372 | if [[ $_line =~ $_so_regex ]]; then | |
373 | _file=${BASH_REMATCH[1]} | |
374 | [[ -e ${initdir}/$_file ]] && continue | |
375 | inst_library "$_file" | |
376 | continue | |
377 | fi | |
378 | ||
379 | if [[ $_line =~ not\ found ]]; then | |
380 | dfatal "Missing a shared library required by $_bin." | |
381 | dfatal "Run \"ldd $_bin\" to find out what it is." | |
382 | dfatal "$_line" | |
383 | dfatal "dracut cannot create an initrd." | |
384 | exit 1 | |
385 | fi | |
386 | done | |
387 | inst_simple "$_bin" "$_target" | |
388 | } | |
389 | ||
390 | # same as above, except for shell scripts. | |
391 | # If your shell script does not start with shebang, it is not a shell script. | |
392 | inst_script() { | |
393 | local _bin | |
394 | _bin=$(find_binary "$1") || return 1 | |
395 | shift | |
396 | local _line _shebang_regex | |
397 | read -r -n 80 _line <"$_bin" | |
398 | # If debug is set, clean unprintable chars to prevent messing up the term | |
399 | [[ $debug ]] && _line=$(echo -n "$_line" | tr -c -d '[:print:][:space:]') | |
400 | _shebang_regex='(#! *)(/[^ ]+).*' | |
401 | [[ $_line =~ $_shebang_regex ]] || return 1 | |
402 | inst "${BASH_REMATCH[2]}" && inst_simple "$_bin" "$@" | |
403 | } | |
404 | ||
405 | # same as above, but specialized for symlinks | |
406 | inst_symlink() { | |
407 | local _src=$1 _target=${2:-$1} _realsrc | |
408 | strstr "$1" "/" || return 1 | |
409 | [[ -L $1 ]] || return 1 | |
410 | [[ -L $initdir/$_target ]] && return 0 | |
411 | _realsrc=$(readlink -f "$_src") | |
412 | if ! [[ -e $initdir/$_realsrc ]]; then | |
413 | if [[ -d $_realsrc ]]; then | |
414 | inst_dir "$_realsrc" | |
415 | else | |
416 | inst "$_realsrc" | |
417 | fi | |
418 | fi | |
419 | [[ ! -e $initdir/${_target%/*} ]] && inst_dir "${_target%/*}" | |
420 | [[ -d ${_target%/*} ]] && _target=$(readlink -f ${_target%/*})/${_target##*/} | |
421 | ln -sfn $(convert_abs_rel "${_target}" "${_realsrc}") "$initdir/$_target" | |
422 | } | |
423 | ||
424 | # attempt to install any programs specified in a udev rule | |
425 | inst_rule_programs() { | |
426 | local _prog _bin | |
427 | ||
428 | if grep -qE 'PROGRAM==?"[^ "]+' "$1"; then | |
429 | for _prog in $(grep -E 'PROGRAM==?"[^ "]+' "$1" | sed -r 's/.*PROGRAM==?"([^ "]+).*/\1/'); do | |
430 | if [ -x /lib/udev/$_prog ]; then | |
431 | _bin=/lib/udev/$_prog | |
432 | else | |
433 | _bin=$(find_binary "$_prog") || { | |
434 | dinfo "Skipping program $_prog using in udev rule $(basename $1) as it cannot be found" | |
435 | continue; | |
436 | } | |
437 | fi | |
438 | ||
439 | #dinfo "Installing $_bin due to it's use in the udev rule $(basename $1)" | |
440 | dracut_install "$_bin" | |
441 | done | |
442 | fi | |
443 | } | |
444 | ||
445 | # udev rules always get installed in the same place, so | |
446 | # create a function to install them to make life simpler. | |
447 | inst_rules() { | |
448 | local _target=/etc/udev/rules.d _rule _found | |
449 | ||
450 | inst_dir "/lib/udev/rules.d" | |
451 | inst_dir "$_target" | |
452 | for _rule in "$@"; do | |
453 | if [ "${rule#/}" = "$rule" ]; then | |
454 | for r in /lib/udev/rules.d /etc/udev/rules.d; do | |
455 | if [[ -f $r/$_rule ]]; then | |
456 | _found="$r/$_rule" | |
457 | inst_simple "$_found" | |
458 | inst_rule_programs "$_found" | |
459 | fi | |
460 | done | |
461 | fi | |
462 | for r in '' ./ $dracutbasedir/rules.d/; do | |
463 | if [[ -f ${r}$_rule ]]; then | |
464 | _found="${r}$_rule" | |
465 | inst_simple "$_found" "$_target/${_found##*/}" | |
466 | inst_rule_programs "$_found" | |
467 | fi | |
468 | done | |
469 | [[ $_found ]] || dinfo "Skipping udev rule: $_rule" | |
470 | done | |
471 | } | |
472 | ||
473 | # general purpose installation function | |
474 | # Same args as above. | |
475 | inst() { | |
476 | local _x | |
477 | ||
478 | case $# in | |
479 | 1) ;; | |
480 | 2) [[ ! $initdir && -d $2 ]] && export initdir=$2 | |
481 | [[ $initdir = $2 ]] && set $1;; | |
482 | 3) [[ -z $initdir ]] && export initdir=$2 | |
483 | set $1 $3;; | |
484 | *) dfatal "inst only takes 1 or 2 or 3 arguments" | |
485 | exit 1;; | |
486 | esac | |
487 | for _x in inst_symlink inst_script inst_binary inst_simple; do | |
488 | $_x "$@" && return 0 | |
489 | done | |
490 | return 1 | |
491 | } | |
492 | ||
493 | # install any of listed files | |
494 | # | |
495 | # If first argument is '-d' and second some destination path, first accessible | |
496 | # source is installed into this path, otherwise it will installed in the same | |
497 | # path as source. If none of listed files was installed, function return 1. | |
498 | # On first successful installation it returns with 0 status. | |
499 | # | |
500 | # Example: | |
501 | # | |
502 | # inst_any -d /bin/foo /bin/bar /bin/baz | |
503 | # | |
504 | # Lets assume that /bin/baz exists, so it will be installed as /bin/foo in | |
505 | # initramfs. | |
506 | inst_any() { | |
507 | local to f | |
508 | ||
509 | [[ $1 = '-d' ]] && to="$2" && shift 2 | |
510 | ||
511 | for f in "$@"; do | |
512 | if [[ -e $f ]]; then | |
513 | [[ $to ]] && inst "$f" "$to" && return 0 | |
514 | inst "$f" && return 0 | |
515 | fi | |
516 | done | |
517 | ||
518 | return 1 | |
519 | } | |
520 | ||
521 | # dracut_install [-o ] <file> [<file> ... ] | |
522 | # Install <file> to the initramfs image | |
523 | # -o optionally install the <file> and don't fail, if it is not there | |
524 | dracut_install() { | |
525 | local _optional=no | |
526 | if [[ $1 = '-o' ]]; then | |
527 | _optional=yes | |
528 | shift | |
529 | fi | |
530 | while (($# > 0)); do | |
531 | if ! inst "$1" ; then | |
532 | if [[ $_optional = yes ]]; then | |
533 | dinfo "Skipping program $1 as it cannot be found and is" \ | |
534 | "flagged to be optional" | |
535 | else | |
536 | dfatal "Failed to install $1" | |
537 | exit 1 | |
538 | fi | |
539 | fi | |
540 | shift | |
541 | done | |
542 | } | |
543 | ||
0d6e798a HH |
544 | # Install a single kernel module along with any firmware it may require. |
545 | # $1 = full path to kernel module to install | |
546 | install_kmod_with_fw() { | |
547 | # no need to go further if the module is already installed | |
548 | ||
549 | [[ -e "${initdir}/lib/modules/$KERNEL_VER/${1##*/lib/modules/$KERNEL_VER/}" ]] \ | |
550 | && return 0 | |
551 | ||
552 | [[ -e "$initdir/.kernelmodseen/${1##*/}" ]] && return 0 | |
553 | ||
554 | if [[ $omit_drivers ]]; then | |
555 | local _kmod=${1##*/} | |
556 | _kmod=${_kmod%.ko} | |
557 | _kmod=${_kmod/-/_} | |
558 | if [[ "$_kmod" =~ $omit_drivers ]]; then | |
559 | dinfo "Omitting driver $_kmod" | |
560 | return 1 | |
561 | fi | |
562 | if [[ "${1##*/lib/modules/$KERNEL_VER/}" =~ $omit_drivers ]]; then | |
563 | dinfo "Omitting driver $_kmod" | |
564 | return 1 | |
565 | fi | |
566 | fi | |
567 | ||
568 | [ -d "$initdir/.kernelmodseen" ] && \ | |
569 | > "$initdir/.kernelmodseen/${1##*/}" | |
570 | ||
571 | inst_simple "$1" "/lib/modules/$KERNEL_VER/${1##*/lib/modules/$KERNEL_VER/}" \ | |
572 | || return $? | |
573 | ||
574 | local _modname=${1##*/} _fwdir _found _fw | |
575 | _modname=${_modname%.ko*} | |
576 | for _fw in $(modinfo -k $KERNEL_VER -F firmware $1 2>/dev/null); do | |
577 | _found='' | |
578 | for _fwdir in $fw_dir; do | |
579 | if [[ -d $_fwdir && -f $_fwdir/$_fw ]]; then | |
580 | inst_simple "$_fwdir/$_fw" "/lib/firmware/$_fw" | |
581 | _found=yes | |
582 | fi | |
583 | done | |
584 | if [[ $_found != yes ]]; then | |
585 | if ! grep -qe "\<${_modname//-/_}\>" /proc/modules; then | |
586 | dinfo "Possible missing firmware \"${_fw}\" for kernel module" \ | |
587 | "\"${_modname}.ko\"" | |
588 | else | |
589 | dwarn "Possible missing firmware \"${_fw}\" for kernel module" \ | |
590 | "\"${_modname}.ko\"" | |
591 | fi | |
592 | fi | |
593 | done | |
594 | return 0 | |
595 | } | |
596 | ||
597 | # Do something with all the dependencies of a kernel module. | |
598 | # Note that kernel modules depend on themselves using the technique we use | |
599 | # $1 = function to call for each dependency we find | |
600 | # It will be passed the full path to the found kernel module | |
601 | # $2 = module to get dependencies for | |
602 | # rest of args = arguments to modprobe | |
603 | # _fderr specifies FD passed from surrounding scope | |
604 | for_each_kmod_dep() { | |
605 | local _func=$1 _kmod=$2 _cmd _modpath _options _found=0 | |
606 | shift 2 | |
607 | modprobe "$@" --ignore-install --show-depends $_kmod 2>&${_fderr} | ( | |
608 | while read _cmd _modpath _options; do | |
609 | [[ $_cmd = insmod ]] || continue | |
610 | $_func ${_modpath} || exit $? | |
611 | _found=1 | |
612 | done | |
613 | [[ $_found -eq 0 ]] && exit 1 | |
614 | exit 0 | |
615 | ) | |
616 | } | |
617 | ||
618 | # filter kernel modules to install certain modules that meet specific | |
619 | # requirements. | |
620 | # $1 = search only in subdirectory of /kernel/$1 | |
621 | # $2 = function to call with module name to filter. | |
622 | # This function will be passed the full path to the module to test. | |
623 | # The behaviour of this function can vary depending on whether $hostonly is set. | |
624 | # If it is, we will only look at modules that are already in memory. | |
625 | # If it is not, we will look at all kernel modules | |
626 | # This function returns the full filenames of modules that match $1 | |
627 | filter_kernel_modules_by_path () ( | |
628 | local _modname _filtercmd | |
629 | if ! [[ $hostonly ]]; then | |
630 | _filtercmd='find "$KERNEL_MODS/kernel/$1" "$KERNEL_MODS/extra"' | |
631 | _filtercmd+=' "$KERNEL_MODS/weak-updates" -name "*.ko" -o -name "*.ko.gz"' | |
632 | _filtercmd+=' -o -name "*.ko.xz"' | |
633 | _filtercmd+=' 2>/dev/null' | |
634 | else | |
635 | _filtercmd='cut -d " " -f 1 </proc/modules|xargs modinfo -F filename ' | |
636 | _filtercmd+='-k $KERNEL_VER 2>/dev/null' | |
637 | fi | |
638 | for _modname in $(eval $_filtercmd); do | |
639 | case $_modname in | |
640 | *.ko) "$2" "$_modname" && echo "$_modname";; | |
641 | *.ko.gz) gzip -dc "$_modname" > $initdir/$$.ko | |
642 | $2 $initdir/$$.ko && echo "$_modname" | |
643 | rm -f $initdir/$$.ko | |
644 | ;; | |
645 | *.ko.xz) xz -dc "$_modname" > $initdir/$$.ko | |
646 | $2 $initdir/$$.ko && echo "$_modname" | |
647 | rm -f $initdir/$$.ko | |
648 | ;; | |
649 | esac | |
650 | done | |
651 | ) | |
652 | find_kernel_modules_by_path () ( | |
653 | if ! [[ $hostonly ]]; then | |
654 | find "$KERNEL_MODS/kernel/$1" "$KERNEL_MODS/extra" "$KERNEL_MODS/weak-updates" \ | |
655 | -name "*.ko" -o -name "*.ko.gz" -o -name "*.ko.xz" 2>/dev/null | |
656 | else | |
657 | cut -d " " -f 1 </proc/modules \ | |
658 | | xargs modinfo -F filename -k $KERNEL_VER 2>/dev/null | |
659 | fi | |
660 | ) | |
661 | ||
662 | filter_kernel_modules () { | |
663 | filter_kernel_modules_by_path drivers "$1" | |
664 | } | |
665 | ||
666 | find_kernel_modules () { | |
667 | find_kernel_modules_by_path drivers | |
668 | } | |
669 | ||
670 | # instmods [-c] <kernel module> [<kernel module> ... ] | |
671 | # instmods [-c] <kernel subsystem> | |
672 | # install kernel modules along with all their dependencies. | |
673 | # <kernel subsystem> can be e.g. "=block" or "=drivers/usb/storage" | |
674 | instmods() { | |
675 | [[ $no_kernel = yes ]] && return | |
676 | # called [sub]functions inherit _fderr | |
677 | local _fderr=9 | |
678 | local _check=no | |
679 | if [[ $1 = '-c' ]]; then | |
680 | _check=yes | |
681 | shift | |
682 | fi | |
683 | ||
684 | function inst1mod() { | |
685 | local _ret=0 _mod="$1" | |
686 | case $_mod in | |
687 | =*) | |
688 | if [ -f $KERNEL_MODS/modules.${_mod#=} ]; then | |
689 | ( [[ "$_mpargs" ]] && echo $_mpargs | |
690 | cat "${KERNEL_MODS}/modules.${_mod#=}" ) \ | |
691 | | instmods | |
692 | else | |
693 | ( [[ "$_mpargs" ]] && echo $_mpargs | |
694 | find "$KERNEL_MODS" -path "*/${_mod#=}/*" -printf '%f\n' ) \ | |
695 | | instmods | |
696 | fi | |
697 | ;; | |
698 | --*) _mpargs+=" $_mod" ;; | |
699 | i2o_scsi) return ;; # Do not load this diagnostic-only module | |
700 | *) | |
701 | _mod=${_mod##*/} | |
702 | # if we are already installed, skip this module and go on | |
703 | # to the next one. | |
704 | [[ -f "$initdir/.kernelmodseen/${_mod%.ko}.ko" ]] && return | |
705 | ||
706 | if [[ $omit_drivers ]] && [[ "$1" =~ $omit_drivers ]]; then | |
707 | dinfo "Omitting driver ${_mod##$KERNEL_MODS}" | |
708 | return | |
709 | fi | |
710 | # If we are building a host-specific initramfs and this | |
711 | # module is not already loaded, move on to the next one. | |
712 | [[ $hostonly ]] && ! grep -qe "\<${_mod//-/_}\>" /proc/modules \ | |
713 | && ! echo $add_drivers | grep -qe "\<${_mod}\>" \ | |
714 | && return | |
715 | ||
716 | # We use '-d' option in modprobe only if modules prefix path | |
717 | # differs from default '/'. This allows us to use Dracut with | |
718 | # old version of modprobe which doesn't have '-d' option. | |
719 | local _moddirname=${KERNEL_MODS%%/lib/modules/*} | |
720 | [[ -n ${_moddirname} ]] && _moddirname="-d ${_moddirname}/" | |
721 | ||
722 | # ok, load the module, all its dependencies, and any firmware | |
723 | # it may require | |
724 | for_each_kmod_dep install_kmod_with_fw $_mod \ | |
725 | --set-version $KERNEL_VER ${_moddirname} $_mpargs | |
726 | ((_ret+=$?)) | |
727 | ;; | |
728 | esac | |
729 | return $_ret | |
730 | } | |
731 | ||
732 | function instmods_1() { | |
733 | local _mod _mpargs | |
734 | if (($# == 0)); then # filenames from stdin | |
735 | while read _mod; do | |
736 | inst1mod "${_mod%.ko*}" || { | |
737 | if [ "$_check" = "yes" ]; then | |
738 | dfatal "Failed to install $_mod" | |
739 | return 1 | |
740 | fi | |
741 | } | |
742 | done | |
743 | fi | |
744 | while (($# > 0)); do # filenames as arguments | |
745 | inst1mod ${1%.ko*} || { | |
746 | if [ "$_check" = "yes" ]; then | |
747 | dfatal "Failed to install $1" | |
748 | return 1 | |
749 | fi | |
750 | } | |
751 | shift | |
752 | done | |
753 | return 0 | |
754 | } | |
755 | ||
756 | local _ret _filter_not_found='FATAL: Module .* not found.' | |
757 | set -o pipefail | |
758 | # Capture all stderr from modprobe to _fderr. We could use {var}>... | |
759 | # redirections, but that would make dracut require bash4 at least. | |
760 | eval "( instmods_1 \"\$@\" ) ${_fderr}>&1" \ | |
761 | | while read line; do [[ "$line" =~ $_filter_not_found ]] && echo $line || echo $line >&2 ;done | derror | |
762 | _ret=$? | |
763 | set +o pipefail | |
764 | return $_ret | |
765 | } | |
898720b7 HH |
766 | |
767 | # inst_libdir_file [-n <pattern>] <file> [<file>...] | |
768 | # Install a <file> located on a lib directory to the initramfs image | |
769 | # -n <pattern> install non-matching files | |
770 | inst_libdir_file() { | |
771 | if [[ "$1" == "-n" ]]; then | |
772 | local _pattern=$1 | |
773 | shift 2 | |
774 | for _dir in $libdirs; do | |
775 | for _i in "$@"; do | |
776 | for _f in "$_dir"/$_i; do | |
777 | [[ "$_i" =~ $_pattern ]] || continue | |
778 | [[ -e "$_i" ]] && dracut_install "$_i" | |
779 | done | |
780 | done | |
781 | done | |
782 | else | |
783 | for _dir in $libdirs; do | |
784 | for _i in "$@"; do | |
785 | for _f in "$_dir"/$_i; do | |
786 | [[ -e "$_f" ]] && dracut_install "$_f" | |
787 | done | |
788 | done | |
789 | done | |
790 | fi | |
791 | } | |
792 | ||
1ecf6a2b HH |
793 | check_qemu() { |
794 | command -v qemu-kvm &>/dev/null && [[ -c /dev/kvm ]] | |
795 | } | |
796 | ||
797 | check_nspawn() { | |
798 | [[ -d /sys/fs/cgroup/systemd ]] | |
799 | } | |
800 | ||
801 | ||
898720b7 | 802 | do_test() { |
33a5e20f HH |
803 | if [[ $UID != "0" ]]; then |
804 | echo "TEST: $TEST_DESCRIPTION [SKIPPED]: not root" >&2 | |
805 | exit 0 | |
806 | fi | |
807 | ||
898720b7 HH |
808 | # Detect lib paths |
809 | [[ $libdir ]] || for libdir in /lib64 /lib; do | |
810 | [[ -d $libdir ]] && libdirs+=" $libdir" && break | |
811 | done | |
812 | ||
813 | [[ $usrlibdir ]] || for usrlibdir in /usr/lib64 /usr/lib; do | |
814 | [[ -d $usrlibdir ]] && libdirs+=" $usrlibdir" && break | |
815 | done | |
816 | ||
817 | import_testdir | |
818 | ||
819 | while (($# > 0)); do | |
820 | case $1 in | |
821 | --run) | |
822 | echo "TEST RUN: $TEST_DESCRIPTION" | |
823 | test_run | |
824 | ret=$? | |
825 | if [ $ret -eq 0 ]; then | |
826 | echo "TEST RUN: $TEST_DESCRIPTION [OK]" | |
827 | else | |
828 | echo "TEST RUN: $TEST_DESCRIPTION [FAILED]" | |
829 | fi | |
830 | exit $ret;; | |
831 | --setup) | |
832 | echo "TEST SETUP: $TEST_DESCRIPTION" | |
833 | test_setup | |
834 | exit $?;; | |
835 | --clean) | |
836 | echo "TEST CLEANUP: $TEST_DESCRIPTION" | |
837 | test_cleanup | |
838 | rm -fr "$TESTDIR" | |
839 | rm -f .testdir | |
840 | exit $?;; | |
841 | --all) | |
842 | echo -n "TEST: $TEST_DESCRIPTION "; | |
843 | ( | |
844 | test_setup && test_run | |
845 | ret=$? | |
846 | test_cleanup | |
847 | rm -fr "$TESTDIR" | |
848 | rm -f .testdir | |
849 | exit $ret | |
850 | ) </dev/null >test.log 2>&1 | |
851 | ret=$? | |
852 | if [ $ret -eq 0 ]; then | |
853 | rm test.log | |
854 | echo "[OK]" | |
855 | else | |
856 | echo "[FAILED]" | |
857 | echo "see $(pwd)/test.log" | |
858 | fi | |
859 | exit $ret;; | |
860 | *) break ;; | |
861 | esac | |
862 | shift | |
863 | done | |
864 | } |