/
githubmirror
/
mdevctl
Обзор
Документация
Войти
/
githubmirror
/
mdevctl
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
0.50
mdevctl
1 067 строк
29 KB
Alex Williamson
Create an lsmdev alias
27 сен 2019, 19:14
27 сен 2019, 19:14
a6372fc
Код
Авторство
О чём код?
#!/usr/bin/bash persist_base=/etc/mdevctl.d mdev_base=/sys/bus/mdev/devices parent_base=/sys/class/mdev_bus # Alias 'lsmdev' to 'mdevctl list' if [ $(basename $0) == "lsmdev" ]; then set -- "list" "${@}" fi # See https://stackoverflow.com/a/29754866/4775714 for getopt usage getopt --test > /dev/null if [ $? -ne 4 ]; then echo "Sorry, $0 requires enhanced getopt support" exit 1 fi config={} attrs=[] jsonify() { echo "\"$1\"" } del_config_key() { key="$1" config=$(echo "$config" | jq -c -M --arg key "$key" 'del(."$key")') } set_config_key() { key=$(jsonify "$1") value=$(jsonify "$2") del_config_key "$key" config=$(echo "$config" | jq -c -M --argjson obj "{$key:$value}" '. + $obj') } has_config_key() { key="$1" if [ $(echo "$config" | jq -M --arg key "$key" 'has($key)') == "true" ]; then return 0 else return 1 fi } get_config_key() { key="$1" echo "$config" | jq -r -M --arg key "$key" '.[$key]' } config_file() { uuid="$1" parent="$2" if [ -n "$parent" ]; then if [ ! -e "$persist_base/$parent/$uuid" ]; then echo "Config for $uuid on $parent does not exist, define it first?" >&2 return 1 fi echo "$persist_base/$parent/$uuid" else count=$(find "$persist_base" -name "$uuid" -type f | wc -l) if [ "$count" -eq 0 ]; then echo "Config for $uuid does not exist, define it first?" >&2 return 1 elif [ "$count" -gt 1 ]; then echo "Multiple configs found for $uuid, specify a parent" >&2 return 1 fi echo $(find "$persist_base" -name "$uuid" -type f) fi return 0 } get_attr_length() { echo "$attrs" | jq -M '. | length' } get_attrs_raw() { echo "$attrs" } add_attr_index() { key=$(jsonify "$1") value=$(jsonify "$2") if [ -z "$3" ]; then index=$(get_attr_length) else index="$3" fi attrs=$(echo "$attrs" | jq -c -M --argjson obj "{$key:$value}" \ --argjson i $index '.[0:$i] + [$obj] + .[$i:]') } del_attr_index() { if [ -z "$1" ]; then index=$(( $(get_attr_length) - 1 )) else index="$1" fi attrs=$(echo "$attrs" | jq -c -M --argjson i "$index" '.[0:$i] + .[$i+1:]') } get_attr_index_key() { if [ -z "$1" ]; then index=0 else index="$1" fi echo "$attrs" | jq -r -M --argjson i "$index" '.[$i] | keys | .[]' } get_attr_index_value() { if [ -z "$1" ]; then index=0 else index="$1" fi echo "$attrs" | jq -r -M --argjson i "$index" '.[$i] | .[]' } get_attr_index_raw() { if [ -z "$1" ]; then index=0 else index="$1" fi echo "$attrs" | jq -c -M --argjson i "$index" '.[$i]' } read_config() { file="$1" config=$(jq -c -M '.' "$file") if [ $? -eq 0 ] && has_config_key mdev_type && has_config_key start; then attrs=$(echo "$config" | jq -c -M '.attrs') if [ "$attrs" == null ]; then attrs=[] fi config=$(echo "$config" | jq -c -M 'del(.attrs)') return 0 else config={} attrs=[] return 1 fi } dump_config() { echo "$config" | jq -M --argjson attrs "{\"attrs\":$attrs}" '. + $attrs' } write_config() { file="$1" dump_config > "$file" } # get the absolute path in sysfs of the parent device for the mdev, strip the # leading slash, append the uuid, then systemd-escape it get_systemd_name() { uuid="$1" parent="$2" name="$(realpath "$parent_base/$parent" | sed -e 's/^\///')/$uuid" echo $(systemd-escape "$name") } valid_uuid () { uuid="$1" if [[ "$uuid" =~ ^\{?[A-F0-9a-f]{8}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{12}\}?$ ]]; then echo "$uuid" fi } create_mdev() { uuid="$1" parent="$2" type="$3" if [ -z "$(valid_uuid $uuid)" ]; then echo "Invalid UUID $uuid" >&2 return 1 fi if [ -L "$mdev_base/$uuid" ]; then cur_parent=$(basename $(realpath "$mdev_base/$uuid" | sed -s "s/\/$uuid//")) if [ $? -ne 0 ] || [ "$cur_parent" != "$parent" ]; then echo "Device exists under different parent" >&2 return 1 fi cur_type=$(basename $(realpath "$mdev_base/$uuid/mdev_type")) if [ $? -ne 0 ] || [ "$cur_type" != "$type" ]; then echo "Device exists with different type" >&2 return 1 fi return 1 fi if [ ! -d "$parent_base/$parent/mdev_supported_types" ]; then echo "Parent $parent is not currently registered for mdev support" >&2 return 1 fi if [ ! -d "$parent_base/$parent/mdev_supported_types/$type" ]; then echo "Parent $parent does not support mdev type $type" >&2 return 1 fi avail=$(cat "$parent_base/$parent/mdev_supported_types/$type/available_instances") if [ $? -ne 0 ] || [ "$avail" -eq 0 ]; then echo "No available instances of $type on $parent" >&2 return 1 fi echo "$uuid" > "$parent_base/$parent/mdev_supported_types/$type/create" if [ $? -ne 0 ]; then echo "Error creating mdev type $type on $parent" >&2 return 1 fi return 0 } start_mdev() { uuid="$1" parent="$2" type="$3" if [ $# -eq 4 ]; then print_uuid="$4" fi create_mdev "$uuid" "$parent" "$type" if [ $? -eq 0 ]; then count=$(( $(get_attr_length) - 1 )) if [ "$count" -ge 0 ]; then for i in $(seq 0 "$count"); do attr=$(get_attr_index_key $i) if [ ! -w "$mdev_base/$uuid/$attr" ]; then echo "Attribute $attr cannot be set" >&2 remove_mdev "$uuid" return 1 fi val=$(get_attr_index_value $i) echo -e "$val" > "$mdev_base/$uuid/$attr" if [ $? -ne 0 ]; then echo "Failed to write $val to attribute $attr" >&2 remove_mdev "$uuid" return 1 fi done fi $print_uuid return 0 fi return 1 } remove_mdev() { uuid="$1" if [ -z "$(valid_uuid $uuid)" ]; then echo "Invalid UUID $uuid" >&2 return 1 fi if [ ! -L "$mdev_base/$uuid" ]; then return 1 fi echo 1 > "$mdev_base/$uuid/remove" if [ $? -ne 0 ]; then echo "Error removing device $uuid" >&2 return 1 fi return 0 } # Get a UUID that's not locally defined or running unique_uuid() { count=1 while [ $count -ne 0 ]; do uuid=$(uuidgen) count=$(find "$persist_base" -name "$uuid" -type f | wc -l) if [ "$count" -eq 0 ] && [ -L "$mdev_base/$uuid" ]; then count=1 fi done echo "$uuid" } usage() { cat >&2 <<EOF Usage: $(basename $0) {COMMAND} [options...] Available commands: define Define a config for an mdev device. Options: <-u|--uuid=UUID> [-a|--auto] [-u|--uuid=UUID] <-p|--parent=PARENT> <-t|--type=TYPE> [-a|--auto] [-u|--uuid=UUID] <-p|--parent=PARENT> <--jsonfile=FILE> If the device specified by the UUID currently exists, parent and type may be omitted to use the existing values. The auto option marks the device to start on parent availability. If defined via FILE then type, startup, and any attributes are provided via the file. Running devices are unaffected by this command. undefine Undefine, or remove a config for an mdev device. Options: <-u|--uuid=UUID> [-p|--parent=PARENT] If a UUID exists for multiple parents, all will be removed unless a parent is specified. Running devices are unaffected by this command. modify Modify the config for a defined mdev device. Options: <-u|--uuid=UUID> [-p|--parent=PARENT] [-t|--type=TYPE] \\ [--addattr=ATTRIBUTE] [--delattr] [-i|--index=INDEX] [--value=VALUE] \\ [-a|--auto|-m|--manual] The parent option further identifies a UUID if it is not unique, the parent for a device cannot be modified via this command, undefine and re-define should be used instead. An ATTRIBUTE can be added or removed, which correlates to a sysfs attribute under the created device. Unless an INDEX value is provided, operations are performed at the end of the attribute list. VALUE is to be specified in the format that is accepted by the attribute. Upon device start, mdevctl will go through each attribute in order, writing the value into the corresponding sysfs attribute for the device. The startup mode of the device can also be selected, auto or manual. Running devices are unaffected by this command. start Start an mdev device. Options: <-u|--uuid=UUID> [-p|--parent=PARENT] [-u|--uuid=UUID] <-p|--parent=PARENT> <-t|--type=TYPE> [-u|--uuid=UUID] <-p|--parent=PARENT> <--jsonfile=FILE> If the UUID is previously defined and unique, the UUID is sufficient to start the device (UUIDs may not collide between running devices). If a UUID is used in multiple defined configs, the parent device is necessary to identify the config. When specified with PARENT and TYPE, the device is fully specified and will be started based only on these parameters. The UUID is optional in this case, if not provided a UUID is generated and returned as output. A FILE may replace the TYPE specification and also include additional attributes to be applied to the started device. stop Stop an mdev device. Options: <-u|--uuid=UUID> list List mdev devices. Options: [-d|--defined] [-u|--uuid=UUID] [-p|--parent=PARENT] \\ [--dumpjson] [-v|--verbose] With no options, information about the currently running mdev devices is provided. Specifying DEFINED lists the configuration of defined devices, regardless of their running state. This may be further reduced by specifying specific UUID or PARENT devices to list. The dumpjson option provides output listing in machine readable JSON format. When a UUID option is provided and the result is a single device, the output contains only the JSON fields necessary to recreate a config file for the device (minus attributes for listings of running devices). When the verbose option is provided, the human readable listing will include attributes for the device(s). types List mdev types. Options: [-p|--parent=PARENT] [--dumpjson] Specifying a PARENT lists only the types provided by the given parent device. The dumpjson option provides output in machine readable JSON format. EOF exit 1 } if [ $# -lt 1 ]; then usage fi case ${1} in # # Internal commands, these are expected to be called from udev or systemd # and therefore do not offer the convenience of getopt processing # (they typically take one arg after the command) and are not listed in # the usage text # start-systemd-mdev) if [ $# -ne 2 ]; then echo "Usage: $0 $1 <systemd mdev device path>" >&2 exit 1 fi uuid=$(basename $(systemd-escape -u "$2")) parent=$(basename $(systemd-escape -u "$2" | sed -s "s/\/$uuid//")) if [ -L "$mdev_base/$uuid" ]; then exit 0 # mdev is already running, done elif [ -e "$persist_base/$parent/$uuid" ]; then read_config "$persist_base/$parent/$uuid" if [ $? -ne 0 ]; then echo "Invalid config for device $uuid" >&2 exit 1 fi type="$(get_config_key mdev_type)" else echo "Device $uuid is not defined, cannot start" >&2 exit 1 fi create_mdev "$uuid" "$parent" "$type" exit $? ;; stop-systemd-mdev) if [ $# -ne 2 ]; then echo "Usage: $0 $1 <systemd mdev device path>" >&2 exit 1 fi path="/" path+=$(systemd-escape -u "$2") if [ ! -d "$path" ]; then exit 0 fi echo 1 > "$path/remove" exit $? ;; systemd-service-name) if [ $# -ne 2 ]; then echo "Usage: $0 $1 <running mdev uuid>" >&2 exit 1 fi pidof systemd > /dev/null if [ $? -ne 0 ]; then exit 1 # Not systemd host fi uuid="$2" if [ ! -L "$mdev_base/$uuid" ]; then echo "Device $uuid is not running" >&2 exit 1 fi parent=$(basename $(realpath "$mdev_base/$uuid" | sed -s "s/\/$uuid//")) name=$(get_systemd_name "$uuid" "$parent") if [ -z "$name" ]; then exit 1 fi echo "mdev@$name.service" exit 0 ;; start-parent-mdevs) if [ $# -ne 2 ]; then echo "Usage: $0 $1 <parent device>" >&2 exit 1 fi parent="$2" if [ ! -d "$persist_base/$parent" ]; then # Nothing to do exit 0 fi for file in $(find "$persist_base/$parent/" -maxdepth 1 -mindepth 1 -type f); do uuid=$(basename "$file") if [ -n "$(valid_uuid $uuid)" ]; then read_config "$file" if [ $? -ne 0 ]; then continue fi if [ "$(get_config_key start)" == "auto" ]; then create_mdev "$uuid" "$parent" "$(get_config_key mdev_type)" if [ $? -ne 0 ]; then echo "Failed to create mdev $uuid, type $(get_config_key mdev_type) on $parent" >&2 # continue... fi fi fi done exit 0 ;; # # User commands # --help|-h|-?) usage ;; define) cmd="$1" OPTIONS="u:p:t:a" LONGOPTS="uuid:,parent:,type:,auto,jsonfile:" shift ;; undefine) cmd="$1" OPTIONS="u:p:" LONGOPTS="uuid:,parent:" shift ;; modify) cmd="$1" OPTIONS="u:p:t:ami:" LONGOPTS="uuid:,parent:,type:,auto,manual,addattr:,delattr,index:,value:" shift ;; start) cmd="$1" OPTIONS="u:p:t:" LONGOPTS="uuid:,parent:,type:,jsonfile:" shift ;; stop) cmd="$1" OPTIONS="u:" LONGOPTS="uuid:" shift ;; list) cmd="$1" OPTIONS="du:p:v" LONGOPTS="defined,uuid:,dumpjson,parent:,verbose" shift ;; types) cmd="$1" OPTIONS="p:" LONGOPTS="parent:,dumpjson" shift ;; *) echo "Unknown command $1" >&2 usage ;; esac PARSED=$(getopt --options="$OPTIONS" --longoptions="$LONGOPTS" --name "$(basename $0)" -- "$@") if [ $? -ne 0 ]; then exit 1 fi eval set -- "$PARSED" while true; do case "$1" in -u|--uuid) uuid="$2" shift 2 ;; -p|--parent) parent="$2" shift 2 ;; -t|--type) type="$2" shift 2 ;; --jsonfile) jsonfile="$2" shift 2 ;; --addattr) addattr="$2" shift 2 ;; -i|--index) index="$2" shift 2 ;; --value) value="$2" shift 2 ;; --delattr) delattr=y shift 1 ;; --dumpjson) dumpjson=y shift ;; -a|--auto) auto=y shift 1 ;; -m|--manual) manual=y shift 1 ;; -d|--defined) defined=y shift 1 ;; -a|--available) available=y shift 1 ;; -v|--verbose) verbose=y shift 1 ;; --) shift break ;; *) echo "Programming error" exit 1 ;; esac done if [ $# -ne 0 ]; then echo "$(basename $0): Unknown options: $@" exit 1 fi case "$cmd" in define) if [ -n "$jsonfile" ]; then if [ ! -r "$jsonfile" ]; then echo "Unable to read file $jsonfile" >&2 exit 1 fi if [ -n "$type" ]; then echo "Device type cannot be specified separately from $jsonfile" >&2 exit 1 fi if [ -z "$parent" ]; then echo "Parent device required to define device via $jsonfile" >&2 exit 1 fi if [ -z "$uuid" ]; then uuid=$(unique_uuid) print_uuid="echo $uuid" fi if [ -e "$persist_base/$parent/$uuid" ]; then echo "Cowardly refusing to overwrite existing config for $parent/$uuid" >&2 exit 1 fi set -o errexit read_config "$jsonfile" if [ $? -ne 0 ]; then echo "Error reading $jsonfile" >&2 exit 1 fi write_config "$persist_base/$parent/$uuid" if [ $? -ne 0 ]; then exit 1 fi $print_uuid exit 0 fi if [ -n "$uuid" ]; then if [ -z "$parent" ]; then if [ ! -L "$mdev_base/$uuid" ] || [ -n "$type" ]; then usage fi parent=$(basename $(realpath "$mdev_base/$uuid" | sed -s "s/\/$uuid//")) type=$(basename $(realpath "$mdev_base/$uuid/mdev_type")) fi if [ -e "$persist_base/$parent/$uuid" ]; then echo "Device $uuid on $parent already defined, try modify?" >&2 exit 1 fi else uuid=$(unique_uuid) print_uuid="echo $uuid" fi if [ -z "$parent" ]; then usage fi if [ -z "$type" ]; then usage fi if [ -n "$auto" ]; then start="auto" else start="manual" fi set -o errexit mkdir -p "$persist_base/$parent" set_config_key mdev_type "$type" set_config_key start "$start" write_config "$persist_base/$parent/$uuid" if [ $? -eq 0 ]; then $print_uuid fi ;; undefine) if [ -z "$uuid" ]; then usage fi set -o errexit if [ -n "$parent" ]; then rm -f "$persist_base/$parent/$uuid" else find "$persist_base" -name "$uuid" -type f | xargs rm -f fi ;; modify) if [ -z "$uuid" ]; then usage fi if [ -n "$auto" ] && [ -n "$manual" ]; then echo "Options --auto and --manual are mutually exclusive" >&2 exit 1 fi set -o errexit file=$(config_file "$uuid" "$parent") if [ $? -ne 0 ]; then exit 1 fi read_config "$file" if [ $? -ne 0 ]; then echo "Config file $file invalid" >&2 exit 1 fi if [ -n "$type" ]; then set_config_key mdev_type "$type" fi if [ -n "$auto" ]; then set_config_key start auto fi if [ -n "$manual" ]; then set_config_key start manual fi if [ -n "$addattr" ] && [ -n "$delattr" ]; then usage fi if [ -n "$addattr" ]; then if [ -n "$index" ]; then if [ "$index" -eq "$index" ] 2>/dev/null; then : else echo "Provided index is not a number" >&2 usage fi fi if [ -z "$value" ]; then echo "No attribute value provided" >&2 usage fi add_attr_index "$addattr" "$value" "$index" fi if [ -n "$delattr" ]; then if [ -n "$index" ]; then if [ "$index" -eq "$index" ] 2>/dev/null; then : else echo "Provided index is not a number" >&2 usage fi fi del_attr_index "$index" fi write_config "$file" ;; start) set -o errexit if [ -n "$jsonfile" ]; then if [ ! -r "$jsonfile" ]; then echo "Unable to read file $jsonfile" >&2 exit 1 fi if [ -n "$type" ]; then echo "Device type cannot be specified separately from $jsonfile" >&2 exit 1 fi if [ -z "$parent" ]; then echo "Parent device required to start device via $jsonfile" >&2 exit 1 fi if [ -z "$uuid" ]; then uuid=$(unique_uuid) print_uuid="echo $uuid" fi read_config "$jsonfile" if [ $? -ne 0 ]; then echo "Error reading $jsonfile" >&2 exit 1 fi type="$(get_config_key mdev_type)" start_mdev "$uuid" "$parent" "$type" "$print_uuid" exit $? fi # We don't implement a placement policy if [ -n "$type" ] && [ -z "$parent" ]; then usage fi # The device is not fully specified without TYPE, we must find # a config file, with optional PARENT for disambiguation if [ -z "$type" ] && [ -n "$uuid" ]; then count=$(find "$persist_base" -name "$uuid" -type f | wc -l) if [ "$count" -eq 0 ]; then echo "Config for $uuid does not exist, define it first?" >&2 exit 1 elif [ "$count" -gt 1 ]; then if [ -z "$parent" ] || [ ! -e "$persist_base/$parent/$uuid" ]; then echo "Multiple configs found for $uuid, specify a parent" >&2 exit 1 fi file="$persist_base/$parent/$uuid" else file=$(find "$persist_base" -name "$uuid" -type f) if [ -n "$parent" ]; then cur_parent=$(basename $(echo "$file" | sed -s "s/\/$uuid//")) if [ "$cur_parent" != "$parent" ]; then echo "Config for $parent/$uuid does not exist, define it first?" >&2 exit 1 fi fi fi read_config "$file" if [ $? -ne 0 ]; then echo "Config file $file invalid" >&2 exit 1 fi if [ -z "$parent" ]; then parent=$(basename $(echo "$file" | sed -s "s/\/$uuid//")) fi type="$(get_config_key mdev_type)" fi if [ -z "$uuid" ]; then # Device must be full specified otherwise for generated UUID if [ -z "$parent" ] || [ -z "$type" ]; then echo "Device is insufficiently specified" >&2 usage fi uuid=$(unique_uuid) print_uuid="echo $uuid" fi start_mdev "$uuid" "$parent" "$type" "$print_uuid" exit $? ;; stop) if [ -z "$uuid" ]; then usage fi set -o errexit remove_mdev "$uuid" ;; list) json="[]" txt="" if [ -n "$defined" ]; then for dir in $(find "$persist_base/" -maxdepth 1 -mindepth 1 -type d | sort); do p=$(basename "$dir") if [ -n "$parent" ] && [ "$parent" != "$p" ]; then continue fi for mdev in $(find "$dir/" -maxdepth 1 -mindepth 1 -type f); do u=$(basename "$mdev") if [ -n "$uuid" ] && [ "$uuid" != "$u" ]; then continue fi read_config "$mdev" if [ $? -ne 0 ]; then continue fi type="$(get_config_key mdev_type)" start="$(get_config_key start)" txt+="$u $p $type $start" if [ -L "$mdev_base/$u" ]; then cur_parent=$(basename $(realpath "$mdev_base/$u" | sed -s "s/\/$u//")) if [ "$cur_parent" == "$p" ]; then cur_type=$(basename $(realpath "$mdev_base/$u/mdev_type")) if [ "$cur_type" == "$type" ]; then txt+=" (active)" fi fi fi json_tmp="{\"$p\":[{\"$u\":{"\"mdev_type\":\"$type\"",\"start\":\"$start\"" txt+="\n" if [ -n "$verbose" ] || [ -n "$dumpjson" ]; then count=$(( $(get_attr_length) - 1 )) if [ $count -ge 0 ]; then json_tmp+=",\"attrs\":$(get_attrs_raw)" txt+=" Attrs:\n" for i in $(seq 0 "$count"); do txt+=" @{$i}: $(get_attr_index_raw $i)\n" done fi fi json_tmp+="}}]}" json=$(echo "$json" | jq -c -M --argjson obj "$json_tmp" '. + [$obj]') done done else if [ ! -d "$mdev_base" ]; then exit 0 fi for mdev in $(find "$mdev_base/" -maxdepth 1 -mindepth 1 -type l); do u=$(basename "$mdev") if [ -n "$uuid" ] && [ "$uuid" != "$u" ]; then continue fi p=$(basename $(realpath "$mdev_base/$u" | sed -s "s/\/$u//")) if [ -n "$parent" ] && [ "$parent" != "$p" ]; then continue fi type=$(basename $(realpath "$mdev/mdev_type")) json_tmp="{\"$p\":[{\"$u\":{\"mdev_type\":\"$type\"}}]}" txt+="$u $p $type" if [ -f "$persist_base/$p/$u" ]; then read_config "$persist_base/$p/$u" if [ $? -eq 0 ] && [ "$(get_config_key mdev_type)" == "$type" ]; then txt+=" (defined)" fi fi txt+="\n" json=$(echo "$json" | jq -c -M --argjson obj "$json_tmp" '. + [$obj]') done fi if [ -n "$dumpjson" ]; then if [ $(echo "$json" | jq 'length') -gt 0 ]; then # https://stackoverflow.com/a/43337323/4775714 json=$(echo "$json" | jq -c -M '[reduce .[] as $o ({}; reduce ($o|keys)[] as $key (.; .[$key] += $o[$key] ))]') fi # If specified to a single device, output such that it can be # piped into a config file, else print entire hierarchy if [ -n "$uuid" ] && [ $(echo "$json" | jq -M 'length') -eq 1 ] && [ $(echo "$json" | jq -M '.[] | length') -eq 1 ] && [ $(echo "$json" | jq -M '.[] | .[] | length') -eq 1 ]; then key=$(echo "$json" | jq -r -M '.[] | .[] | .[] | keys | .[]') key=$(jsonify $key) echo "$json" | jq -M --argjson key "$key" '.[] | .[] | .[] | .[$key]' else echo "$json" | jq -M '.' fi else echo -en "$txt" fi ;; types) if [ ! -d "$parent_base" ]; then if [ -n "$dumpjson" ]; then echo "[]" | jq -M '.' fi exit 0 fi json="[]" txt="" for dir in $(find "$parent_base/" -maxdepth 1 -mindepth 1 -type l | sort); do p=$(basename "$dir") if [ -n "$parent" ] && [ "$parent" != "$p" ]; then continue fi txt+="$p\n" for parent_type in $(find "$dir/mdev_supported_types/" -maxdepth 1 -mindepth 1 -type d | sort); do type=$(basename "$parent_type") txt+=" $type\n" avail=$(cat "$parent_type/available_instances") txt+=" Available instances: $avail\n" api=$(cat "$parent_type/device_api") txt+=" Device API: $api\n" json_tmp="{\"$p\":[{\"$type\":{\"available_instances\":$avail,\"device_api\":\"$api\"" if [ -e "$parent_type/name" ]; then name=$(cat "$parent_type/name") json_tmp+=",\"name\":\"$name\"" txt+=" Name: $name\n" fi if [ -e "$parent_type/description" ]; then descr=$(cat "$parent_type/description" | sed -e ':a;N;$!ba;s/\n/, /g') json_tmp+=",\"description\":\"$descr\"" txt+=" Description: $descr\n" fi json_tmp+="}}]}" json=$(echo "$json" | jq -c -M --argjson obj "$json_tmp" '. + [$obj]') done done if [ -n "$dumpjson" ]; then if [ $(echo "$json" | jq 'length') -gt 0 ]; then # https://stackoverflow.com/a/43337323/4775714 json=$(echo "$json" | jq -c -M '[reduce .[] as $o ({}; reduce ($o|keys)[] as $key (.; .[$key] += $o[$key] ))]') fi echo "$json" | jq -M '.' else echo -en "$txt" fi ;; esac