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