|
| 1 | +# shellcheck shell=ash |
| 2 | +# Copyright (c) 2017 Markus Weippert |
| 3 | +# Copyright (c) 2026, Alexei Znamensky |
| 4 | +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) |
| 5 | +# SPDX-License-Identifier: GPL-3.0-or-later |
| 6 | + |
| 7 | +# Core shell framework for community.openwrt modules. |
| 8 | +# Sourced by wrapper.sh on every module execution. |
| 9 | +# Provides: |
| 10 | +# parameter parsing (JSON and legacy) |
| 11 | +# exit/result handling |
| 12 | +# check mode support |
| 13 | +# diff support |
| 14 | +# control flow helpers (changed, fail, succeed, try, final) |
| 15 | + |
| 16 | +_ANSIBLE_PARAMS=" |
| 17 | + _ansible_version/s |
| 18 | + _ansible_no_log/b |
| 19 | + _ansible_module_name/s |
| 20 | + _ansible_syslog_facility/s |
| 21 | + _ansible_socket/s |
| 22 | + _ansible_verbosity/i |
| 23 | + _ansible_diff/b |
| 24 | + _ansible_debug/b |
| 25 | + _ansible_check_mode/b |
| 26 | +" |
| 27 | +CHANGED="" |
| 28 | +FACT_VARS="" |
| 29 | +JSON_PREFIX="" |
| 30 | +MESSAGE="" |
| 31 | +NO_EXIT_JSON="" |
| 32 | +PARAMS="" |
| 33 | +RESPONSE_VARS="" |
| 34 | +SKIPPED="" |
| 35 | +SUPPORTS_CHECK_MODE="1" |
| 36 | + |
| 37 | +N=" |
| 38 | +" |
| 39 | +T=" " |
| 40 | + |
| 41 | +init() { :; } |
| 42 | +validate() { :; } |
| 43 | +main() { :; } |
| 44 | +cleanup() { :; } |
| 45 | + |
| 46 | +_exit_add_vars() { |
| 47 | + local _var _name _type _always _value _IFS |
| 48 | + for _var; do |
| 49 | + _IFS="$IFS"; IFS="/"; set -- $_var; IFS="$_IFS" |
| 50 | + _var="$1"; _type="$(_map_type "$2")"; _always="$3" |
| 51 | + _IFS="$IFS"; IFS="="; set -- $_var; IFS="$_IFS" |
| 52 | + _name="$1"; _var="${2:-$_name}" |
| 53 | + eval "_value=\"\$$_var\"" |
| 54 | + [ -z "$_value" -a -z "$_always" ] || |
| 55 | + eval "json_add_$_type \"\$_name\" \"\$_value\"" |
| 56 | + done |
| 57 | +} |
| 58 | + |
| 59 | +_init_done="" |
| 60 | +_exit() { |
| 61 | + local _rc="$?" |
| 62 | + local _v _k |
| 63 | + [ -z "$_init_done" ] || cleanup "$_rc" || : |
| 64 | + [ -z "$NO_EXIT_JSON" ] || return $_rc |
| 65 | + json_set_namespace result |
| 66 | + json_add_boolean changed $([ -z "$CHANGED" ]; echo $?) |
| 67 | + json_add_boolean failed $([ $_rc -eq 0 ]; echo $?) |
| 68 | + [ -z "$SKIPPED" ] || json_add_boolean skipped 1 |
| 69 | + [ -z "$MESSAGE" ] || json_add_string msg "$MESSAGE" |
| 70 | + [ -z "$_init_done" ] || { |
| 71 | + [ -z "$RESPONSE_VARS" ] || _exit_add_vars $RESPONSE_VARS |
| 72 | + [ -z "$FACT_VARS" ] || { |
| 73 | + json_add_object ansible_facts |
| 74 | + _exit_add_vars "$FACT_VARS" |
| 75 | + json_close_object |
| 76 | + } |
| 77 | + } |
| 78 | + [ -z "$_ansible_diff" -o -z "$_diff_set" ] || { |
| 79 | + json_add_object diff |
| 80 | + json_add_string before "$_diff_before${_diff_before:+$N}" |
| 81 | + json_add_string after "$_diff_after${_diff_after:+$N}" |
| 82 | + [ -z "$_diff_before_header" ] || |
| 83 | + json_add_string before_header "$_diff_before_header" |
| 84 | + [ -z "$_diff_after_header" ] || |
| 85 | + json_add_string after_header "$_diff_after_header" |
| 86 | + json_close_object |
| 87 | + } |
| 88 | + echo; json_dump |
| 89 | + json_cleanup |
| 90 | + return $_rc |
| 91 | +} |
| 92 | +trap _exit EXIT |
| 93 | + |
| 94 | +_map_type() { |
| 95 | + [ -n "$1" ] || { echo "string"; return 0; } |
| 96 | + case "$1" in |
| 97 | + any) echo "any";; |
| 98 | + s|str|string) echo "string";; |
| 99 | + i|int|integer) echo "int";; |
| 100 | + b|bool|boolean) echo "boolean";; |
| 101 | + f|d|float|double) echo "double";; |
| 102 | + l|a|list|array) echo "array";; |
| 103 | + o|h|obj|object|hash|map) echo "object";; |
| 104 | + *) fail "unknown type: $1";; |
| 105 | + esac |
| 106 | +} |
| 107 | + |
| 108 | +_verify_value_type() { |
| 109 | + local _value="$1" |
| 110 | + local _type="$2" |
| 111 | + case "$_type" in |
| 112 | + int) printf "%d" "$_value" >/dev/null 2>&1 || return 1;; |
| 113 | + double) printf "%f" "$_value" >/dev/null 2>&1 || return 1;; |
| 114 | + boolean) |
| 115 | + case "$_value" in |
| 116 | + yes|true|True|1) _value="1";; |
| 117 | + no|false|False|0) _value="";; |
| 118 | + *) return 1;; |
| 119 | + esac;; |
| 120 | + esac |
| 121 | + echo "$_value" |
| 122 | +} |
| 123 | + |
| 124 | +_parse_legacy_params() { |
| 125 | + local _var _type _required _default _alias |
| 126 | + local _param _value _IFS |
| 127 | + for _param in $PARAMS; do |
| 128 | + eval "${_param%%[/=]*}=\"\"" |
| 129 | + done |
| 130 | + eval "$(cat "$_params")" || fail "could not parse params" |
| 131 | + for _param in $PARAMS $_ANSIBLE_PARAMS; do |
| 132 | + _value=""; _IFS="$IFS"; IFS="/"; set -- $_param; IFS="$_IFS" |
| 133 | + _var="$1"; _type="$(_map_type "$2")"; _required="$3"; _default="$4" |
| 134 | + _IFS="$IFS"; IFS="="; set -- $_var; IFS="$_IFS"; _var="$1" |
| 135 | + for _alias; do |
| 136 | + eval "_value=\"\$$_alias\"" |
| 137 | + [ -z "$_value" ] || break |
| 138 | + done |
| 139 | + eval "export -- _orig_$_var=\"\$_value\"" |
| 140 | + [ -z "$_value" ] && { |
| 141 | + [ -z "$_required" ] || fail "$_var is required" |
| 142 | + [ -z "$_default" ] || |
| 143 | + _value="$(_verify_value_type "$_default" "$_type")" |
| 144 | + } || { |
| 145 | + _value="$(_verify_value_type "$_value" "$_type")" || |
| 146 | + fail "$_var must be $_type" |
| 147 | + } |
| 148 | + eval "export -- $_var=\"\$_value\" _type_$_var=\"\$_type\"" |
| 149 | + done |
| 150 | + return 0 |
| 151 | +} |
| 152 | + |
| 153 | +_parse_json_params() { |
| 154 | + local _var _type _required _default _alias |
| 155 | + local _param _value _found _is_type _IFS |
| 156 | + json_set_namespace params |
| 157 | + json_load "$(cat -- "$_params")" || fail "could not parse params" |
| 158 | + for _param in $PARAMS $_ANSIBLE_PARAMS; do |
| 159 | + _value=""; _IFS="$IFS"; IFS="/"; set -- $_param; IFS="$_IFS" |
| 160 | + _var="$1"; _type="$(_map_type "$2")"; _required="$3"; _default="$4" |
| 161 | + _IFS="$IFS"; IFS="="; set -- $_var; IFS="$_IFS"; _var="$1" |
| 162 | + _is_type=""; _found="" |
| 163 | + for _alias; do |
| 164 | + json_get_type _is_type "$_alias" && |
| 165 | + json_get_var _value "$_alias" || |
| 166 | + continue |
| 167 | + _found="1"; break |
| 168 | + done |
| 169 | + eval "export -- _orig_$_var=\"\$_value\" _defined_$_var=\"$_found\"" |
| 170 | + [ -n "$_found" ] || { |
| 171 | + [ -z "$_required" ] || fail "$_var is required" |
| 172 | + [ -z "$_default" ] || |
| 173 | + _value="$(_verify_value_type "$_default" "$_type")" |
| 174 | + } |
| 175 | + case "$_is_type" in |
| 176 | + array|object) |
| 177 | + [ "$_type" = "$_is_type" -o "$_type" = "any" ] || |
| 178 | + fail "$_var must be $_type" |
| 179 | + eval "export -- _type_${_var}=\"\$_is_type\"" |
| 180 | + json_add_string "_$_var" "$_alias";; |
| 181 | + *) |
| 182 | + [ -z "$_found" ] || |
| 183 | + _value="$(_verify_value_type "$_value" "$_type")" || |
| 184 | + fail "$_var must be $_type" |
| 185 | + eval "export -- $_var=\"\$_value\" _type_$_var=\"\$_type\"" |
| 186 | + json_add_string "$_var" "$_value";; |
| 187 | + esac |
| 188 | + done |
| 189 | + return 0 |
| 190 | +} |
| 191 | + |
| 192 | +_parse_params() { |
| 193 | + [ -n "$WANT_JSON" ] && _parse_json_params || _parse_legacy_params |
| 194 | +} |
| 195 | + |
| 196 | +_support_check_mode() { |
| 197 | + [ -n "$_ansible_check_mode" -a -z "$SUPPORTS_CHECK_MODE" ] || return 0 |
| 198 | + SKIPPED="1" |
| 199 | + MESSAGE="module does not support check mode" |
| 200 | + exit 0 |
| 201 | +} |
| 202 | + |
| 203 | +json_select_real() { |
| 204 | + local real_var |
| 205 | + json_get_var real_var "_$1" |
| 206 | + json_select "$real_var" |
| 207 | +} |
| 208 | + |
| 209 | +changed() { |
| 210 | + CHANGED="1" |
| 211 | +} |
| 212 | + |
| 213 | +unchanged() { |
| 214 | + CHANGED="" |
| 215 | +} |
| 216 | + |
| 217 | +succeed() { |
| 218 | + [ -n "$*" ] && MESSAGE="$*" |
| 219 | + exit 0 |
| 220 | +} |
| 221 | + |
| 222 | +fail() { |
| 223 | + MESSAGE="$*" |
| 224 | + exit 1 |
| 225 | +} |
| 226 | + |
| 227 | +_result="" |
| 228 | +try() { |
| 229 | + [ $# -eq 1 ] && { |
| 230 | + _result="$(eval "$1" 2>&1)" || fail "$*: $_result" |
| 231 | + } || { |
| 232 | + _result="$("$@" 2>&1)" || fail "$*: $_result" |
| 233 | + } |
| 234 | +} |
| 235 | + |
| 236 | +final() { |
| 237 | + try "$@" |
| 238 | + exit 0 |
| 239 | +} |
| 240 | + |
| 241 | +_diff_set="" |
| 242 | +_diff_before="" |
| 243 | +_diff_after="" |
| 244 | +_diff_before_header="" |
| 245 | +_diff_after_header="" |
| 246 | +set_diff() { |
| 247 | + _diff_before="${1:-$_diff_before}" |
| 248 | + _diff_after="${2:-$_diff_after}" |
| 249 | + _diff_before_header="${3:-$_diff_before_header}" |
| 250 | + _diff_after_header="${4:-$_diff_after_header}" |
| 251 | + _diff_set="1" |
| 252 | +} |
0 commit comments