]> git.ipfire.org Git - thirdparty/dracut.git/blob - modules.d/99base/dracut-lib.sh
Specify strstr tightly, add strglob/strglobin.
[thirdparty/dracut.git] / modules.d / 99base / dracut-lib.sh
1 #!/bin/sh
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 export DRACUT_SYSTEMD
6 export NEWROOT
7 if [ -n "$NEWROOT" ]; then
8 [ -d $NEWROOT ] || mkdir -p -m 0755 $NEWROOT
9 fi
10
11 [ -d /run/initramfs ] || mkdir -p -m 0755 /run/initramfs
12 [ -d /run/lock ] || mkdir -p -m 0755 /run/lock
13 [ -d /run/log ] || mkdir -p -m 0755 /run/log
14
15 debug_off() {
16 set +x
17 }
18
19 debug_on() {
20 [ "$RD_DEBUG" = "yes" ] && set -x
21 }
22
23 # returns OK if $1 contains literal string $2 (and isn't empty)
24 strstr() {
25 [ "${1##*"$2"*}" != "$1" ]
26 }
27
28 # returns OK if $1 matches (completely) glob pattern $2
29 # An empty $1 will not be considered matched, even if $2 is * which technically
30 # matches; as it would match anything, it's not an interesting case.
31 strglob() {
32 [ -n "$1" -a -z "${1##$2}" ]
33 }
34
35 # returns OK if $1 contains (anywhere) a match of glob pattern $2
36 # An empty $1 will not be considered matched, even if $2 is * which technically
37 # matches; as it would match anything, it's not an interesting case.
38 strglobin() {
39 [ -n "$1" -a -z "${1##*$2*}" ]
40 }
41
42 # returns OK if $1 contains literal string $2 at the beginning, and isn't empty
43 str_starts() {
44 [ "${1#"$2"*}" != "$1" ]
45 }
46
47 # returns OK if $1 contains literal string $2 at the end, and isn't empty
48 str_ends() {
49 [ "${1%*"$2"}" != "$1" ]
50 }
51
52 if [ -z "$DRACUT_SYSTEMD" ]; then
53
54 warn() {
55 check_quiet
56 echo "<28>dracut Warning: $*" > /dev/kmsg
57 echo "dracut Warning: $*" >&2
58 }
59
60 info() {
61 check_quiet
62 echo "<30>dracut: $*" > /dev/kmsg
63 [ "$DRACUT_QUIET" != "yes" ] && \
64 echo "dracut: $*"
65 }
66
67 else
68
69 warn() {
70 echo "Warning: $*" >&2
71 }
72
73 info() {
74 echo "$*"
75 }
76
77 fi
78
79 vwarn() {
80 while read line; do
81 warn $line;
82 done
83 }
84
85 vinfo() {
86 while read line; do
87 info $line;
88 done
89 }
90
91 # replaces all occurrences of 'search' in 'str' with 'replacement'
92 #
93 # str_replace str search replacement
94 #
95 # example:
96 # str_replace ' one two three ' ' ' '_'
97 str_replace() {
98 local in="$1"; local s="$2"; local r="$3"
99 local out=''
100
101 while strstr "${in}" "$s"; do
102 chop="${in%%"$s"*}"
103 out="${out}${chop}$r"
104 in="${in#*"$s"}"
105 done
106 echo "${out}${in}"
107 }
108
109 killall_proc_mountpoint() {
110 local _pid
111 local _t
112 for _pid in /proc/*; do
113 _pid=${_pid##/proc/}
114 case $_pid in
115 *[!0-9]*) continue;;
116 esac
117 [ -e "/proc/$_pid/exe" ] || continue
118 [ -e "/proc/$_pid/root" ] || continue
119 strstr "$(ls -l -- "/proc/$_pid" "/proc/$_pid/fd" 2>/dev/null)" "$1" && kill -9 "$_pid"
120 done
121 }
122
123 getcmdline() {
124 local _line
125 local _i
126 local CMDLINE_ETC_D
127 local CMDLINE_ETC
128 unset _line
129
130 if [ -e /etc/cmdline ]; then
131 while read -r _line; do
132 CMDLINE_ETC="$CMDLINE_ETC $_line";
133 done </etc/cmdline;
134 fi
135 for _i in /etc/cmdline.d/*.conf; do
136 [ -e "$_i" ] || continue
137 while read -r _line; do
138 CMDLINE_ETC_D="$CMDLINE_ETC_D $_line";
139 done <"$_i";
140 done
141 if [ -e /proc/cmdline ]; then
142 read -r CMDLINE </proc/cmdline;
143 CMDLINE="$CMDLINE_ETC_D $CMDLINE_ETC $CMDLINE"
144 fi
145 printf "%s" "$CMDLINE"
146 }
147
148 _dogetarg() {
149 local _o _val _doecho
150 unset _val
151 unset _o
152 unset _doecho
153 CMDLINE=$(getcmdline)
154
155 for _o in $CMDLINE; do
156 if [ "${_o%%=*}" = "${1%%=*}" ]; then
157 if [ -n "${1#*=}" -a "${1#*=*}" != "${1}" ]; then
158 # if $1 has a "=<value>", we want the exact match
159 if [ "$_o" = "$1" ]; then
160 _val="1";
161 unset _doecho
162 fi
163 continue
164 fi
165
166 if [ "${_o#*=}" = "$_o" ]; then
167 # if cmdline argument has no "=<value>", we assume "=1"
168 _val="1";
169 unset _doecho
170 continue
171 fi
172
173 _val="${_o#*=}"
174 _doecho=1
175 fi
176 done
177 if [ -n "$_val" ]; then
178 [ "x$_doecho" != "x" ] && echo "$_val";
179 return 0;
180 fi
181 return 1;
182 }
183
184 getarg() {
185 debug_off
186 local _deprecated _newoption
187 while [ $# -gt 0 ]; do
188 case $1 in
189 -d) _deprecated=1; shift;;
190 -y) if _dogetarg $2 >/dev/null; then
191 if [ "$_deprecated" = "1" ]; then
192 [ -n "$_newoption" ] && warn "Kernel command line option '$2' is deprecated, use '$_newoption' instead." || warn "Option '$2' is deprecated."
193 fi
194 echo 1
195 debug_on
196 return 0
197 fi
198 _deprecated=0
199 shift 2;;
200 -n) if _dogetarg $2 >/dev/null; then
201 echo 0;
202 if [ "$_deprecated" = "1" ]; then
203 [ -n "$_newoption" ] && warn "Kernel command line option '$2' is deprecated, use '$_newoption=0' instead." || warn "Option '$2' is deprecated."
204 fi
205 debug_on
206 return 1
207 fi
208 _deprecated=0
209 shift 2;;
210 *) if [ -z "$_newoption" ]; then
211 _newoption="$1"
212 fi
213 if _dogetarg $1; then
214 if [ "$_deprecated" = "1" ]; then
215 [ -n "$_newoption" ] && warn "Kernel command line option '$1' is deprecated, use '$_newoption' instead." || warn "Option '$1' is deprecated."
216 fi
217 debug_on
218 return 0;
219 fi
220 _deprecated=0
221 shift;;
222 esac
223 done
224 debug_on
225 return 1
226 }
227
228 # getargbool <defaultval> <args...>
229 # False if "getarg <args...>" returns "0", "no", or "off".
230 # True if getarg returns any other non-empty string.
231 # If not found, assumes <defaultval> - usually 0 for false, 1 for true.
232 # example: getargbool 0 rd.info
233 # true: rd.info, rd.info=1, rd.info=xxx
234 # false: rd.info=0, rd.info=off, rd.info not present (default val is 0)
235 getargbool() {
236 local _b
237 unset _b
238 local _default
239 _default="$1"; shift
240 _b=$(getarg "$@")
241 [ $? -ne 0 -a -z "$_b" ] && _b="$_default"
242 if [ -n "$_b" ]; then
243 [ $_b = "0" ] && return 1
244 [ $_b = "no" ] && return 1
245 [ $_b = "off" ] && return 1
246 fi
247 return 0
248 }
249
250 isdigit() {
251 case "$1" in
252 *[!0-9]*|"") return 1;;
253 esac
254
255 return 0
256 }
257
258 # getargnum <defaultval> <minval> <maxval> <arg>
259 # Will echo the arg if it's in range [minval - maxval].
260 # If it's not set or it's not valid, will set it <defaultval>.
261 # Note all values are required to be >= 0 here.
262 # <defaultval> should be with [minval -maxval].
263 getargnum() {
264 local _b
265 unset _b
266 local _default _min _max
267 _default="$1"; shift
268 _min="$1"; shift
269 _max="$1"; shift
270 _b=$(getarg "$1")
271 [ $? -ne 0 -a -z "$_b" ] && _b=$_default
272 if [ -n "$_b" ]; then
273 isdigit "$_b" && _b=$(($_b)) && \
274 [ $_b -ge $_min ] && [ $_b -le $_max ] && echo $_b && return
275 fi
276 echo $_default
277 }
278
279 _dogetargs() {
280 debug_off
281 local _o _found _key
282 unset _o
283 unset _found
284 CMDLINE=$(getcmdline)
285 _key="$1"
286 set --
287 for _o in $CMDLINE; do
288 if [ "$_o" = "$_key" ]; then
289 _found=1;
290 elif [ "${_o%%=*}" = "${_key%=}" ]; then
291 [ -n "${_o%%=*}" ] && set -- "$@" "${_o#*=}";
292 _found=1;
293 fi
294 done
295 if [ -n "$_found" ]; then
296 [ $# -gt 0 ] && echo -n "$@"
297 return 0
298 fi
299 return 1;
300 }
301
302 getargs() {
303 debug_off
304 local _val _i _args _gfound _deprecated
305 unset _val
306 unset _gfound
307 _newoption="$1"
308 _args="$@"
309 set --
310 for _i in $_args; do
311 if [ "$i" = "-d" ]; then
312 _deprecated=1
313 continue
314 fi
315 _val="$(_dogetargs $_i)"
316 if [ $? -eq 0 ]; then
317 if [ "$_deprecated" = "1" ]; then
318 [ -n "$_newoption" ] && warn "Option '$_i' is deprecated, use '$_newoption' instead." || warn "Option $_i is deprecated!"
319 fi
320 _gfound=1
321 fi
322 [ -n "$_val" ] && set -- "$@" "$_val"
323 _deprecated=0
324 done
325 if [ -n "$_gfound" ]; then
326 if [ $# -gt 0 ]; then
327 echo -n "$@"
328 else
329 echo -n 1
330 fi
331 debug_on
332 return 0
333 fi
334 debug_on
335 return 1;
336 }
337
338
339 # Prints value of given option. If option is a flag and it's present,
340 # it just returns 0. Otherwise 1 is returned.
341 # $1 = options separated by commas
342 # $2 = option we are interested in
343 #
344 # Example:
345 # $1 = cipher=aes-cbc-essiv:sha256,hash=sha256,verify
346 # $2 = hash
347 # Output:
348 # sha256
349 getoptcomma() {
350 local line=",$1,"; local opt="$2"; local tmp
351
352 case "${line}" in
353 *,${opt}=*,*)
354 tmp="${line#*,${opt}=}"
355 echo "${tmp%%,*}"
356 return 0
357 ;;
358 *,${opt},*) return 0;;
359 esac
360 return 1
361 }
362
363 # Splits given string 'str' with separator 'sep' into variables 'var1', 'var2',
364 # 'varN'. If number of fields is less than number of variables, remaining are
365 # not set. If number of fields is greater than number of variables, the last
366 # variable takes remaining fields. In short - it acts similary to 'read'.
367 #
368 # splitsep sep str var1 var2 varN
369 #
370 # example:
371 # splitsep ':' 'foo:bar:baz' v1 v2
372 # in result:
373 # v1='foo', v2='bar:baz'
374 #
375 # TODO: ':' inside fields.
376 splitsep() {
377 debug_off
378 local sep="$1"; local str="$2"; shift 2
379 local tmp
380
381 while [ -n "$str" -a "$#" -gt 1 ]; do
382 tmp="${str%%$sep*}"
383 eval "$1='${tmp}'"
384 str="${str#"$tmp"}"
385 str="${str#$sep}"
386 shift
387 done
388 [ -n "$str" -a -n "$1" ] && eval "$1='$str'"
389 debug_on
390 return 0
391 }
392
393 setdebug() {
394 [ -f /etc/initrd-release ] || return
395 if [ -z "$RD_DEBUG" ]; then
396 if [ -e /proc/cmdline ]; then
397 RD_DEBUG=no
398 if getargbool 0 rd.debug -d -y rdinitdebug -d -y rdnetdebug; then
399 RD_DEBUG=yes
400 [ -n "$BASH" ] && \
401 export PS4='${BASH_SOURCE}@${LINENO}(${FUNCNAME[0]}): ';
402 fi
403 fi
404 export RD_DEBUG
405 fi
406 debug_on
407 }
408
409 setdebug
410
411 source_all() {
412 local f
413 local _dir
414 _dir=$1; shift
415 [ "$_dir" ] && [ -d "/$_dir" ] || return
416 for f in "/$_dir"/*.sh; do [ -e "$f" ] && . "$f" "$@"; done
417 }
418
419 hookdir=/lib/dracut/hooks
420 export hookdir
421
422 source_hook() {
423 local _dir
424 _dir=$1; shift
425 source_all "/lib/dracut/hooks/$_dir" "$@"
426 }
427
428 check_finished() {
429 local f
430 for f in $hookdir/initqueue/finished/*.sh; do
431 [ "$f" = "$hookdir/initqueue/finished/*.sh" ] && return 0
432 { [ -e "$f" ] && ( . "$f" ) ; } || return 1
433 done
434 return 0
435 }
436
437 source_conf() {
438 local f
439 [ "$1" ] && [ -d "/$1" ] || return
440 for f in "/$1"/*.conf; do [ -e "$f" ] && . "$f"; done
441 }
442
443 die() {
444 {
445 echo "<24>dracut: FATAL: $*";
446 echo "<24>dracut: Refusing to continue";
447 } > /dev/kmsg
448
449 {
450 echo "warn dracut: FATAL: \"$*\"";
451 echo "warn dracut: Refusing to continue";
452 } >> $hookdir/emergency/01-die.sh
453 [ -d /run/initramfs ] || mkdir -p -- /run/initramfs
454
455 > /run/initramfs/.die
456
457 getargbool 0 "rd.debug=" && emergency_shell
458
459 if [ -n "$DRACUT_SYSTEMD" ]; then
460 systemctl --no-block --force halt
461 fi
462
463 exit 1
464 }
465
466 check_quiet() {
467 if [ -z "$DRACUT_QUIET" ]; then
468 DRACUT_QUIET="yes"
469 getargbool 0 rd.info -d -y rdinfo && DRACUT_QUIET="no"
470 getargbool 0 rd.debug -d -y rdinitdebug && DRACUT_QUIET="no"
471 getarg quiet || DRACUT_QUIET="yes"
472 a=$(getarg loglevel=)
473 [ -n "$a" ] && [ $a -ge 28 ] && DRACUT_QUIET="yes"
474 export DRACUT_QUIET
475 fi
476 }
477
478
479 check_occurances() {
480 # Count the number of times the character $ch occurs in $str
481 # Return 0 if the count matches the expected number, 1 otherwise
482 local str="$1"
483 local ch="$2"
484 local expected="$3"
485 local count=0
486
487 while [ "${str#*$ch}" != "${str}" ]; do
488 str="${str#*$ch}"
489 count=$(( $count + 1 ))
490 done
491
492 [ $count -eq $expected ]
493 }
494
495 incol2() {
496 debug_off
497 local dummy check;
498 local file="$1";
499 local str="$2";
500
501 [ -z "$file" ] && return 1;
502 [ -z "$str" ] && return 1;
503
504 while read dummy check restofline; do
505 if [ "$check" = "$str" ]; then
506 debug_on
507 return 0
508 fi
509 done < $file
510 debug_on
511 return 1
512 }
513
514 udevsettle() {
515 [ -z "$UDEVVERSION" ] && export UDEVVERSION=$(udevadm --version)
516
517 if [ $UDEVVERSION -ge 143 ]; then
518 udevadm settle --exit-if-exists=$hookdir/initqueue/work $settle_exit_if_exists
519 else
520 udevadm settle --timeout=30
521 fi
522 }
523
524 udevproperty() {
525 [ -z "$UDEVVERSION" ] && export UDEVVERSION=$(udevadm --version)
526
527 if [ $UDEVVERSION -ge 143 ]; then
528 for i in "$@"; do udevadm control --property=$i; done
529 else
530 for i in "$@"; do udevadm control --env=$i; done
531 fi
532 }
533
534 find_mount() {
535 local dev mnt etc wanted_dev
536 wanted_dev="$(readlink -e -q $1)"
537 while read dev mnt etc; do
538 [ "$dev" = "$wanted_dev" ] && echo "$dev" && return 0
539 done < /proc/mounts
540 return 1
541 }
542
543 # usage: ismounted <mountpoint>
544 # usage: ismounted /dev/<device>
545 if command -v findmnt >/dev/null; then
546 ismounted() {
547 findmnt "$1" > /dev/null 2>&1
548 }
549 else
550 ismounted() {
551 if [ -b "$1" ]; then
552 find_mount "$1" > /dev/null && return 0
553 return 1
554 fi
555
556 while read a m a; do
557 [ "$m" = "$1" ] && return 0
558 done < /proc/mounts
559 return 1
560 }
561 fi
562
563 # root=nfs:[<server-ip>:]<root-dir>[:<nfs-options>]
564 # root=nfs4:[<server-ip>:]<root-dir>[:<nfs-options>]
565 nfsroot_to_var() {
566 # strip nfs[4]:
567 local arg="$@:"
568 nfs="${arg%%:*}"
569 arg="${arg##$nfs:}"
570
571 # check if we have a server
572 if strstr "$arg" ':/' ; then
573 server="${arg%%:/*}"
574 arg="/${arg##*:/}"
575 fi
576
577 path="${arg%%:*}"
578
579 # rest are options
580 options="${arg##$path}"
581 # strip leading ":"
582 options="${options##:}"
583 # strip ":"
584 options="${options%%:}"
585
586 # Does it really start with '/'?
587 [ -n "${path%%/*}" ] && path="error";
588
589 #Fix kernel legacy style separating path and options with ','
590 if [ "$path" != "${path#*,}" ] ; then
591 options=${path#*,}
592 path=${path%%,*}
593 fi
594 }
595
596 # Create udev rule match for a device with its device name, or the udev property
597 # ID_FS_UUID or ID_FS_LABEL
598 #
599 # example:
600 # udevmatch LABEL=boot
601 # prints:
602 # ENV{ID_FS_LABEL}="boot"
603 #
604 # TOOD: symlinks
605 udevmatch() {
606 case "$1" in
607 UUID=????????-????-????-????-????????????|LABEL=*|PARTLABEL=*|PARTUUID=????????-????-????-????-????????????)
608 printf 'ENV{ID_FS_%s}=="%s"' "${1%%=*}" "${1#*=}"
609 ;;
610 UUID=*)
611 printf 'ENV{ID_FS_UUID}=="%s*"' "${1#*=}"
612 ;;
613 PARTUUID=*)
614 printf 'ENV{ID_FS_PARTUUID}=="%s*"' "${1#*=}"
615 ;;
616 /dev/?*) printf -- 'KERNEL=="%s"' "${1#/dev/}" ;;
617 *) return 255 ;;
618 esac
619 }
620
621 # Prints unique path for potential file inside specified directory. It consists
622 # of specified directory, prefix and number at the end which is incremented
623 # until non-existing file is found.
624 #
625 # funiq dir prefix
626 #
627 # example:
628 # # ls /mnt
629 # cdrom0 cdrom1
630 #
631 # # funiq /mnt cdrom
632 # /mnt/cdrom2
633 funiq() {
634 local dir="$1"; local prefix="$2"
635 local i=0
636
637 [ -d "${dir}" ] || return 1
638
639 while [ -e "${dir}/${prefix}$i" ]; do
640 i=$(($i+1)) || return 1
641 done
642
643 echo "${dir}/${prefix}$i"
644 }
645
646 # Creates unique directory and prints its path. It's using funiq to generate
647 # path.
648 #
649 # mkuniqdir subdir new_dir_name
650 mkuniqdir() {
651 local dir="$1"; local prefix="$2"
652 local retdir; local retdir_new
653
654 [ -d "${dir}" ] || mkdir -m 0755 -p "${dir}" || return 1
655
656 retdir=$(funiq "${dir}" "${prefix}") || return 1
657 until mkdir -m 0755 "${retdir}" 2>/dev/null; do
658 retdir_new=$(funiq "${dir}" "${prefix}") || return 1
659 [ "$retdir_new" = "$retdir" ] && return 1
660 retdir="$retdir_new"
661 done
662
663 echo "${retdir}"
664 }
665
666 # Copy the contents of SRC into DEST, merging the contents of existing
667 # directories (kinda like rsync, or cpio -p).
668 # Creates DEST if it doesn't exist. Overwrites files with the same names.
669 #
670 # copytree SRC DEST
671 copytree() {
672 local src="$1" dest="$2"
673 mkdir -p "$dest"; dest=$(readlink -e -q "$dest")
674 ( cd "$src"; cp -af . -t "$dest" )
675 }
676
677 # Evaluates command for UUIDs either given as arguments for this function or all
678 # listed in /dev/disk/by-uuid. UUIDs doesn't have to be fully specified. If
679 # beginning is given it is expanded to all matching UUIDs. To pass full UUID to
680 # your command use '$___' as a place holder. Remember to escape '$'!
681 #
682 # foreach_uuid_until [ -p prefix ] command UUIDs
683 #
684 # prefix - string to put just before $___
685 # command - command to be evaluated
686 # UUIDs - list of UUIDs separated by space
687 #
688 # The function returns after *first successful evaluation* of the given command
689 # with status 0. If evaluation fails for every UUID function returns with
690 # status 1.
691 #
692 # Example:
693 # foreach_uuid_until "mount -U \$___ /mnt; echo OK; umount /mnt" \
694 # "01234 f512 a235567f-12a3-c123-a1b1-01234567abcb"
695 foreach_uuid_until() (
696 cd /dev/disk/by-uuid
697
698 [ "$1" = -p ] && local prefix="$2" && shift 2
699 local cmd="$1"; shift; local uuids_list="$*"
700 local uuid; local full_uuid; local ___
701
702 [ -n "${cmd}" ] || return 1
703
704 for uuid in ${uuids_list:-*}; do
705 for full_uuid in ${uuid}*; do
706 [ -e "${full_uuid}" ] || continue
707 ___="${prefix}${full_uuid}"
708 eval ${cmd} && return 0
709 done
710 done
711
712 return 1
713 )
714
715 # Get kernel name for given device. Device may be the name too (then the same
716 # is returned), a symlink (full path), UUID (prefixed with "UUID=") or label
717 # (prefixed with "LABEL="). If just a beginning of the UUID is specified or
718 # even an empty, function prints all device names which UUIDs match - every in
719 # single line.
720 #
721 # NOTICE: The name starts with "/dev/".
722 #
723 # Example:
724 # devnames UUID=123
725 # May print:
726 # /dev/dm-1
727 # /dev/sdb1
728 # /dev/sdf3
729 devnames() {
730 local dev="$1"; local d; local names
731
732 case "$dev" in
733 UUID=*)
734 dev="$(foreach_uuid_until '! blkid -U $___' "${dev#UUID=}")" \
735 && return 255
736 [ -z "$dev" ] && return 255
737 ;;
738 LABEL=*) dev="$(blkid -L "${dev#LABEL=}")" || return 255 ;;
739 /dev/?*) ;;
740 *) return 255 ;;
741 esac
742
743 for d in $dev; do
744 names="$names
745 $(readlink -e -q "$d")" || return 255
746 done
747
748 echo "${names#
749 }"
750 }
751
752
753 usable_root() {
754 local _d
755 [ -d $1 ] || return 1
756 for _d in proc sys dev; do
757 [ -e "$1"/$_d ] || return 1
758 done
759 return 0
760 }
761
762 inst_hook() {
763 local _hookname _unique _name _job _exe
764 while [ $# -gt 0 ]; do
765 case "$1" in
766 --hook)
767 _hookname="/$2";shift;;
768 --unique)
769 _unique="yes";;
770 --name)
771 _name="$2";shift;;
772 *)
773 break;;
774 esac
775 shift
776 done
777
778 if [ -z "$_unique" ]; then
779 _job="${_name}$$"
780 else
781 _job="${_name:-$1}"
782 _job=${_job##*/}
783 fi
784
785 _exe=$1
786 shift
787
788 [ -x "$_exe" ] || _exe=$(command -v $_exe)
789
790 if [ -n "$onetime" ]; then
791 {
792 echo '[ -e "$_job" ] && rm -f -- "$_job"'
793 echo "$_exe $@"
794 } > "/tmp/$$-${_job}.sh"
795 else
796 echo "$_exe $@" > "/tmp/$$-${_job}.sh"
797 fi
798
799 mv -f "/tmp/$$-${_job}.sh" "$hookdir/${_hookname}/${_job}.sh"
800 }
801
802 # inst_mount_hook <mountpoint> <prio> <name> <script>
803 #
804 # Install a mount hook with priority <prio>,
805 # which executes <script> as soon as <mountpoint> is mounted.
806 inst_mount_hook() {
807 local _prio="$2" _jobname="$3" _script="$4"
808 local _hookname="mount-$(str_replace "$1" '/' '\\x2f')"
809 [ -d "$hookdir/${_hookname}" ] || mkdir -p "$hookdir/${_hookname}"
810 inst_hook --hook "$_hookname" --unique --name "${_prio}-${_jobname}" "$_script"
811 }
812
813 # add_mount_point <dev> <mountpoint> <filesystem> <fsopts>
814 #
815 # Mount <dev> on <mountpoint> with <filesystem> and <fsopts>
816 # and call any mount hooks, as soon, as it is mounted
817 add_mount_point() {
818 local _dev="$1" _mp="$2" _fs="$3" _fsopts="$4"
819 local _hookname="mount-$(str_replace "$2" '/' '\\x2f')"
820 local _devname="dev-$(str_replace "$1" '/' '\\x2f')"
821 echo "$_dev $_mp $_fs $_fsopts 0 0" >> /etc/fstab
822
823 exec 7>/etc/udev/rules.d/99-mount-${_devname}.rules
824 echo 'SUBSYSTEM!="block", GOTO="mount_end"' >&7
825 echo 'ACTION!="add|change", GOTO="mount_end"' >&7
826 if [ -n "$_dev" ]; then
827 udevmatch "$_dev" >&7 || {
828 warn "add_mount_point dev=$_dev incorrect!"
829 continue
830 }
831 printf ', ' >&7
832 fi
833
834 {
835 printf -- 'RUN+="%s --unique --onetime ' $(command -v initqueue)
836 printf -- '--name mount-%%k '
837 printf -- '%s %s"\n' "$(command -v mount_hook)" "${_mp}"
838 } >&7
839 echo 'LABEL="mount_end"' >&7
840 exec 7>&-
841 }
842
843 # wait_for_mount <mountpoint>
844 #
845 # Installs a initqueue-finished script,
846 # which will cause the main loop only to exit,
847 # if <mountpoint> is mounted.
848 wait_for_mount()
849 {
850 local _name
851 _name="$(str_replace "$1" '/' '\\x2f')"
852 printf '. /lib/dracut-lib.sh\nismounted "%s"\n' $1 \
853 >> "$hookdir/initqueue/finished/ismounted-${_name}.sh"
854 {
855 printf 'ismounted "%s" || ' $1
856 printf 'warn "\"%s\" is not mounted"\n' $1
857 } >> "$hookdir/emergency/90-${_name}.sh"
858 }
859
860 dev_unit_name()
861 {
862 _name="${1%%/}"
863 _name="${_name##/}"
864 _name="$(str_replace "$_name" '-' '\x2d')"
865 _name="$(str_replace "$_name" '/' '-')"
866 echo "$_name"
867 }
868
869 # wait_for_dev <dev>
870 #
871 # Installs a initqueue-finished script,
872 # which will cause the main loop only to exit,
873 # if the device <dev> is recognized by the system.
874 wait_for_dev()
875 {
876 local _name
877 local _needreload
878 local _noreload
879
880 if [ "$1" = "-n" ]; then
881 _noreload=1
882 shift
883 fi
884
885 _name="$(str_replace "$1" '/' '\x2f')"
886
887 [ -e "${PREFIX}$hookdir/initqueue/finished/devexists-${_name}.sh" ] && return 0
888
889 printf '[ -e "%s" ]\n' $1 \
890 >> "${PREFIX}$hookdir/initqueue/finished/devexists-${_name}.sh"
891 {
892 printf '[ -e "%s" ] || ' $1
893 printf 'warn "\"%s\" does not exist"\n' $1
894 } >> "${PREFIX}$hookdir/emergency/80-${_name}.sh"
895
896 if [ -n "$DRACUT_SYSTEMD" ]; then
897 _name=$(dev_unit_name "$1")
898 if ! [ -L ${PREFIX}/etc/systemd/system/initrd.target.wants/${_name}.device ]; then
899 [ -d ${PREFIX}/etc/systemd/system/initrd.target.wants ] || mkdir -p ${PREFIX}/etc/systemd/system/initrd.target.wants
900 ln -s ../${_name}.device ${PREFIX}/etc/systemd/system/initrd.target.wants/${_name}.device
901 _needreload=1
902 fi
903
904 if ! [ -f ${PREFIX}/etc/systemd/system/${_name}.device.d/timeout.conf ]; then
905 mkdir -p ${PREFIX}/etc/systemd/system/${_name}.device.d
906 {
907 echo "[Unit]"
908 echo "JobTimeoutSec=0"
909 } > ${PREFIX}/etc/systemd/system/${_name}.device.d/timeout.conf
910 _needreload=1
911 fi
912
913 if [ -z "$PREFIX" ] && [ "$_needreload" = 1 ] && [ -z "$_noreload" ]; then
914 /sbin/initqueue --onetime --unique --name daemon-reload systemctl daemon-reload
915 fi
916 fi
917 }
918
919 cancel_wait_for_dev()
920 {
921 local _name
922 _name="$(str_replace "$1" '/' '\\x2f')"
923 rm -f -- "$hookdir/initqueue/finished/devexists-${_name}.sh"
924 rm -f -- "$hookdir/emergency/80-${_name}.sh"
925 if [ -n "$DRACUT_SYSTEMD" ]; then
926 _name=$(dev_unit_name "$1")
927 rm -f -- ${PREFIX}/etc/systemd/system/initrd.target.wants/${_name}.device
928 rm -f -- ${PREFIX}/etc/systemd/system/${_name}.device.d/timeout.conf
929 /sbin/initqueue --onetime --unique --name daemon-reload systemctl daemon-reload
930 fi
931 }
932
933 killproc() {
934 debug_off
935 local _exe="$(command -v $1)"
936 local _sig=$2
937 local _i
938 [ -x "$_exe" ] || return 1
939 for _i in /proc/[0-9]*; do
940 [ "$_i" = "/proc/1" ] && continue
941 if [ -e "$_i"/_exe ] && [ "$_i/_exe" -ef "$_exe" ] ; then
942 kill $_sig ${_i##*/}
943 fi
944 done
945 debug_on
946 }
947
948 need_shutdown() {
949 >/run/initramfs/.need_shutdown
950 }
951
952 wait_for_loginit()
953 {
954 [ "$RD_DEBUG" = "yes" ] || return
955 [ -e /run/initramfs/loginit.pipe ] || return
956 debug_off
957 echo "DRACUT_LOG_END"
958 exec 0<>/dev/console 1<>/dev/console 2<>/dev/console
959 # wait for loginit
960 i=0
961 while [ $i -lt 10 ]; do
962 if [ ! -e /run/initramfs/loginit.pipe ]; then
963 j=$(jobs)
964 [ -z "$j" ] && break
965 [ -z "${j##*Running*}" ] || break
966 fi
967 sleep 0.1
968 i=$(($i+1))
969 done
970
971 if [ $i -eq 10 ]; then
972 kill %1 >/dev/null 2>&1
973 kill $(while read line;do echo $line;done</run/initramfs/loginit.pid)
974 fi
975
976 setdebug
977 rm -f -- /run/initramfs/loginit.pipe /run/initramfs/loginit.pid
978 }
979
980 # pidof version for root
981 if ! command -v pidof >/dev/null 2>/dev/null; then
982 pidof() {
983 local _cmd
984 local _exe
985 local _rl
986 local i
987 _cmd="$1"
988 [ -z "$_cmd" ] && return 1
989 _exe=$(type -P "$1")
990 for i in /proc/*/exe; do
991 [ -e "$i" ] || continue
992 if [ -n "$_exe" ]; then
993 [ "$i" -ef "$_exe" ] || continue
994 else
995 _rl=$(readlink -f "$i");
996 [ "${_rl%/$_cmd}" != "$_rl" ] || continue
997 fi
998 i=${i%/exe}
999 echo ${i##/proc/}
1000 done
1001 return 0
1002 }
1003 fi
1004
1005 _emergency_shell()
1006 {
1007 local _name="$1"
1008 if [ -n "$DRACUT_SYSTEMD" ]; then
1009 > /.console_lock
1010 echo "PS1=\"$_name:\\\${PWD}# \"" >/etc/profile
1011 systemctl start dracut-emergency.service
1012 rm -f -- /etc/profile
1013 rm -f -- /.console_lock
1014 else
1015 debug_off
1016 echo
1017 /sbin/rdsosreport
1018 echo 'You might want to save "/run/initramfs/rdsosreport.txt" to a USB stick or /boot'
1019 echo 'after mounting them and attach it to a bug report.'
1020 if ! RD_DEBUG= getargbool 0 rd.debug -d -y rdinitdebug -d -y rdnetdebug; then
1021 echo
1022 echo 'To get more debug information in the report,'
1023 echo 'reboot with "rd.debug" added to the kernel command line.'
1024 fi
1025 echo
1026 echo 'Dropping to debug shell.'
1027 echo
1028 export PS1="$_name:\${PWD}# "
1029 [ -e /.profile ] || >/.profile
1030
1031 _ctty="$(RD_DEBUG= getarg rd.ctty=)" && _ctty="/dev/${_ctty##*/}"
1032 if [ -z "$_ctty" ]; then
1033 _ctty=console
1034 while [ -f /sys/class/tty/$_ctty/active ]; do
1035 _ctty=$(cat /sys/class/tty/$_ctty/active)
1036 _ctty=${_ctty##* } # last one in the list
1037 done
1038 _ctty=/dev/$_ctty
1039 fi
1040 [ -c "$_ctty" ] || _ctty=/dev/tty1
1041 case "$(/usr/bin/setsid --help 2>&1)" in *--ctty*) CTTY="--ctty";; esac
1042 setsid $CTTY /bin/sh -i -l 0<>$_ctty 1<>$_ctty 2<>$_ctty
1043 fi
1044 }
1045
1046 emergency_shell()
1047 {
1048 local _ctty
1049 set +e
1050 local _rdshell_name="dracut" action="Boot" hook="emergency"
1051 if [ "$1" = "-n" ]; then
1052 _rdshell_name=$2
1053 shift 2
1054 elif [ "$1" = "--shutdown" ]; then
1055 _rdshell_name=$2; action="Shutdown"; hook="shutdown-emergency"
1056 if type plymouth >/dev/null 2>&1; then
1057 plymouth --hide-splash
1058 elif [ -x /oldroot/bin/plymouth ]; then
1059 /oldroot/bin/plymouth --hide-splash
1060 fi
1061 shift 2
1062 fi
1063
1064 echo ; echo
1065 warn "$*"
1066 source_hook "$hook"
1067 echo
1068
1069 if getargbool 1 rd.shell -d -y rdshell || getarg rd.break -d rdbreak; then
1070 _emergency_shell $_rdshell_name
1071 else
1072 warn "$action has failed. To debug this issue add \"rd.shell rd.debug\" to the kernel command line."
1073 # cause a kernel panic
1074 exit 1
1075 fi
1076
1077 if [ -e /run/initramfs/.die ]; then
1078 if [ -n "$DRACUT_SYSTEMD" ]; then
1079 systemctl --no-block --force halt
1080 fi
1081 exit 1
1082 fi
1083 }
1084
1085 action_on_fail()
1086 {
1087 if [ -f "$initdir/lib/dracut/no-emergency-shell" ]; then
1088 [ "$1" = "-n" ] && shift 2
1089 [ "$1" = "--shutdown" ] && shift 2
1090 warn "$*"
1091 warn "Not dropping to emergency shell, because $initdir/lib/dracut/no-emergency-shell exists."
1092 return 0
1093 fi
1094
1095 emergency_shell $@
1096 return 1
1097 }
1098
1099 # Retain the values of these variables but ensure that they are unexported
1100 # This is a POSIX-compliant equivalent of bash's "export -n"
1101 export_n()
1102 {
1103 local var
1104 local val
1105 for var in "$@"; do
1106 eval val=\$$var
1107 unset $var
1108 [ -n "$val" ] && eval $var=\"$val\"
1109 done
1110 }
1111
1112 # returns OK if list1 contains all elements of list2, i.e. checks if list2 is a
1113 # sublist of list1. An order and a duplication doesn't matter.
1114 #
1115 # $1 = separator
1116 # $2 = list1
1117 # $3 = list2
1118 # $4 = ignore values, separated by $1
1119 listlist() {
1120 local _sep="$1"
1121 local _list="${_sep}${2}${_sep}"
1122 local _sublist="$3"
1123 [ -n "$4" ] && local _iglist="${_sep}${4}${_sep}"
1124 local IFS="$_sep"
1125 local _v
1126
1127 [ "$_list" = "$_sublist" ] && return 0
1128
1129 for _v in $_sublist; do
1130 if [ -n "$_v" ] && ! ( [ -n "$_iglist" ] && strstr "$_iglist" "$_v" )
1131 then
1132 strstr "$_list" "$_v" || return 1
1133 fi
1134 done
1135
1136 return 0
1137 }
1138
1139 # returns OK if both lists contain the same values. An order and a duplication
1140 # doesn't matter.
1141 #
1142 # $1 = separator
1143 # $2 = list1
1144 # $3 = list2
1145 # $4 = ignore values, separated by $1
1146 are_lists_eq() {
1147 listlist "$1" "$2" "$3" "$4" && listlist "$1" "$3" "$2" "$4"
1148 }
1149
1150 setmemdebug() {
1151 if [ -z "$DEBUG_MEM_LEVEL" ]; then
1152 export DEBUG_MEM_LEVEL=$(getargnum 0 0 3 rd.memdebug)
1153 fi
1154 }
1155
1156 setmemdebug
1157
1158 # parameters: msg [trace_level:trace]...
1159 make_trace_mem()
1160 {
1161 local msg
1162 msg="$1"
1163 shift
1164 if [ -n "$DEBUG_MEM_LEVEL" ] && [ "$DEBUG_MEM_LEVEL" -gt 0 ]; then
1165 make_trace show_memstats $DEBUG_MEM_LEVEL "[debug_mem]" "$msg" "$@" >&2
1166 fi
1167 }
1168
1169 # parameters: func log_level prefix msg [trace_level:trace]...
1170 make_trace()
1171 {
1172 local func log_level prefix msg msg_printed
1173 local trace trace_level trace_in_higher_levels insert_trace
1174
1175 func=$1
1176 shift
1177
1178 log_level=$1
1179 shift
1180
1181 prefix=$1
1182 shift
1183
1184 msg=$1
1185 shift
1186
1187 if [ -z "$log_level" ]; then
1188 return
1189 fi
1190
1191 msg=$(echo $msg)
1192
1193 msg_printed=0
1194 while [ $# -gt 0 ]; do
1195 trace=${1%%:*}
1196 trace_level=${trace%%+}
1197 [ "$trace" != "$trace_level" ] && trace_in_higher_levels="yes"
1198 trace=${1##*:}
1199
1200 if [ -z "$trace_level" ]; then
1201 trace_level=0
1202 fi
1203
1204 insert_trace=0
1205 if [ -n "$trace_in_higher_levels" ]; then
1206 if [ "$log_level" -ge "$trace_level" ]; then
1207 insert_trace=1
1208 fi
1209 else
1210 if [ "$log_level" -eq "$trace_level" ]; then
1211 insert_trace=1
1212 fi
1213 fi
1214
1215 if [ $insert_trace -eq 1 ]; then
1216 if [ $msg_printed -eq 0 ]; then
1217 echo "$prefix $msg"
1218 msg_printed=1
1219 fi
1220 $func $trace
1221 fi
1222 shift
1223 done
1224 }
1225
1226 # parameters: type
1227 show_memstats()
1228 {
1229 case $1 in
1230 shortmem)
1231 cat /proc/meminfo | grep -e "^MemFree" -e "^Cached" -e "^Slab"
1232 ;;
1233 mem)
1234 cat /proc/meminfo
1235 ;;
1236 slab)
1237 cat /proc/slabinfo
1238 ;;
1239 iomem)
1240 cat /proc/iomem
1241 ;;
1242 esac
1243 }