diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/README.md | 1 | ||||
-rwxr-xr-x | tools/setup-unit | 270 | ||||
-rwxr-xr-x | tools/unitc | 53 |
3 files changed, 284 insertions, 40 deletions
diff --git a/tools/README.md b/tools/README.md index f534aa1f..1a631e10 100644 --- a/tools/README.md +++ b/tools/README.md @@ -35,6 +35,7 @@ web page with NGINX Unit. |---------|-| | filename … | Read configuration data consequently from the specified files instead of stdin. | _HTTP method_ | It is usually not required to specify a HTTP method. `GET` is used to read the configuration. `PUT` is used when making configuration changes unless a specific method is provided. +| `edit` | Opens **URI** in the default editor for interactive configuration. The [jq](https://stedolan.github.io/jq/) tool is required for this option. | `INSERT` | A _virtual_ HTTP method that prepends data when the URI specifies an existing array. The [jq](https://stedolan.github.io/jq/) tool is required for this option. | `-q` \| `--quiet` | No output to stdout. diff --git a/tools/setup-unit b/tools/setup-unit index de1d4f5f..38592fe3 100755 --- a/tools/setup-unit +++ b/tools/setup-unit @@ -30,16 +30,13 @@ test -v ZSH_VERSION \ export LC_ALL=C -program_name="$0"; -prog_name="$(basename $program_name)"; - dry_run='no'; help_unit() { cat <<__EOF__ ; SYNOPSIS - $program_name [-h] COMMAND [ARGS] + $0 [-h] COMMAND [ARGS] Subcommands ├── repo-config [-hn] [PKG-MANAGER OS-NAME OS-VERSION] @@ -49,7 +46,7 @@ DESCRIPTION This script simplifies installing and configuring an NGINX Unit server for first-time users. - Run '$program_name COMMAND -h' for more information on a command. + Run '$0 COMMAND -h' for more information on a command. COMMANDS repo-config @@ -75,11 +72,12 @@ help_more_unit() { cat <<__EOF__ ; SYNOPSIS - $program_name [-h] COMMAND [ARGS] + $0 [-h] COMMAND [ARGS] Subcommands ├── cmd [-h] ├── ctl [-h] [-s SOCK] SUBCOMMAND [ARGS] + │ ├── edit [-h] PATH │ ├── http [-h] [-c CURLOPT] METHOD PATH │ └── insert [-h] PATH INDEX ├── freeport [-h] @@ -87,6 +85,7 @@ SYNOPSIS ├── os-probe [-h] ├── ps [-h] [-t TYPE] ├── repo-config [-hn] [PKG-MANAGER OS-NAME OS-VERSION] + ├── restart [-hls] ├── sock [-h] SUBCOMMAND [ARGS] │ ├── filter [-chs] │ └── find [-h] @@ -96,7 +95,7 @@ DESCRIPTION This script simplifies installing and configuring an NGINX Unit server for first-time users. - Run '$program_name COMMAND -h' for more information on a command. + Run '$0 COMMAND -h' for more information on a command. COMMANDS cmd Print the invocation line of unitd(8). @@ -137,12 +136,12 @@ __EOF__ warn() { - >&2 echo "$prog_name: error: $*"; + >&2 echo "$(basename "$0"): error: $*"; } err() { - >&2 echo "$prog_name: error: $*"; + >&2 echo "$(basename "$0"): error: $*"; exit 1; } @@ -167,7 +166,7 @@ help_unit_cmd() { cat <<__EOF__ ; SYNOPSIS - $program_name cmd [-h] + $0 cmd [-h] DESCRIPTION Print the invocation line of running unitd(8) instances. @@ -207,19 +206,22 @@ help_unit_ctl() { cat <<__EOF__ ; SYNOPSIS - $program_name ctl [-h] [-s SOCK] SUBCOMMAND [ARGS] + $0 ctl [-h] [-s SOCK] SUBCOMMAND [ARGS] Subcommands + ├── edit [-h] PATH ├── http [-h] [-c CURLOPT] METHOD PATH └── insert [-h] PATH INDEX DESCRIPTION Control a running unitd(8) instance through its control API socket. - Run '$program_name ctl SUBCOMMAND -h' for more information on a + Run '$0 ctl SUBCOMMAND -h' for more information on a subcommand. SUBCOMMANDS + edit Edit the unitd(8) configuration with an editor. + http Send an HTTP request to the control API socket. insert Insert an element at the specified index into an array in the @@ -300,6 +302,10 @@ unit_ctl() fi; case $1 in + edit) + shift; + unit_ctl_edit ${remote:+ ---r $remote} ---s "$sock" $@; + ;; http) shift; unit_ctl_http ${remote:+ ---r $remote} ---s "$sock" $@; @@ -315,11 +321,115 @@ unit_ctl() } +help_unit_ctl_edit() +{ + cat <<__EOF__ ; +SYNOPSIS + $0 ctl [CTL-OPTS] edit [-h] PATH + +DESCRIPTION + Edit the JSON configuration with an editor. The current configuration + is downloaded into a temporary file, open with the editor, and then + sent back to the control API socket. + + The following editors are tried in this order of preference: \$VISUAL, + \$EDITOR, editor(1), vi(1), vim(1), ed(1). + + +OPTIONS + -h, --help + Print this help. + +ENVIRONMENT + VISUAL + EDITOR + See environ(7). + +SEE ALSO + $0 ctl http -h; + + update-alternatives(1) + +__EOF__ +} + + +unit_ctl_edit() +{ + while test $# -ge 1; do + case "$1" in + -h | --help) + help_unit_ctl_edit; + exit 0; + ;; + ---r | ----remote) + local remote="$2"; + shift; + ;; + ---s | ----sock) + local sock="$2"; + shift; + ;; + -*) + err "ctl: edit: $1: Unknown option."; + ;; + *) + break; + ;; + esac; + shift; + done; + + if ! test $# -ge 1; then + err 'ctl: insert: PATH: Missing argument.'; + fi; + local req_path="$1"; + + if test -v remote; then + local remote_sock="$(echo "$sock" | unit_sock_filter -s)"; + local local_sock="$(mktemp -u -p /var/run/unit/)"; + local ssh_ctrl="$(mktemp -u -p /var/run/unit/)"; + + mkdir -p /var/run/unit/; + + ssh -fMNnT -S "$ssh_ctrl" \ + -o 'ExitOnForwardFailure yes' \ + -L "$local_sock:$remote_sock" "$remote"; + + sock="unix:$local_sock"; + fi; + + local tmp="$(mktemp ||:)"; + + unit_ctl_http ---s "$sock" -c --no-progress-meter GET "$req_path" \ + </dev/null >"$tmp" \ + ||:; + + $( + ((test -v VISUAL && test -n "$VISUAL") && printf '%s\n' "$VISUAL") \ + || ((test -v EDITOR && test -n "$EDITOR") && printf '%s\n' "$EDITOR") \ + || command -v editor \ + || command -v vi \ + || command -v vim \ + || echo ed; + ) "$tmp" \ + ||:; + + unit_ctl_http ---s "$sock" PUT "$req_path" <"$tmp" \ + ||:; + + if test -v remote; then + ssh -S "$ssh_ctrl" -O exit "$remote" 2>/dev/null; + unlink "$local_sock"; + fi; +} + + help_unit_ctl_http() { cat <<__EOF__ ; SYNOPSIS - $program_name ctl [CTL-OPTS] http [-h] [-c CURLOPT] METHOD PATH + $0 ctl [CTL-OPTS] http [-h] [-c CURLOPT] METHOD PATH DESCRIPTION Send an HTTP request to the unitd(8) control API socket. @@ -341,7 +451,7 @@ ENVIRONMENT Equivalent to the option -c (--curl). EXAMPLES - $program_name ctl http -c --no-progress-meter GET /config >tmp; + $0 ctl http -c --no-progress-meter GET /config >tmp; SEE ALSO <https://unit.nginx.org/controlapi/#api-manipulation> @@ -424,7 +534,7 @@ help_unit_ctl_insert() { cat <<__EOF__ ; SYNOPSIS - $program_name ctl [CTL-OPTS] insert [-h] PATH INDEX + $0 ctl [CTL-OPTS] insert [-h] PATH INDEX DESCRIPTION Insert an element at the specified position (INDEX) into the JSON array @@ -437,7 +547,7 @@ OPTIONS Print this help. SEE ALSO - $program_name ctl http -h; + $0 ctl http -h; __EOF__ } @@ -514,7 +624,7 @@ help_unit_ctl_welcome() { cat <<__EOF__ ; SYNOPSIS - $program_name welcome [-hn] + $0 welcome [-hn] DESCRIPTION This script tests an NGINX Unit installation by creating an initial @@ -676,7 +786,7 @@ unit_ctl_welcome() <hr> <p><a href="https://unit.nginx.org/?referer=welcome">NGINX Unit — the universal web app server</a><br> - NGINX, Inc. © 2022</p> + NGINX, Inc. © 2023</p> </body> </html> __EOF__'; @@ -720,7 +830,7 @@ help_unit_freeport() { cat <<__EOF__ ; SYNOPSIS - $program_name freeport [-h] + $0 freeport [-h] DESCRIPTION Print an available TCP port. @@ -828,7 +938,7 @@ help_unit_json_ins() { cat <<__EOF__ ; SYNOPSIS - $program_name json-ins [-hn] JSON INDEX + $0 json-ins [-hn] JSON INDEX ARGUMENTS JSON Path to a JSON file containing a top-level array. @@ -901,7 +1011,7 @@ help_unit_os_probe() { cat <<__EOF__ ; SYNOPSIS - $program_name os-probe [-h] + $0 os-probe [-h] DESCRIPTION This script probes the OS and prints three fields, delimited by ':'; @@ -978,7 +1088,7 @@ help_unit_ps() { cat <<__EOF__ ; SYNOPSIS - $program_name ps [-h] [-t TYPE] + $0 ps [-h] [-t TYPE] DESCRIPTION List unitd(8) processes. @@ -1034,7 +1144,7 @@ unit_ps() shift; done; - ps ax \ + ps awwx \ | if test -v type; then grep ${type_c:+-e 'unit: controller'} \ ${type_m:+-e 'unit: main'} \ @@ -1051,7 +1161,7 @@ help_unit_repo_config() { cat <<__EOF__ ; SYNOPSIS - $program_name repo-config [-hn] [PKG-MANAGER OS-NAME OS-VERSION] + $0 repo-config [-hn] [PKG-MANAGER OS-NAME OS-VERSION] DESCRIPTION This script configures the NGINX Unit repository for the system @@ -1082,11 +1192,11 @@ OPTIONS what it does. EXAMPLES - $ $prog_name repo-config apt debian bullseye; - $ $prog_name repo-config apt ubuntu jammy; - $ $prog_name repo-config dnf fedora 36; - $ $prog_name repo-config dnf rhel 9; - $ $prog_name repo-config yum amzn2 2; + $ $(basename "$0") repo-config apt debian bullseye; + $ $(basename "$0") repo-config apt ubuntu jammy; + $ $(basename "$0") repo-config dnf fedora 36; + $ $(basename "$0") repo-config dnf rhel 9; + $ $(basename "$0") repo-config yum amzn2 2; __EOF__ } @@ -1222,11 +1332,101 @@ __EOF__"; } +help_unit_restart() +{ + cat <<__EOF__ ; +SYNOPSIS + $0 restart [-hls] + +DESCRIPTION + Restart all running unitd(8) instances. + +OPTIONS + -h, --help + Print this help. + + -l, --log + Reset log file. + + -s, --statedir + Reset \$statedir. + +CAVEATS + This command will ask for confirmation before removing + directories; please review those prompts with care, as unknown + bugs in the command may attempt to wipe your file system. + +__EOF__ +} + + +unit_restart() +{ + while test $# -ge 1; do + case "$1" in + -h | --help) + help_unit_restart; + exit 0; + ;; + -l | --log) + local log_flag='yes'; + ;; + -s | --statedir) + local state_flag='yes'; + ;; + -*) + err "restart: $1: Unknown option."; + ;; + *) + err "restart: $1: Unknown argument."; + ;; + esac; + shift; + done; + + local cmds="$(unit_cmd)"; + + pkill -e unitd; + + printf '%s\n' "$cmds" \ + | while read -r cmd; do + if test -v log_flag; then + ( + echo "$cmd" \ + | grep '\--log' \ + | sed 's/.*--log \+\([^ ]\+\).*/\1/' \ + || eval $cmd --help \ + | grep -A1 '\--log FILE' \ + | grep 'default:' \ + | sed 's/.*"\(.*\)".*/\1/'; + ) \ + | xargs rm -f; + fi; + + if test -v state_flag; then + ( + echo "$cmd" \ + | grep '\--statedir' \ + | sed 's/.*--statedir \+\([^ ]\+\).*/\1/' \ + || eval $cmd --help \ + | grep -A1 '\--statedir DIR' \ + | grep 'default:' \ + | sed 's/.*"\(.*\)".*/\1/'; + ) \ + | xargs -I {} find {} -mindepth 1 -maxdepth 1 \ + | xargs rm -rfi; + fi; + + eval $cmd; + done; +} + + help_unit_sock() { cat <<__EOF__ ; SYNOPSIS - $program_name sock [-h] SUBCOMMAND [ARGS] + $0 sock [-h] SUBCOMMAND [ARGS] Subcommands ├── filter [-ch] @@ -1236,7 +1436,7 @@ DESCRIPTION Print the control API socket address of running unitd(8) instances. - Run '$program_name sock SUBCOMMAND -h' for more information on a + Run '$0 sock SUBCOMMAND -h' for more information on a subcommand. SUBCOMMANDS @@ -1297,7 +1497,7 @@ help_unit_sock_filter() { cat <<__EOF__ ; SYNOPSIS - $program_name sock filter [-chs] + $0 sock filter [-chs] DESCRIPTION Filter the output of the 'sock find' command and transform it to @@ -1376,7 +1576,7 @@ help_unit_sock_find() { cat <<__EOF__ ; SYNOPSIS - $program_name sock find [-h] + $0 sock find [-h] DESCRIPTION Find and print the control API socket address of running @@ -1481,6 +1681,10 @@ repo-config) shift; unit_repo_config $@; ;; +restart) + shift; + unit_restart $@; + ;; sock) shift; unit_sock $@; diff --git a/tools/unitc b/tools/unitc index 9973e62d..877e11d4 100755 --- a/tools/unitc +++ b/tools/unitc @@ -1,7 +1,7 @@ #!/bin/bash # unitc - a curl wrapper for configuring NGINX Unit # https://github.com/nginx/unit/tree/master/tools -# NGINX, Inc. (c) 2022 +# NGINX, Inc. (c) 2023 # Defaults # @@ -32,7 +32,7 @@ while [ $# -gt 0 ]; do shift ;; - "GET" | "PUT" | "POST" | "DELETE" | "INSERT") + "GET" | "PUT" | "POST" | "DELETE" | "INSERT" | "EDIT") METHOD=$OPTION shift ;; @@ -71,6 +71,7 @@ USAGE: ${0##*/} [options] URI General options filename … # Read configuration data from files instead of stdin HTTP method # Default=GET, or PUT with config data (case-insensitive) + EDIT # Opens the URI contents in \$EDITOR INSERT # Virtual HTTP method to prepend data to an existing array -q | --quiet # No output to stdout @@ -129,12 +130,23 @@ if [ $REMOTE -eq 0 ]; then exit 1 fi - # Get control address + # Obtain any optional startup parameters from the 'unitd: main' process + # so we can get the actual control address and error log location. + # Command line options and output of ps(1) is notoriously variable across + # different *nix/BSD platforms so multiple attempts might be needed. # - PARAMS=$(ps $PID | grep unitd | cut -f2- -dv | tr '[]' ' ' | cut -f3- -d ' ' | sed -e 's/ --/\n--/g') + PARAMS=$((ps -wwo args=COMMAND -p $PID || ps $PID) 2> /dev/null | grep unit | tr '[]' ^ | cut -f2 -d^ | sed -e 's/ --/\n--/g') + if [ "$PARAMS" = "" ]; then + echo "${0##*/}: WARNING: unable to identify unitd command line parameters for PID $PID, assuming unitd defaults from \$PATH" + PARAMS=unitd + fi CTRL_ADDR=$(echo "$PARAMS" | grep '\--control' | cut -f2 -d' ') if [ "$CTRL_ADDR" = "" ]; then - CTRL_ADDR=$($(echo "$PARAMS" | grep unitd) --help | grep -A1 '\--control' | tail -1 | cut -f2 -d\") + CTRL_ADDR=$($(echo "$PARAMS") --help | grep -A1 '\--control' | tail -1 | cut -f2 -d\") + fi + if [ "$CTRL_ADDR" = "" ]; then + echo "${0##*/}: ERROR: cannot detect control socket. Did you start unitd with a relative path? Try starting unitd with --control option." + exit 2 fi # Prepare for network or Unix socket addressing @@ -156,7 +168,11 @@ if [ $REMOTE -eq 0 ]; then # ERROR_LOG=$(echo "$PARAMS" | grep '\--log' | cut -f2 -d' ') if [ "$ERROR_LOG" = "" ]; then - ERROR_LOG=$($(echo "$PARAMS" | grep unitd) --help | grep -A1 '\--log' | tail -1 | cut -f2 -d\") + ERROR_LOG=$($(echo "$PARAMS") --help | grep -A1 '\--log' | tail -1 | cut -f2 -d\") + fi + if [ "$ERROR_LOG" = "" ]; then + echo "${0##*/}: WARNING: cannot detect unit log file (will not be monitored). If you started unitd from a relative path then try using the --log option." + ERROR_LOG=/dev/null fi # Cache the discovery for this unit PID (and cleanup any old files) @@ -190,6 +206,29 @@ fi if [ -t 0 ] && [ ${#CONF_FILES[@]} -eq 0 ]; then if [ "$METHOD" = "DELETE" ]; then $SSH_CMD curl -X $METHOD $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT + elif [ "$METHOD" = "EDIT" ]; then + EDITOR=$(test "$EDITOR" && printf '%s' "$EDITOR" || command -v editor || command -v vim || echo vi) + EDIT_FILENAME=/tmp/${0##*/}.$$${URI//\//_} + $SSH_CMD curl -fsS $UNIT_CTRL$URI > $EDIT_FILENAME || exit 2 + if [ "${URI:0:12}" = "/js_modules/" ]; then + if ! hash jq 2> /dev/null; then + echo "${0##*/}: ERROR: jq(1) is required to edit JavaScript modules; install at <https://stedolan.github.io/jq/>" + exit 1 + fi + jq -r < $EDIT_FILENAME > $EDIT_FILENAME.js # Unescape linebreaks for a better editing experience + EDIT_FILE=$EDIT_FILENAME.js + $EDITOR $EDIT_FILENAME.js || exit 2 + # Remove the references, delete old config, push new config+reference + $SSH_CMD curl -fsS $UNIT_CTRL/config/settings/js_module > /tmp/${0##*/}.$$_js_module && \ + $SSH_CMD curl -X DELETE $UNIT_CTRL/config/settings/js_module && \ + $SSH_CMD curl -fsSX DELETE $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ && \ + printf "%s" "$(< $EDIT_FILENAME.js)" | $SSH_CMD curl -fX PUT --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ && \ + $SSH_CMD curl -X PUT --data-binary @/tmp/${0##*/}.$$_js_module $UNIT_CTRL/config/settings/js_module 2> /tmp/${0##*/}.$$ + else + tr -d '\r' < $EDIT_FILENAME > $EDIT_FILENAME.json # Remove carriage-return from newlines + $EDITOR $EDIT_FILENAME.json || exit 2 + $SSH_CMD curl -X PUT --data-binary @$EDIT_FILENAME.json $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT + fi else SHOW_LOG=$(echo $URI | grep -c ^/control/) $SSH_CMD curl $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT @@ -225,7 +264,7 @@ if [ $CURL_STATUS -ne 0 ]; then fi exit 4 fi -rm -f /tmp/${0##*/}.$$ 2> /dev/null +rm -f /tmp/${0##*/}.$$* 2> /dev/null if [ $SHOW_LOG -gt 0 ] && [ $NOLOG -eq 0 ] && [ $QUIET -eq 0 ]; then echo -n "${0##*/}: Waiting for log..." |