diff --git a/.gitignore b/.gitignore
index e65eb08..bce5b5c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,7 @@ grenache-get
grenache-put
grenache-keygen
grenache-lookup
+grenache-announce
#
# Distribution
diff --git a/README.md b/README.md
index aa97a4b..699a9b3 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,13 @@
# Grenache CLI
-[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
+![license](https://img.shields.io/github/license/bitfinexcom/grenache-cli.svg)
+![GitHub (pre-)release](https://img.shields.io/github/release/bitfinexcom/grenache-cli/all.svg)
+![GitHub (Pre-)Release Date](https://img.shields.io/github/release-date-pre/bitfinexcom/grenache-cli.svg)
+![GitHub last commit](https://img.shields.io/github/last-commit/bitfinexcom/grenache-cli.svg)
+![GitHub pull requests](https://img.shields.io/github/issues-pr/bitfinexcom/grenache-cli.svg)
+![stability-wip](https://img.shields.io/badge/stability-work_in_progress-lightgrey.svg)
+
+
* [Introduction](#introduction)
* [Prerequisites](#prerequisites)
@@ -17,7 +24,7 @@
## Introduction
-The **Grenache** **C**ommand **L**ine **I**nterface is a set of tools to use the [grenache-grape](https://github.com/bitfinexcom/grenache-grape) suite directly from your command line. Using this set of tools you can create fancy scripts that communicate directly with the DHT.
+The [**Grenache**](https://github.com/bitfinexcom/grenache) **C**ommand **L**ine **I**nterface is a set of tools to use the [grenache-grape](https://github.com/bitfinexcom/grenache-grape) suite directly from your command line. Using this set of tools you can create fancy scripts that communicate directly with the DHT.
## Prerequisites
@@ -135,7 +142,38 @@ to retrieve the complete options list.
## Announce services
-Coming soon...
+The `grenache-announce` command announces the given services in order to be stored in the DHT. To announce the [rest:net:util](https://github.com/bitfinexcom/bfx-util-net-js) service on port _31337_, simply run something like this:
+
+```bash
+grenache-announce 'rest:net:util,31337'
+```
+
+If no services are specified, `grenache-announce` enters the _streaming_ mode, reading the standard input. All comments (marked by a **#** or **;**) and blank lines are ignored. For example, a list of services can be announced using something like this:
+
+```bash
+grenache-announce /run/grape/announce
+```
+
+Storing a service in the DHT now simply requires something like this:
+
+```bash
+echo 'rest:net:util,31337' >/run/grape/announce
+```
+
+A JSON stream can also be required (perhaps to implement a network service with something like [tcpserver](https://cr.yp.to/ucspi-tcp/tcpserver.html)) using the `-j` switch or its long form `--json`. The JSON document must contain a `data` array in which the _first position_ is the service name while the _second_ is the port number. Another useful option is `-d` or `--delimiter` which allows you to set the delimiter string between the _service name_ and _port number_ when not using JSON mode. See
+
+```bash
+grenache-announce --help
+```
+
+to retrieve the complete options list.
## Maintainers
diff --git a/configure.ac b/configure.ac
index 8e6b48e..2d8c12e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -15,9 +15,9 @@ dnl implied. See the License for the specific language governing permissions
dnl and limitations under the License.
AC_PREREQ(2.69)
-AC_INIT([grenache-cli], [0.5.0], [davide@bitfinex.com])
+AC_INIT([grenache-cli], [0.6.0], [davide@bitfinex.com])
-AC_SUBST([VERSION], [0.5.0])
+AC_SUBST([VERSION], [0.6.0])
AC_SUBST([SB], [`$srcdir/shtool echo -n -e %B`])
AC_SUBST([EB], [`$srcdir/shtool echo -n -e %b`])
@@ -151,7 +151,7 @@ AC_MSG_CHECKING([whether you want to enable the x86 features check])
if test "x${ax_cv_enable_x86_features}" = 'xyes'; then
AC_MSG_RESULT([yes])
- case "${target}" in
+ case "${host}" in
i?86-* | amd64-* | x86_64-*)
AX_CHECK_X86_FEATURES
;;
@@ -187,6 +187,7 @@ AC_CONFIG_FILES([ \
src/grenache-get \
src/grenache-keygen \
src/grenache-lookup \
+ src/grenache-announce \
tests/Makefile \
tests/sign-test-vector-1.sh \
tests/sign-test-vector-2.sh \
diff --git a/src/Makefile.am b/src/Makefile.am
index 56b5201..31bc212 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -30,7 +30,8 @@ bin_SCRIPTS = \
grenache-put \
grenache-get \
grenache-keygen \
-grenache-lookup
+grenache-lookup \
+grenache-announce
noinst_LTLIBRARIES = \
libed25519.la \
diff --git a/src/grenache-announce.in b/src/grenache-announce.in
new file mode 100644
index 0000000..f767996
--- /dev/null
+++ b/src/grenache-announce.in
@@ -0,0 +1,182 @@
+#!/bin/bash
+############################################################################
+# This file is part of Grenache Command Line Interface. #
+# #
+# Copyright (C) 2017, 2018 Davide Scola #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License"); you may #
+# not use this file except in compliance with the License. You may obtain #
+# a copy of the License at #
+# #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or #
+# implied. See the License for the specific language governing permissions #
+# and limitations under the License. #
+############################################################################
+
+readonly ME="${BASH_SOURCE[0]}"
+readonly WHOAMI="$(@READLINK@ -snf "${ME}")"
+readonly ARGV=$(@GETOPT@ -o 'd:g:hjp:tV' --long 'delimiter:,grape:,help,json,port:,tls,version' -n "${ME##*/}" -- "$@") || exit 1
+
+TLS=''
+PORT=30002
+JQ_QUERY=''
+EXIT_CODE=1
+DELIMITER=','
+HOSTNAME='127.0.0.1'
+JQ_ARGV=(--unbuffered -r)
+
+
+# Show program's help.
+function show_help {
+ @CAT@ <<_EOF
+Usage:
+ ${ME##*/} [OPTIONS] ... -- announce a service on the DHT.
+
+ Options:
+
+ -d, --delimiter Set the service/port delimiter
+ -g, --grape Set the Grape hostname
+ -j, --json Use JSON as data serializer
+ -p, --port Set the Grape port number
+ -t, --tls Enable TLS
+
+ -h, --help Show this message
+ -V, --version Show version information
+
+ The order of the specified options is not relevant.
+_EOF
+
+ exit 1
+}
+
+# Show program's version.
+function show_version {
+ @CAT@ <<_EOF
+${WHOAMI##*/} (@host_cpu@-@host_os@) @PACKAGE_NAME@/@PACKAGE_VERSION@
+Copyright (C) 2017, 2018 Davide Scola <@PACKAGE_BUGREPORT@>
+This is free software; see the source for copying conditions. There is
+NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
+PURPOSE.
+
+_EOF
+
+ exit 1
+}
+
+
+while true; do
+ case "$1" in
+ -d | --delimiter )
+ [[ -z "${2}" ]] && {
+ echo "${ME##*/}: error: empty delimiter." >&2
+ exit 1
+ }
+
+ DELIMITER="${2}"; shift 2
+ ;;
+ -g | --grape )
+ [[ -z "${2}" ]] && {
+ echo "${ME##*/}: error: empty Grape hostname." >&2
+ exit 1
+ }
+
+ HOSTNAME="${2}"; shift 2
+ ;;
+ -h | --help )
+ show_help; shift 1
+ ;;
+ -j | --json )
+ JQ_QUERY='select(.data) | (.data[0] + "\t" + (.data[1] | tostring))'; shift 1
+ ;;
+ -p | --port )
+ [[ "${2}" =~ ^[0-9]+$ ]] || {
+ echo "${ME##*/}: error: invalid Grape port number." >&2
+ exit 1
+ }
+
+ [[ "$((${2} & 0xFFFF))" -eq 0 ]] && {
+ echo "${ME##*/}: error: Grape port number must be greater than zero." >&2
+ exit 1
+ }
+
+ [[ "${2}" -ne "$((${2} & 0xFFFF))" ]] && {
+ echo "${ME##*/}: error: Grape port number too big." >&2
+ exit 1
+ }
+
+ PORT="$((${2} & 0xFFFF))"; shift 2
+ ;;
+ -t | --tls )
+ TLS='yes'; shift 1
+ ;;
+ -V | --version )
+ show_version; shift 1
+ ;;
+ -- ) shift; break ;;
+ * ) break ;;
+ esac
+done
+
+
+[[ -f "${HOME}/.grenache-cli/grenache-cli.conf" ]] || {
+ echo "${ME##*/}: error: you need to run \`grenache-keygen' first." >&2
+ exit 1
+}
+
+exec {stderr}>&2
+
+[[ x"${GRENACHE_CLI_DEBUG:+set}" != xset ]] && {
+ exec 2>/dev/null
+}
+
+[[ -z "${JQ_QUERY}" ]] && {
+ JQ_ARGV+=(-R)
+ JQ_QUERY="$(printf 'split("%s") | (.[0] + "\t" + .[1])' "${DELIMITER}")"
+}
+
+while read service port
+do
+ [[ -n "${service}" ]] || {
+ echo "${ME##*/}: warning: empty service name, skipping." >&"${stderr}"
+ continue
+ }
+
+ [[ "$((${port} & 0xFFFF))" -gt 0 && "${port}" -eq "$((${port} & 0xFFFF))" ]] || {
+ echo "${ME##*/}: warning: invalid port number, skipping." >&"${stderr}"
+ continue
+ }
+
+ NODES="$(@JQ@ -r '.' < <(
+ @CURL@ -qK "${HOME}/.grenache-cli/grenache-cli.conf" -A '@PACKAGE@/@PACKAGE_VERSION@' "http${TLS:+s}://${HOSTNAME}:${PORT}/announce" < <( \
+ @JQ@ -cnM \
+ --arg 'port' "${port}" \
+ --arg 'service' "${service}" \
+ --arg 'rid' "$(@UUIDGEN@)" \
+ '{ "data": [$service, ($port | tonumber)], "rid": $rid }' \
+ ) \
+ ))"
+
+ [[ "${NODES}" =~ ^[0-9]+$ ]] || {
+ echo "${ME##*/}: warning: unable to announce service ${service}: ${NODES}." >&"${stderr}"
+ continue
+ }
+
+ [[ "${NODES}" -gt 0 ]] && { \
+ EXIT_CODE=0
+ } || { \
+ echo "${ME##*/}: warning: service ${service} has not been indexed." >&"${stderr}"
+ }
+done < <(
+ @JQ@ "${JQ_ARGV[@]}" "${JQ_QUERY}" < <( \
+ [[ "${#}" -eq 0 ]] \
+ && @SED@ -u 's/\s*[#;].*$//;/^\s*$/d' - \
+ || printf '%s\n' "${@}"
+ )
+)
+
+exec {stderr}>&-
+exit "${EXIT_CODE}"
diff --git a/src/grenache-get.in b/src/grenache-get.in
index 794a99d..b97bf68 100644
--- a/src/grenache-get.in
+++ b/src/grenache-get.in
@@ -56,7 +56,7 @@ _EOF
# Show program's version.
function show_version {
@CAT@ <<_EOF
-@PACKAGE_NAME@ (@host_cpu@-@host_os@) @PACKAGE_VERSION@
+${WHOAMI##*/} (@host_cpu@-@host_os@) @PACKAGE_NAME@/@PACKAGE_VERSION@
Copyright (C) 2017, 2018 Davide Scola <@PACKAGE_BUGREPORT@>
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
diff --git a/src/grenache-keygen.in b/src/grenache-keygen.in
index 46a2f85..6761827 100644
--- a/src/grenache-keygen.in
+++ b/src/grenache-keygen.in
@@ -46,7 +46,7 @@ _EOF
# Show program's version.
function show_version {
@CAT@ <<_EOF
-@PACKAGE_NAME@ (@host_cpu@-@host_os@) @PACKAGE_VERSION@
+${WHOAMI##*/} (@host_cpu@-@host_os@) @PACKAGE_NAME@/@PACKAGE_VERSION@
Copyright (C) 2017, 2018 Davide Scola <@PACKAGE_BUGREPORT@>
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
diff --git a/src/grenache-lookup.in b/src/grenache-lookup.in
index 8116ad0..f82e5cc 100644
--- a/src/grenache-lookup.in
+++ b/src/grenache-lookup.in
@@ -61,7 +61,7 @@ _EOF
# Show program's version.
function show_version {
@CAT@ <<_EOF
-@PACKAGE_NAME@ (@host_cpu@-@host_os@) @PACKAGE_VERSION@
+${WHOAMI##*/} (@host_cpu@-@host_os@) @PACKAGE_NAME@/@PACKAGE_VERSION@
Copyright (C) 2017, 2018 Davide Scola <@PACKAGE_BUGREPORT@>
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
diff --git a/src/grenache-put.in b/src/grenache-put.in
index ad2348b..e11bf5b 100644
--- a/src/grenache-put.in
+++ b/src/grenache-put.in
@@ -58,7 +58,7 @@ _EOF
# Show program's version.
function show_version {
@CAT@ <<_EOF
-@PACKAGE_NAME@ (@host_cpu@-@host_os@) @PACKAGE_VERSION@
+${WHOAMI##*/} (@host_cpu@-@host_os@) @PACKAGE_NAME@/@PACKAGE_VERSION@
Copyright (C) 2017, 2018 Davide Scola <@PACKAGE_BUGREPORT@>
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR