|
| 1 | +#!/bin/bash |
| 2 | +############################################################################ |
| 3 | +# This file is part of Grenache Command Line Interface. # |
| 4 | +# # |
| 5 | +# Copyright (C) 2017-2025 Davide Scola <davide@bitfinex.com> # |
| 6 | +# # |
| 7 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may # |
| 8 | +# not use this file except in compliance with the License. You may obtain # |
| 9 | +# a copy of the License at # |
| 10 | +# # |
| 11 | +# http://www.apache.org/licenses/LICENSE-2.0 # |
| 12 | +# # |
| 13 | +# Unless required by applicable law or agreed to in writing, software # |
| 14 | +# distributed under the License is distributed on an "AS IS" BASIS, # |
| 15 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # |
| 16 | +# implied. See the License for the specific language governing permissions # |
| 17 | +# and limitations under the License. # |
| 18 | +############################################################################ |
| 19 | + |
| 20 | +readonly ME="${BASH_SOURCE[0]}" |
| 21 | +readonly WHOAMI="$(@READLINK@ -snf "${ME}")" |
| 22 | +readonly ARGV=$(@GETOPT@ -o 'a:cg:hn:p:q:rs:tVw:' --long 'arg:,color,grape:,help,numeric:,port:,query:,raw,string:,tls,version,worker:' -n "${ME##*/}" -- "$@") || exit 1 |
| 23 | + |
| 24 | +TLS='' |
| 25 | +COLOR='' |
| 26 | +EXIT_CODE=1 |
| 27 | +REQUEST_ARGC=0 |
| 28 | +RESPONSE_ARGV=() |
| 29 | +REQUEST_WORKER='' |
| 30 | +REQUEST_QUERY='.' |
| 31 | +RESPONSE_QUERY='.' |
| 32 | +REQUEST_ARGUMENTS='[]' |
| 33 | +LOOKUP_ARGV=(--random) |
| 34 | +REQUEST_ARGV=(--compact-output --monochrome-output) |
| 35 | + |
| 36 | + |
| 37 | +# Show program's help. |
| 38 | +function show_help { |
| 39 | + @CAT@ <<_EOF |
| 40 | +${ME##*/} sends a request to a service in the DHT |
| 41 | +
|
| 42 | +Usage: ${ME##*/} [OPTION]... SERVICE ACTION |
| 43 | +
|
| 44 | +Options: |
| 45 | + -a, --arg Add an argument, JSON encoded |
| 46 | + -c, --color Pretty print |
| 47 | + -g, --grape Set the Grape hostname |
| 48 | + -n, --numeric Add a numeric argument |
| 49 | + -p, --port Set the Grape port number |
| 50 | + -q, --query Filter response against query |
| 51 | + -r, --raw Print string output as-is |
| 52 | + -s, --string Add a string argument |
| 53 | + -t, --tls Enable TLS |
| 54 | + -w, --worker Don't lookup, use this worker |
| 55 | +
|
| 56 | + -h, --help Show help message |
| 57 | + -V, --version Show version information |
| 58 | +
|
| 59 | +The \`--arg', \`--numeric' and \`--string' options can be repeated several times |
| 60 | +to add multiple arguments, which will be passed in the same order as they were |
| 61 | +supplied on the command line. |
| 62 | +
|
| 63 | +Examples: |
| 64 | + ${ME##*/} 'foo' 'bar' Call the bar action of the foo service |
| 65 | + ${ME##*/} -n 1 'foo' 'bar' The same thing, but passing a numeric argument |
| 66 | +
|
| 67 | +Report bugs to <@PACKAGE_BUGREPORT@>. |
| 68 | +_EOF |
| 69 | + |
| 70 | + exit 1 |
| 71 | +} |
| 72 | + |
| 73 | +# Show program's version. |
| 74 | +function show_version { |
| 75 | + @CAT@ <<_EOF |
| 76 | +${WHOAMI##*/} @PACKAGE_VERSION@ |
| 77 | +
|
| 78 | +Copyright (C) 2017-2025 Davide Scola <@PACKAGE_BUGREPORT@> |
| 79 | +This is free software; see the source for copying conditions. There is |
| 80 | +NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR |
| 81 | +PURPOSE. |
| 82 | +
|
| 83 | +Written by Davide Scola. |
| 84 | +_EOF |
| 85 | + |
| 86 | + exit 1 |
| 87 | +} |
| 88 | + |
| 89 | + |
| 90 | +while true; do |
| 91 | + case "$1" in |
| 92 | + -a | --arg ) |
| 93 | + REQUEST_ARGV+=(--arg "a$((++REQUEST_ARGC))" "${2}") |
| 94 | + REQUEST_QUERY+=' += [ $a'"${REQUEST_ARGC}"' | fromjson ] | .'; shift 2 |
| 95 | + ;; |
| 96 | + -c | --color ) |
| 97 | + COLOR='yes'; shift 1 |
| 98 | + ;; |
| 99 | + -g | --grape ) |
| 100 | + [[ -z "${2}" ]] && { |
| 101 | + echo "${ME##*/}: error: empty Grape hostname." >&2 |
| 102 | + exit 1 |
| 103 | + } |
| 104 | + |
| 105 | + LOOKUP_ARGV+=(--grape "${2}"); shift 2 |
| 106 | + ;; |
| 107 | + -h | --help ) |
| 108 | + show_help; shift 1 |
| 109 | + ;; |
| 110 | + -n | --numeric ) |
| 111 | + REQUEST_ARGV+=(--arg "n$((++REQUEST_ARGC))" "${2}") |
| 112 | + REQUEST_QUERY+=' += [ $n'"${REQUEST_ARGC}"' | tonumber ] | .'; shift 2 |
| 113 | + ;; |
| 114 | + -p | --port ) |
| 115 | + [[ -z "${2}" ]] && { |
| 116 | + echo "${ME##*/}: error: empty Grape port." >&2 |
| 117 | + exit 1 |
| 118 | + } |
| 119 | + |
| 120 | + LOOKUP_ARGV+=(--port "${2}"); shift 2 |
| 121 | + ;; |
| 122 | + -q | --query ) |
| 123 | + [[ -z "${2}" ]] && { |
| 124 | + echo "${ME##*/}: error: empty response query." >&2 |
| 125 | + exit 1 |
| 126 | + } |
| 127 | + |
| 128 | + RESPONSE_QUERY="${2}"; shift 2 |
| 129 | + ;; |
| 130 | + -r | --raw ) |
| 131 | + RESPONSE_ARGV+=(--raw-output); shift 1 |
| 132 | + ;; |
| 133 | + -s | --string ) |
| 134 | + REQUEST_ARGV+=(--arg "s$((++REQUEST_ARGC))" "${2}") |
| 135 | + REQUEST_QUERY+=' += [ $s'"${REQUEST_ARGC}"' | tostring ] | .'; shift 2 |
| 136 | + ;; |
| 137 | + -t | --tls ) |
| 138 | + TLS='yes'; LOOKUP_ARGV+=(--tls); shift 1 |
| 139 | + ;; |
| 140 | + -V | --version ) |
| 141 | + show_version; shift 1 |
| 142 | + ;; |
| 143 | + -w | --worker ) |
| 144 | + [[ -z "${2}" ]] && { |
| 145 | + echo "${ME##*/}: error: empty worker address." >&2 |
| 146 | + exit 1 |
| 147 | + } |
| 148 | + |
| 149 | + [[ "${2##*:}" =~ ^[0-9]+$ ]] || { |
| 150 | + echo "${ME##*/}: error: invalid worker port number." >&2 |
| 151 | + exit 1 |
| 152 | + } |
| 153 | + |
| 154 | + [[ "$((${2##*:} & 0xFFFF))" -eq 0 ]] && { |
| 155 | + echo "${ME##*/}: error: worker port number must be greater than zero." >&2 |
| 156 | + exit 1 |
| 157 | + } |
| 158 | + |
| 159 | + [[ "${2##*:}" -ne "$((${2##*:} & 0xFFFF))" ]] && { |
| 160 | + echo "${ME##*/}: error: worker port number too big." >&2 |
| 161 | + exit 1 |
| 162 | + } |
| 163 | + |
| 164 | + REQUEST_WORKER="${2%:*}:$((${2##*:} & 0xFFFF))"; shift 2 |
| 165 | + ;; |
| 166 | + -- ) shift; break ;; |
| 167 | + * ) break ;; |
| 168 | + esac |
| 169 | +done |
| 170 | + |
| 171 | +[[ -z "${COLOR}" ]] && { |
| 172 | + RESPONSE_ARGV+=(--compact-output --monochrome-output) |
| 173 | +} |
| 174 | + |
| 175 | +[[ "${#}" -lt 2 ]] && { |
| 176 | + show_help |
| 177 | +} |
| 178 | + |
| 179 | +[[ -f "${HOME}/.grenache-cli/grenache-cli.conf" ]] || { |
| 180 | + echo "${ME##*/}: error: you need to run \`grenache-keygen' first." >&2 |
| 181 | + exit "${EXIT_CODE}" |
| 182 | +} |
| 183 | + |
| 184 | +exec {stderr}>&2 |
| 185 | + |
| 186 | +[[ x"${GRENACHE_CLI_DEBUG:+set}" != xset ]] && { |
| 187 | + exec 2>/dev/null |
| 188 | +} |
| 189 | + |
| 190 | +[[ "${REQUEST_ARGC}" -gt 0 ]] && { |
| 191 | + REQUEST_ARGUMENTS="$(@JQ@ "${REQUEST_ARGV[@]}" --from-file <(echo "${REQUEST_QUERY}") <<<"${REQUEST_ARGUMENTS}")" |
| 192 | + |
| 193 | + [[ "${?}" -ne 0 ]] && { |
| 194 | + echo "${ME##*/}: error: provided arguments are invalid." >&"${stderr}" |
| 195 | + |
| 196 | + exec {stderr}>&- |
| 197 | + exit "${EXIT_CODE}" |
| 198 | + } |
| 199 | +} |
| 200 | + |
| 201 | +JSON="$(@CURL@ -qK "${HOME}/.grenache-cli/grenache-cli.conf" -A '@PACKAGE@/@PACKAGE_VERSION@' "http${TLS:+s}://${REQUEST_WORKER:-$(grenache-lookup "${LOOKUP_ARGV[@]}" "${1}")}" < <( \ |
| 202 | + @JQ@ --null-input --compact-output --monochrome-output \ |
| 203 | + --arg 'action' "${2}" \ |
| 204 | + --arg 'service' "${1}" \ |
| 205 | + --arg 'rid' "$(@UUIDGEN@)" \ |
| 206 | + --arg 'args' "${REQUEST_ARGUMENTS}" \ |
| 207 | + '[ $rid, $service, { "action": $action, "args": $args | fromjson } ]' \ |
| 208 | +))" |
| 209 | + |
| 210 | +[[ -z "${JSON}" ]] && { |
| 211 | + echo "${ME##*/}: error: service ${1} cannot be queried." >&"${stderr}" |
| 212 | + |
| 213 | + exec {stderr}>&- |
| 214 | + exit "${EXIT_CODE}" |
| 215 | +} |
| 216 | + |
| 217 | +@JQ@ --exit-status '.[1]' >/dev/null <<<"${JSON}" && { |
| 218 | + @JQ@ --raw-output '.[1]' <<<"${JSON}" >&"${stderr}" |
| 219 | +} || { |
| 220 | + EXIT_CODE=0 |
| 221 | + @JQ@ "${RESPONSE_ARGV[@]}" --from-file <(printf '.[2] | %s' "${RESPONSE_QUERY}") <<<"${JSON}" |
| 222 | +} |
| 223 | + |
| 224 | +exec {stderr}>&- |
| 225 | +exit "${EXIT_CODE}" |
0 commit comments