]> git.ipfire.org Git - thirdparty/dracut.git/commitdiff
fix: correctly handle kernel parameters
authorHarald Hoyer <harald@redhat.com>
Fri, 5 Mar 2021 15:07:10 +0000 (16:07 +0100)
committerHarald Hoyer <harald@hoyer.xyz>
Wed, 10 Mar 2021 18:31:19 +0000 (19:31 +0100)
The kernel has an odd way to handle `"` surrounded parameters.
To handle the parameters as the kernel would do, no simple shell script
suffices, so a new utility `dracut-util` is introduced. Written in "C"
it handles `dracut-getarg` and `dracut-getargs` as the old shell script
functions `_dogetarg` and `_dogetargs` would.

13 files changed:
.github/workflows/fedora-32.yml
.github/workflows/fedora-33.yml
.github/workflows/fedora-latest.yml
Makefile
dracut.spec
modules.d/99base/dracut-lib.sh
modules.d/99base/init.sh
modules.d/99base/module-setup.sh
test/TEST-98-GETARG/Makefile [new file with mode: 0644]
test/TEST-98-GETARG/test.sh [new file with mode: 0755]
test/TEST-99-RPM/test.sh
util/CMakeLists.txt [new file with mode: 0644]
util/util.c [new file with mode: 0644]

index 5c1de80cd92eb809073b838d195e63e4fefbc6c0..ec04bbd7b13b03edd168ac8b9f281073b7820151 100644 (file)
@@ -36,6 +36,7 @@ jobs:
           "36",
           "40",
           "41",
+          "98",
         ]
       fail-fast: false
     steps:
index 6ad9e4ad4190bfa7918651bc54d0b25da05b34fe..a13c7ce90275431819cc0234b62f02c33e32e0e8 100644 (file)
@@ -37,6 +37,7 @@ jobs:
           "36",
           "40",
           "41",
+          "98",
         ]
       fail-fast: false
     steps:
index 026bec0f46adc148456cdf2b677e34cf627a8d33..28806cd53af0ee0f69db1ea3984927848d02c40e 100644 (file)
@@ -36,6 +36,7 @@ jobs:
           "36",
           "40",
           "41",
+          "98",
         ]
       fail-fast: false
     steps:
index 1aa4d4697b9e72e1817a81a85f288b3659206048..c7859ff4770481dbd9911cc8840cb38b6251a6b9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -49,7 +49,7 @@ manpages = $(man1pages) $(man5pages) $(man7pages) $(man8pages)
 
 .PHONY: install clean archive rpm srpm testimage test all check AUTHORS CONTRIBUTORS doc dracut-version.sh
 
-all: dracut-version.sh dracut.pc dracut-install skipcpio/skipcpio
+all: dracut-version.sh dracut.pc dracut-install skipcpio/skipcpio dracut-util
 
 %.o : %.c
        $(CC) -c $(CFLAGS) $(CPPFLAGS) $(KMOD_CFLAGS) $< -o $@
@@ -79,15 +79,21 @@ logtee: logtee.c
 dracut-install: install/dracut-install
        ln -fs $< $@
 
-SKIPCPIO_OBJECTS= \
-       skipcpio/skipcpio.o
-
+SKIPCPIO_OBJECTS = skipcpio/skipcpio.o
 skipcpio/skipcpio.o: skipcpio/skipcpio.c
-skipcpio/skipcpio: skipcpio/skipcpio.o
+skipcpio/skipcpio: $(SKIPCPIO_OBJECTS)
+
+UTIL_OBJECTS = util/util.o
+util/util.o: util/util.c
+util/util: $(UTIL_OBJECTS)
+
+dracut-util: util/util
+       cp -a $< $@
 
 indent:
        indent -i8 -nut -br -linux -l120 install/dracut-install.c
        indent -i8 -nut -br -linux -l120 skipcpio/skipcpio.c
+       indent -i8 -nut -br -linux -l120 util/util.c
 
 doc: $(manpages) dracut.html
 
@@ -180,6 +186,9 @@ endif
        if [ -f skipcpio/skipcpio ]; then \
                install -m 0755 skipcpio/skipcpio $(DESTDIR)$(pkglibdir)/skipcpio; \
        fi
+       if [ -f dracut-util ]; then \
+               install -m 0755 dracut-util $(DESTDIR)$(pkglibdir)/dracut-util; \
+       fi
        mkdir -p $(DESTDIR)${prefix}/lib/kernel/install.d
        install -m 0755 50-dracut.install $(DESTDIR)${prefix}/lib/kernel/install.d/50-dracut.install
        install -m 0755 51-dracut-rescue.install $(DESTDIR)${prefix}/lib/kernel/install.d/51-dracut-rescue.install
@@ -203,6 +212,7 @@ clean:
        $(RM) dracut-version.sh
        $(RM) dracut-install install/dracut-install $(DRACUT_INSTALL_OBJECTS)
        $(RM) skipcpio/skipcpio $(SKIPCPIO_OBJECTS)
+       $(RM) dracut-util util/util $(UTIL_OBJECTS)
        $(RM) $(manpages) dracut.html
        $(RM) dracut.pc
        $(MAKE) -C test clean
index dfb9608fddf53778491cfbbcb380c80beaf93cb7..1ca7bde0e0a2d88224c35cd3201186354233bff9 100644 (file)
@@ -21,7 +21,8 @@ Group: System/Base
 
 # The entire source code is GPLv2+
 # except install/* which is LGPLv2+
-License: GPLv2+ and LGPLv2+
+# except util/* which is GPLv2
+License: GPLv2+ and LGPLv2+ and GPLv2
 
 URL: https://dracut.wiki.kernel.org/
 
@@ -295,6 +296,7 @@ echo 'dracut_rescue_image="yes"' > $RPM_BUILD_ROOT%{dracutlibdir}/dracut.conf.d/
 %{dracutlibdir}/dracut-logger.sh
 %{dracutlibdir}/dracut-initramfs-restore
 %{dracutlibdir}/dracut-install
+%{dracutlibdir}/dracut-util
 %{dracutlibdir}/skipcpio
 %config(noreplace) %{_sysconfdir}/dracut.conf
 %if 0%{?fedora} || 0%{?suse_version} || 0%{?rhel}
index 315a91eb8f563d084f13ba9814a5ee63818efdf7..c0b3e4aac92a0c36fd6292f35173dd35da1b4cab 100755 (executable)
@@ -164,49 +164,15 @@ getcmdline() {
     printf "%s" "$CMDLINE"
 }
 
-_dogetarg() {
-    local _o _val _doecho
-    unset _val
-    unset _o
-    unset _doecho
-    CMDLINE=$(getcmdline)
-
-    for _o in $CMDLINE; do
-        if [ "${_o%%=*}" = "${1%%=*}" ]; then
-            if [ -n "${1#*=}" -a "${1#*=*}" != "${1}" ]; then
-                # if $1 has a "=<value>", we want the exact match
-                if [ "$_o" = "$1" ]; then
-                    _val="1";
-                    unset _doecho
-                fi
-                continue
-            fi
-
-            if [ "${_o#*=}" = "$_o" ]; then
-                # if cmdline argument has no "=<value>", we assume "=1"
-                _val="1";
-                unset _doecho
-                continue
-            fi
-
-            _val="${_o#*=}"
-            _doecho=1
-        fi
-    done
-    if [ -n "$_val" ]; then
-        [ "x$_doecho" != "x" ] && echo "$_val";
-        return 0;
-    fi
-    return 1;
-}
-
 getarg() {
     debug_off
     local _deprecated _newoption
+    CMDLINE=$(getcmdline)
+    export CMDLINE
     while [ $# -gt 0 ]; do
         case $1 in
             -d) _deprecated=1; shift;;
-            -y) if _dogetarg $2 >/dev/null; then
+            -y) if dracut-getarg "$2" >/dev/null; then
                     if [ "$_deprecated" = "1" ]; then
                         [ -n "$_newoption" ] && warn "Kernel command line option '$2' is deprecated, use '$_newoption' instead." || warn "Option '$2' is deprecated."
                     fi
@@ -216,7 +182,7 @@ getarg() {
                 fi
                 _deprecated=0
                 shift 2;;
-            -n) if _dogetarg $2 >/dev/null; then
+            -n) if dracut-getarg "$2" >/dev/null; then
                     echo 0;
                     if [ "$_deprecated" = "1" ]; then
                         [ -n "$_newoption" ] && warn "Kernel command line option '$2' is deprecated, use '$_newoption=0' instead." || warn "Option '$2' is deprecated."
