diff --git a/contrib/README.md b/contrib/README.md index b8b039c..fa9d4b2 100644 --- a/contrib/README.md +++ b/contrib/README.md @@ -60,6 +60,8 @@ cd contrib/ ./clboss-askrene-layer-summary +./cln-plugin-bounce ... + The `clboss-routing-stats` and `clboss-forwarding-stats` scripts now accept `--days` to limit how many days of earnings history are considered when ranking channels. @@ -83,6 +85,19 @@ how many days of earnings history are considered when ranking channels. with `--top N` resolving the busiest directions to node aliases. Works on any persistent layer (`clboss` by default) and can replay a captured `askrene-listlayers` dump offline via `--input`. +- **`cln-plugin-bounce`** stops and restarts running plugins without + restarting `lightningd`. `plugin stop` needs a plugin's exact + registered name, which for versioned installs includes the version + string; the script looks each one up from `plugin list`, stops the + named plugins in the order given, and starts them again in reverse + order, so the list order encodes any shutdown dependency between + them. Restarts use the unversioned sibling path when one exists + (usually a symlink maintained by the install script), so a repointed + symlink brings up the new version. Plugin names are the arguments + not starting with `-`; every other argument is passed to + `lightning-cli` (e.g. `--signet --lightning-dir=...`), so names and + options may appear in any order. Plain POSIX sh plus `jq`, so unlike + a shell alias it also works under `sudo`. - **`fee-log-parser`** is a parser that streams DEBUG-level logging and writes a sqlite database containing fee algorithm information. CLBOSS now records the same schema in its internal database (`data.clboss`, tables diff --git a/contrib/cln-plugin-bounce b/contrib/cln-plugin-bounce new file mode 100755 index 0000000..d5c8a43 --- /dev/null +++ b/contrib/cln-plugin-bounce @@ -0,0 +1,134 @@ +#!/bin/sh +# cln-plugin-bounce - stop and restart running CLN plugins without +# restarting lightningd. +# +# usage: cln-plugin-bounce [lightning-cli options...] ... +# +# "plugin stop" requires a plugin's exact registered name, which for +# versioned installs includes the version string (e.g. +# /usr/local/bin/xrebalance-v0.4.1). This script looks each exact +# name up from "plugin list", stops the plugins in the order given, +# then starts them again in reverse order, so the list order encodes +# any shutdown dependency between them. When an unversioned sibling +# path exists (usually a symlink maintained by the install script), +# the restart uses that, so a repointed symlink brings up the new +# version. +# +# Plugin names are the arguments that do not start with "-"; each is +# a short name ("clboss", "xrebalance"), or a full versioned basename +# if the short name matches more than one running plugin. All other +# arguments are passed to every lightning-cli call, so names and +# options may appear in any order: +# cln-plugin-bounce --signet --lightning-dir="$CLNDIR" xrebalance clboss +# Set $LIGHTNING_CLI to override the lightning-cli binary. + +set -eu + +me=${0##*/} + +names= +rebuilt= +for arg do + [ -n "$rebuilt" ] || { set --; rebuilt=1; } + if [ "${arg#-}" = "$arg" ]; then + names="${names}${arg} +" + else + set -- "$@" "$arg" + fi +done + +if [ -z "$names" ]; then + echo "usage: $me [lightning-cli options...] ..." >&2 + exit 2 +fi + +lcli=${LIGHTNING_CLI:-lightning-cli} + +if ! command -v jq >/dev/null; then + echo "$me: jq is required" >&2 + exit 1 +fi + +# Registered paths whose basename is or -. +match='.plugins[].name + | select((sub(".*/"; "")) as $b + | $b == $n or ($b | startswith($n + "-")))' + +# Resolve every name before touching anything, from one snapshot. +plugins=$("$lcli" "$@" plugin list) + +resolved= +while IFS= read -r n; do + [ -n "$n" ] || continue + running=$(printf '%s\n' "$plugins" | jq -r --arg n "$n" "$match") + if [ -z "$running" ]; then + echo "$me: no running plugin matches '$n'" >&2 + exit 1 + fi + if [ "$(printf '%s\n' "$running" | wc -l)" -gt 1 ]; then + echo "$me: '$n' matches more than one running plugin:" >&2 + printf '%s\n' "$running" >&2 + exit 1 + fi + resolved="${resolved}${n} ${running} +" +done </dev/null; then + echo "$me: stop failed for $path" >&2 + if [ -n "$stopped_names" ]; then + echo "$me: already stopped, not restarted:$stopped_names" >&2 + fi + exit 1 + fi + stopped_names="$stopped_names $n" + tostart="${n} ${path} +${tostart}" +done </dev/null; then + ok= + if [ "$start" != "$path" ]; then + echo "$me: start failed; retrying with $path" >&2 + if "$lcli" "$@" plugin start "$path" >/dev/null; then + ok=1 + fi + fi + if [ -z "$ok" ]; then + echo "$me: failed to start $n" >&2 + failed=1 + fi + fi +done <