--- /dev/null
+#!/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
--- /dev/null
+// 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;
+}