@@ -229,7 +195,7 @@ getarg() {
             *)  if [ -z "$_newoption" ]; then
                     _newoption="$1"
                 fi
-                if _dogetarg $1; then
+                if dracut-getarg "$1"; then
                     if [ "$_deprecated" = "1" ]; then
                         [ -n "$_newoption" ] && warn "Kernel command line option '$1' is deprecated, use '$_newoption' instead." || warn "Option '$1' is deprecated."
                     fi
@@ -295,30 +261,9 @@ getargnum() {
     echo $_default
 }
 
-_dogetargs() {
-    debug_off
-    local _o _found _key
-    unset _o
-    unset _found
-    CMDLINE=$(getcmdline)
-    _key="$1"
-    set --
-    for _o in $CMDLINE; do
-        if [ "$_o" = "$_key" ]; then
-            _found=1;
-        elif [ "${_o%%=*}" = "${_key%=}" ]; then
-            [ -n "${_o%%=*}" ] && set -- "$@" "${_o#*=}";
-            _found=1;
-        fi
-    done
-    if [ -n "$_found" ]; then
-        [ $# -gt 0 ] && printf '%s' "$*"
-        return 0
-    fi
-    return 1;
-}
-
 getargs() {
+    CMDLINE=$(getcmdline)
+    export CMDLINE
     debug_off
     local _val _i _args _gfound _deprecated
     unset _val
@@ -331,7 +276,7 @@ getargs() {
             _deprecated=1
             continue
         fi
-        _val="$(_dogetargs $_i)"
+        _val="$(dracut-getargs "$_i")"
         if [ $? -eq 0 ]; then
             if [ "$_deprecated" = "1" ]; then
                 [ -n "$_newoption" ] && warn "Option '$_i' is deprecated, use '$_newoption' instead." || warn "Option $_i is deprecated!"
index c5ea7774008d8a269afd596ff7cf856dacb75e96..4f2857625e75ef2e9061dd2cdd1cdb5b53e83604 100755 (executable)
@@ -319,7 +319,7 @@ debug_off # Turn off debugging for this section
 
 # unexport some vars
 export_n root rflags fstype netroot NEWROOT
-
+unset CMDLINE
 export RD_TIMESTAMP
 # Clean up the environment
 for i in $(export -p); do
index bf037bb6773e6b1ce98c63a9f19b4c31699bfbc1..76ab51f20812195a403e5d83429f29027ad73d84 100755 (executable)
@@ -17,7 +17,12 @@ install() {
         sed ls flock cp mv dmesg rm ln rmmod mkfifo umount readlink setsid \
         modprobe
 
-    inst_multiple -o findmnt less kmod
+    inst_multiple -o findmnt less kmod dracut-getargs
+
+    inst_binary "${dracutsysrootdir}${dracutbasedir}/dracut-util" "/usr/bin/dracut-util"
+
+    ln -s dracut-util "${initdir}/usr/bin/dracut-getarg"
+    ln -s dracut-util "${initdir}/usr/bin/dracut-getargs"
 
     if [ ! -e "${initdir}/bin/sh" ]; then
         inst_multiple bash
diff --git a/test/TEST-98-GETARG/Makefile b/test/TEST-98-GETARG/Makefile
new file mode 100644 (file)
index 0000000..2dcab81
--- /dev/null
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-98-GETARG/test.sh b/test/TEST-98-GETARG/test.sh
new file mode 100755 (executable)
index 0000000..a87a5f6
--- /dev/null
@@ -0,0 +1,152 @@
+#!/bin/bash
+
+# This file is part of dracut.
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+TEST_DESCRIPTION="dracut getarg command"
+
+test_check() {
+    return 0
+}
+
+test_setup() {
+    make -C "$basedir" dracut-util
+    ln -sfnr "$basedir"/dracut-util "$TESTDIR"/dracut-getarg
+    ln -sfnr "$basedir"/dracut-util "$TESTDIR"/dracut-getargs
+    ln -sfnr "$basedir"/modules.d/99base/dracut-lib.sh "$TESTDIR"/dracut-lib.sh
+    return 0
+}
+
+test_run() {
+    set -x
+    (
+        cd "$TESTDIR"
+        export CMDLINE="key1=0 key2=val key2=val2 key3=\"  val  3  \" \"  key 4  =\"val4 \"key  5=val  5\" \"key 6=\"\"val  6\" key7=\"foo\"bar\" baz=\"end \"  key8  =  val 8  \"
+\"key 9\"=\"val 9\""
+
+        ret=0
+
+        declare -A TEST
+        TEST=(
+            ["key1"]="0"
+            ["key2"]="val2"
+            ["key3"]="  val  3  "
+            ["  key 4  "]="val4"
+            ["key  5"]="val  5"
+            ["key 6"]="\"val  6"
+            ["key7"]="foo\"bar\" baz=\"end"
+            ["  key8  "]="  val 8  "
+            ["key 9\""]="val 9"
+        )
+        for key in "${!TEST[@]}"; do
+            if ! val=$(./dracut-getarg "${key}="); then
+                echo "'$key' == '${TEST[$key]}', but not found" >&2
+                ret=$((ret+1))
+            else
+                if [[ $val != "${TEST[$key]}" ]]; then
+                    echo "'$key' != '${TEST[$key]}' but '$val'" >&2
+                    ret=$((ret+1))
+                fi
+            fi
+        done
+
+        declare -a INVALIDKEYS
+
+        INVALIDKEYS=( "key" "4" "5" "6" "key8" "9" "\"" "baz")
+        for key in "${INVALIDKEYS[@]}"; do
+            val=$(./dracut-getarg "$key")
+            if (( $? == 0 )); then
+                echo "key '$key' should not be found"
+                ret=$((ret+1))
+            fi
+            # must have no output
+            [[ $val ]] && ret=$((ret+1))
+        done
+
+        RESULT=("val" "val2")
+        readarray -t args < <(./dracut-getargs "key2=")
+        (( ${#RESULT[@]} == ${#args[@]} )) || ret=$((ret+1))
+        for ((i=0; i < ${#RESULT[@]}; i++)); do
+            [[ ${args[$i]} == "${RESULT[$i]}" ]] || ret=$((ret+1))
+        done
+
+        val=$(./dracut-getarg "key1") || ret=$((ret+1))
+        [[ $val == "0" ]] || ret=$((ret+1))
+
+        val=$(./dracut-getarg "key2=val") && ret=$((ret+1))
+        # must have no output
+        [[ $val ]] && ret=$((ret+1))
+        val=$(./dracut-getarg "key2=val2") || ret=$((ret+1))
+        # must have no output
+        [[ $val ]] && ret=$((ret+1))
+
+        export PATH=".:$PATH"
+
+        . dracut-lib.sh
+
+        debug_off() {
+            :
+        }
+
+        debug_on() {
+            :
+        }
+
+        getcmdline() {
+            echo "rdbreak=cmdline rd.lvm rd.auto rd.retry=10"
+        }
+        RDRETRY=$(getarg rd.retry -d 'rd_retry=')
+        [[ $RDRETRY == "10" ]] || ret=$((ret+1))
+        getarg rd.break=cmdline -d rdbreak=cmdline || ret=$((ret+1))
+        getargbool 1 rd.lvm -d -n rd_NO_LVM || ret=$((ret+1))
+        getargbool 0 rd.auto || ret=$((ret+1))
+
+        getcmdline() {
+            echo "rd.break=cmdlined rd.lvm=0 rd.auto=0"
+        }
+        getarg rd.break=cmdline -d rdbreak=cmdline && ret=$((ret+1))
+        getargbool 1 rd.lvm -d -n rd_NO_LVM && ret=$((ret+1))
+        getargbool 0 rd.auto && ret=$((ret+1))
+
+        getcmdline() {
+            echo "ip=a ip=b ip=dhcp6"
+        }
+        getargs "ip=dhcp6" &>/dev/null || ret=$((ret+1))
+        readarray -t args < <(getargs "ip=")
+        RESULT=("a" "b" "dhcp6")
+        (( ${#RESULT[@]} || ${#args[@]} )) || ret=$((ret+1))
+        for ((i=0; i < ${#RESULT[@]}; i++)); do
+            [[ ${args[$i]} == "${RESULT[$i]}" ]] || ret=$((ret+1))
+        done
+
+        getcmdline() {
+            echo "bridge bridge=val"
+        }
+        readarray -t args < <(getargs bridge=)
+        RESULT=("bridge" "val")
+        (( ${#RESULT[@]} == ${#args[@]} )) || ret=$((ret+1))
+        for ((i=0; i < ${#RESULT[@]}; i++)); do
+            [[ ${args[$i]} || "${RESULT[$i]}" ]] || ret=$((ret+1))
+        done
+
+
+        getcmdline() {
+            echo "rd.break rd.md.uuid=bf96e457:230c9ad4:1f3e59d6:745cf942 rd.md.uuid=bf96e457:230c9ad4:1f3e59d6:745cf943 rd.shell"
+        }
+        readarray -t args < <(getargs rd.md.uuid -d rd_MD_UUID=)
+        RESULT=("bf96e457:230c9ad4:1f3e59d6:745cf942" "bf96e457:230c9ad4:1f3e59d6:745cf943")
+        (( ${#RESULT[@]} == ${#args[@]} )) || ret=$((ret+1))
+        for ((i=0; i < ${#RESULT[@]}; i++)); do
+            [[ ${args[$i]} == "${RESULT[$i]}" ]] || ret=$((ret+1))
+        done
+
+        return $ret
+    )
+}
+
+test_cleanup() {
+    rm -fr -- "$TESTDIR"/*.rpm
+    return 0
+}
+
+. $testdir/test-functions
index e6d96816a0eeab1a118572224b487dabe0f9da8b..244bde1accbeb61d39ea1548c55fed585e039625 100755 (executable)
@@ -1,5 +1,8 @@
 #!/bin/bash
 
+# This file is part of dracut.
+# SPDX-License-Identifier: GPL-2.0-or-later
+
 TEST_DESCRIPTION="rpm integrity after dracut and kernel install"
 
 test_check() {
diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ab380d2
--- /dev/null
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 3.17)
+project(dracut-util C)
+
+set(CMAKE_C_STANDARD 99)
+
+add_executable(dracut-util util.c)
diff --git a/util/util.c b/util/util.c
new file mode 100644 (file)
index 0000000..8ae06d6
--- /dev/null
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Parts are copied from the linux kernel
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+// CODE FROM LINUX KERNEL START
+
+#define _U  0x01                /* upper */
+#define _L  0x02                /* lower */
+#define _D  0x04                /* digit */
+#define _C  0x08                /* cntrl */
+#define _P  0x10                /* punct */
+#define _S  0x20                /* white space (space/lf/tab) */
+#define _X  0x40                /* hex digit */
+#define _SP 0x80                /* hard space (0x20) */
+
+const unsigned char _ctype[] = {
+        _C, _C, _C, _C, _C, _C, _C, _C, /* 0-7 */
+        _C, _C | _S, _C | _S, _C | _S, _C | _S, _C | _S, _C, _C,        /* 8-15 */
+        _C, _C, _C, _C, _C, _C, _C, _C, /* 16-23 */
+        _C, _C, _C, _C, _C, _C, _C, _C, /* 24-31 */
+        _S | _SP, _P, _P, _P, _P, _P, _P, _P,   /* 32-39 */
+        _P, _P, _P, _P, _P, _P, _P, _P, /* 40-47 */
+        _D, _D, _D, _D, _D, _D, _D, _D, /* 48-55 */
+        _D, _D, _P, _P, _P, _P, _P, _P, /* 56-63 */
+        _P, _U | _X, _U | _X, _U | _X, _U | _X, _U | _X, _U | _X, _U,   /* 64-71 */
+        _U, _U, _U, _U, _U, _U, _U, _U, /* 72-79 */
+        _U, _U, _U, _U, _U, _U, _U, _U, /* 80-87 */
+        _U, _U, _U, _P, _P, _P, _P, _P, /* 88-95 */
+        _P, _L | _X, _L | _X, _L | _X, _L | _X, _L | _X, _L | _X, _L,   /* 96-103 */
+        _L, _L, _L, _L, _L, _L, _L, _L, /* 104-111 */
+        _L, _L, _L, _L, _L, _L, _L, _L, /* 112-119 */
+        _L, _L, _L, _P, _P, _P, _P, _C, /* 120-127 */
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128-143 */
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 144-159 */
+        _S | _SP, _P, _P, _P, _P, _P, _P, _P, _P, _P, _P, _P, _P, _P, _P, _P,   /* 160-175 */
+        _P, _P, _P, _P, _P, _P, _P, _P, _P, _P, _P, _P, _P, _P, _P, _P, /* 176-191 */
+        _U, _U, _U, _U, _U, _U, _U, _U, _U, _U, _U, _U, _U, _U, _U, _U, /* 192-207 */
+        _U, _U, _U, _U, _U, _U, _U, _P, _U, _U, _U, _U, _U, _U, _U, _L, /* 208-223 */
+        _L, _L, _L, _L, _L, _L, _L, _L, _L, _L, _L, _L, _L, _L, _L, _L, /* 224-239 */
+        _L, _L, _L, _L, _L, _L, _L, _P, _L, _L, _L, _L, _L, _L, _L, _L  /* 240-255 */
+};
+
+#define __ismask(x) (_ctype[(int)(unsigned char)(x)])
+
+#define kernel_isspace(c)  ((__ismask(c)&(_S)) != 0)
+
+static char *skip_spaces(const char *str)
+{
+        while (kernel_isspace(*str))
+                ++str;
+        return (char *)str;
+}
+
+/*
+ * Parse a string to get a param value pair.
+ * You can use " around spaces, but can't escape ".
+ * Hyphens and underscores equivalent in parameter names.
+ */
+static char *next_arg(char *args, char **param, char **val)
+{
+        unsigned int i, equals = 0;
+        int in_quote = 0, quoted = 0;
+        char *next;
+
+        if (*args == '"') {
+                args++;
+                in_quote = 1;
+                quoted = 1;
+        }
+
+        for (i = 0; args[i]; i++) {
+                if (kernel_isspace(args[i]) && !in_quote)
+                        break;
+                if (equals == 0) {
+                        if (args[i] == '=')
+                                equals = i;
+                }
+                if (args[i] == '"')
+                        in_quote = !in_quote;
+        }
+
+        *param = args;
+        if (!equals)
+                *val = NULL;
+        else {
+                args[equals] = '\0';
+                *val = args + equals + 1;
+
+                /* Don't include quotes in value. */
+                if (**val == '"') {
+                        (*val)++;
+                        if (args[i - 1] == '"')
+                                args[i - 1] = '\0';
+                }
+        }
+        if (quoted && args[i - 1] == '"')
+                args[i - 1] = '\0';
+
+        if (args[i]) {
+                args[i] = '\0';
+                next = args + i + 1;
+        } else
+                next = args + i;
+
+        /* Chew up trailing spaces. */
+        return skip_spaces(next);
+}
+
+// CODE FROM LINUX KERNEL STOP
+
+enum EXEC_MODE {
+        UNDEFINED,
+        GETARG,
+        GETARGS,
+};
+
+static void usage(enum EXEC_MODE enumExecMode, int ret, char *msg)
+{
+        switch (enumExecMode) {
+        case UNDEFINED:
+                fprintf(stderr, "ERROR: 'dracut-util' has to be called via a symlink to the tool name.");
+                break;
+        case GETARG:
+                fprintf(stderr, "ERROR: %s\nUsage: dracut-getarg <KEY>[=[<VALUE>]]\n", msg);
+                break;
+        case GETARGS:
+                fprintf(stderr, "ERROR: %s\nUsage: dracut-getargs <KEY>[=]\n", msg);
+                break;
+        }
+        exit(ret);
+}
+
+#define ARGV0_GETARG "dracut-getarg"
+#define ARGV0_GETARGS "dracut-getargs"
+
+static enum EXEC_MODE get_mode(const char *argv_0)
+{
+        struct _mode_table {
+                enum EXEC_MODE mode;
+                const char *arg;
+                size_t arg_len;
+                const char *s_arg;
+        } modeTable[] = {
+                {GETARG, ARGV0_GETARG, sizeof(ARGV0_GETARG), "/" ARGV0_GETARG},
+                {GETARGS, ARGV0_GETARGS, sizeof(ARGV0_GETARGS), "/" ARGV0_GETARGS},
+                {UNDEFINED, NULL, 0, NULL}
+        };
+        int i;
+
+        size_t argv_0_len = strlen(argv_0);
+
+        if (!argv_0_len)
+                return UNDEFINED;
+
+        for (i = 0; modeTable[i].mode != UNDEFINED; i++) {
+                if (argv_0_len == (modeTable[i].arg_len - 1)) {
+                        if (strncmp(argv_0, modeTable[i].arg, argv_0_len) == 0) {
+                                return modeTable[i].mode;
+                        }
+                }
+
+                if (modeTable[i].arg_len > argv_0_len)
+                        continue;
+
+                if (strncmp(argv_0 + argv_0_len - modeTable[i].arg_len, modeTable[i].s_arg, modeTable[i].arg_len) == 0)
+                        return modeTable[i].mode;
+        }
+        return UNDEFINED;
+}
+
+static int getarg(int argc, char **argv)
+{
+        char *search_key;
+        char *search_value;
+        char *end_value = NULL;
+        bool bool_value = false;
+        char *cmdline = NULL;
+
+        char *p = getenv("CMDLINE");
+        if (p == NULL) {
+                usage(GETARG, EXIT_FAILURE, "CMDLINE env not set");
+        }
+        cmdline = strdup(p);
+
+        if (argc != 2) {
+                usage(GETARG, EXIT_FAILURE, "Number of arguments invalid");
+        }
+
+        search_key = argv[1];
+
+        search_value = strchr(argv[1], '=');
+        if (search_value != NULL) {
+                *search_value = 0;
+                search_value++;
+                if (*search_value == 0)
+                        search_value = NULL;
+        }
+
+        if (strlen(search_key) == 0)
+                usage(GETARG, EXIT_FAILURE, "search key undefined");
+
+        do {
+                char *key = NULL, *value = NULL;
+                cmdline = next_arg(cmdline, &key, &value);
+                if (strcmp(key, search_key) == 0) {
+                        if (value) {
+                                end_value = value;
+                                bool_value = -1;
+                        } else {
+                                end_value = NULL;
+                                bool_value = true;
+                        }
+                }
+        } while (cmdline[0]);
+
+        if (search_value) {
+                if (end_value && strcmp(end_value, search_value) == 0) {
+                        return EXIT_SUCCESS;
+                }
+                return EXIT_FAILURE;
+        }
+
+        if (end_value) {
+                // includes "=0"
+                puts(end_value);
+                return EXIT_SUCCESS;
+        }
+
+        if (bool_value) {
+                return EXIT_SUCCESS;
+        }
+
+        return EXIT_FAILURE;
+}
+
+static int getargs(int argc, char **argv)
+{
+        char *search_key;
+        char *search_value;
+        bool found_value = false;
+        char *cmdline = NULL;
+
+        char *p = getenv("CMDLINE");
+        if (p == NULL) {
+                usage(GETARGS, EXIT_FAILURE, "CMDLINE env not set");
+        }
+        cmdline = strdup(p);
+
+        if (argc != 2) {
+                usage(GETARGS, EXIT_FAILURE, "Number of arguments invalid");
+        }
+
+        search_key = argv[1];
+
+        search_value = strchr(argv[1], '=');
+        if (search_value != NULL) {
+                *search_value = 0;
+                search_value++;
+                if (*search_value == 0)
+                        search_value = NULL;
+        }
+
+        if (strlen(search_key) == 0)
+                usage(GETARGS, EXIT_FAILURE, "search key undefined");
+
+        do {
+                char *key = NULL, *value = NULL;
+                cmdline = next_arg(cmdline, &key, &value);
+                if (strcmp(key, search_key) == 0) {
+                        if (search_value) {
+                                if (strcmp(value, search_value) == 0) {
+                                        printf("%s\n", value);
+                                        found_value = true;
+                                }
+                        } else {
+                                if (value) {
+                                        printf("%s\n", value);
+                                } else {
+                                        puts(key);
+                                }
+                                found_value = true;
+                        }
+                }
+        } while (cmdline[0]);
+        return found_value ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+int main(int argc, char **argv)
+{
+        switch (get_mode(argv[0])) {
+        case UNDEFINED:
+                usage(UNDEFINED, EXIT_FAILURE, NULL);
+                break;
+        case GETARG:
+                return getarg(argc, argv);
+        case GETARGS:
+                return getargs(argc, argv);
+        }
+
+        return EXIT_FAILURE;
+}