#!/usr/bin/env bash ##################################################################### # # Copyright (C) NGINX, Inc. # Author: NGINX Unit Team, F5 Inc. # Copyright 2024, Alejandro Colomar # ##################################################################### if test -n ${BASH_VERSION} && test "${BASH_VERSINFO[0]}" -eq 3; then >&2 cat <<__EOF__ ; Your version of bash(1) isn't supported by this script. You're probably running on macOS. We recommend that you either install a newer version of bash(1) or run this script with another shell, such as zsh(1): $ ${SUDO_USER:+sudo }zsh $0 ... __EOF__ exit 1; fi; set -Eefuo pipefail; test -v BASH_VERSION \ && shopt -s lastpipe; test -v ZSH_VERSION \ && setopt sh_word_split; export LC_ALL=C dry_run='no'; help_unit() { cat <<__EOF__ ; SYNOPSIS $0 [-h[h]] COMMAND [ARGS] Subcommands ├── repo-config [-hn] [PKG-MANAGER OS-NAME OS-VERSION] └── welcome [-hn] DESCRIPTION This script simplifies installing and configuring an NGINX Unit server for first-time users. Run '$0 COMMAND -h' for more information on a command. COMMANDS repo-config Configure your package manager with the NGINX Unit repository for later installation. welcome Create an initial configuration to serve a welcome web page with NGINX Unit. OPTIONS -h, --help Print this help. -hh, --help-more Print help for more (advanced) commands. __EOF__ } help_more_unit() { cat <<__EOF__ ; SYNOPSIS $0 [-h[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] ├── json-ins [-hn] JSON INDEX ├── 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] └── welcome [-hn] DESCRIPTION This script simplifies installing and configuring an NGINX Unit server for first-time users. Run '$0 COMMAND -h' for more information on a command. COMMANDS cmd Print the invocation line of unitd(8). ctl Control a running unitd(8) instance via its control API socket. freeport Print an available TCP port. json-ins Insert a JSON element read from standard input into a JSON array read from a file at a given INDEX. os-probe Probe the OS and print details about its version. ps List unitd(8) processes. repo-config Configure your package manager with the NGINX Unit repository for later installation. restart Restart all running unitd(8) instances. sock Print the control API socket address. welcome Create an initial configuration to serve a welcome web page with NGINX Unit. OPTIONS -h, --help Print basic help (some commands are hidden). -hh, --help-more Print the hidden help with more commands. __EOF__ } info() { >&2 echo "$(basename "$0"): info: $*"; } warn() { >&2 echo "$(basename "$0"): error: $*"; } err() { >&2 echo "$(basename "$0"): error: $*"; exit 1; } dry_run_echo() { if test "$dry_run" = "yes"; then echo "$*"; fi; } dry_run_eval() { if test "$dry_run" = "yes"; then echo " $*"; else eval "$*"; fi; } run_trap() { trap -p "$1" \ | tr -d '\n' \ | sed "s/[^']*'\(.*\)'[^']*/\1/" \ | sed "s/'\\\\''/'/g" \ | read -r trap_cmd; eval $trap_cmd; trap - "$1"; } help_unit_cmd() { cat <<__EOF__ ; SYNOPSIS $0 cmd [-h] DESCRIPTION Print the invocation line of running unitd(8) instances. OPTIONS -h, --help Print this help. __EOF__ } unit_cmd() { while test $# -ge 1; do case "$1" in -h | --help) help_unit_cmd; exit 0; ;; -*) err "cmd: $1: Unknown option."; ;; *) err "cmd: $1: Unknown argument."; ;; esac; shift; done; unit_ps -t m \ | sed 's/.*\[\(.*\)].*/\1/'; } help_unit_ctl() { cat <<__EOF__ ; SYNOPSIS $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 '$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 JSON configuration. OPTIONS -h, --help Print this help. -s, --sock SOCK Use SOCK as the control API socket address. If not specified, the script tries to find it. This value is used by subcommands. The socket can be a tcp(7) socket or a unix(7) socket; in the case of a unix(7) socket, it can exist locally or on a remote machine, accessed through ssh(1). Accepted syntax for SOCK: unix:/path/to/control.sock ssh://[user@]host[:port]/path/to/control.sock [http[s]://]host[:port] The last form is less secure than the first two; have a look: ENVIRONMENT Options take precedence over their equivalent environment variables; if both are specified, the command-line option is used. UNIT_CTL_SOCK Equivalent to the option -s (--sock). __EOF__ } unit_ctl() { if test -v UNIT_CTL_SOCK; then local sock="$UNIT_CTL_SOCK"; fi; while test $# -ge 1; do case "$1" in -h | --help) help_unit_ctl; exit 0; ;; -s | --sock) if ! test $# -ge 2; then err "ctl: $1: Missing argument."; fi; local sock="$2"; shift; ;; -*) err "ctl: $1: Unknown option."; ;; *) break; ;; esac; shift; done; if test ! $# -ge 1; then err 'ctl: Missing subcommand.'; fi; if ! test -v sock; then local sock="$(unit_sock_find)"; fi; if echo $sock | grep '^ssh://' >/dev/null; then local remote="$(echo $sock | sed 's,\(ssh://[^/]*\).*,\1,')"; local sock="$(echo $sock | sed 's,ssh://[^/]*\(.*\),unix:\1,')"; 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"; trap "ssh -S '$ssh_ctrl' -O exit '$remote' 2>/dev/null; unlink '$local_sock';" EXIT; sock="unix:$local_sock"; fi; case $1 in edit) shift; unit_ctl_edit ---s "$sock" $@; ;; http) shift; unit_ctl_http ---s "$sock" $@; ;; insert) shift; unit_ctl_insert ---s "$sock" $@; ;; *) err "ctl: $1: Unknown argument."; ;; esac; if test -v remote; then run_trap EXIT; fi; } 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; ;; ---s | ----sock) local sock="$2"; shift; ;; -*) err "ctl: edit: $1: Unknown option."; ;; *) break; ;; esac; shift; done; if ! test $# -ge 1; then err 'ctl: edit: PATH: Missing argument.'; fi; local req_path="$1"; echo "$req_path" \ | sed 's%^/js_modules/.*%.js%' \ | sed 's%^/config\>.*%.json%' \ | sed 's%^/.*%.txt%' \ | xargs mktemp --suffix \ | read -r tmp; unit_ctl_http ---s "$sock" -c --no-progress-meter GET "$req_path" \ "$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"; trap "info 'ctl: edit: Invalid configuration saved in <$tmp>.'" ERR unit_ctl_http ---s "$sock" PUT "$req_path" <"$tmp"; trap - ERR; } help_unit_ctl_http() { cat <<__EOF__ ; SYNOPSIS $0 ctl [CTL-OPTS] http [-h] [-c CURLOPT] METHOD PATH DESCRIPTION Send an HTTP request to the unitd(8) control API socket. The payload is read from standard input. OPTIONS -c, --curl CURLOPT Pass CURLOPT as an option to curl. This script is implemented in terms of curl(1), so it's useful to be able to tweak its behavior. The option can be cumulatively used multiple times (the result is also appended to UNIT_CTL_HTTP_CURLOPTS). -h, --help Print this help. ENVIRONMENT UNIT_CTL_HTTP_CURLOPTS Equivalent to the option -c (--curl). EXAMPLES $0 ctl http -c --no-progress-meter GET /config >tmp; SEE ALSO __EOF__ } unit_ctl_http() { local curl_options="${UNIT_CTL_HTTP_CURLOPTS:-}"; while test $# -ge 1; do case "$1" in -c | --curl) if ! test $# -ge 2; then err "ctl: http: $1: Missing argument."; fi; curl_options="$curl_options $2"; shift; ;; -h | --help) help_unit_ctl_http; exit 0; ;; ---s | ----sock) local sock="$2"; shift; ;; -*) err "ctl: http: $1: Unknown option."; ;; *) break; ;; esac; shift; done; if ! test $# -ge 1; then err 'ctl: http: METHOD: Missing argument.'; fi; local method="$1"; if ! test $# -ge 2; then err 'ctl: http: PATH: Missing argument.'; fi; local req_path="$2"; curl --fail-with-body $curl_options -X $method -d@- \ $(echo "$sock" | unit_sock_filter -c)${req_path}; } help_unit_ctl_insert() { cat <<__EOF__ ; SYNOPSIS $0 ctl [CTL-OPTS] insert [-h] PATH INDEX DESCRIPTION Insert an element at the specified position (INDEX) into the JSON array located at PATH in unitd(8) control API. The new element is read from standard input. OPTIONS -h, --help Print this help. SEE ALSO $0 ctl http -h; __EOF__ } unit_ctl_insert() { while test $# -ge 1; do case "$1" in -h | --help) help_unit_ctl_insert; exit 0; ;; ---s | ----sock) local sock="$2"; shift; ;; -*) err "ctl: insert: $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 $# -ge 2; then err 'ctl: insert: INDEX: Missing argument.'; fi; local idx="$2"; local old="$(mktemp)"; unit_ctl_http ---s "$sock" -c --no-progress-meter GET "$req_path" \ "$old"; unit_json_ins "$old" "$idx" \ | unit_ctl_http ---s "$sock" PUT "$req_path"; } help_unit_ctl_welcome() { cat <<__EOF__ ; SYNOPSIS $0 welcome [-hn] DESCRIPTION This script tests an NGINX Unit installation by creating an initial configuration and serving a welcome web page. Recommended for first-time users. OPTIONS -h, --help Print this help. -n, --dry-run Dry run. Print the commands to be run instead of actually running them. Each command is preceded by a line explaining what it does. __EOF__ } unit_ctl_welcome() { while test $# -ge 1; do case "$1" in -f | --force) local force='yes'; ;; -h | --help) help_unit_ctl_welcome; exit 0; ;; -n | --dry-run) dry_run='yes'; ;; -*) err "welcome: $1: Unknown option."; ;; *) err "welcome: $1: Unknown argument."; ;; esac; shift; done; command -v curl >/dev/null \ || err 'welcome: curl(1) not found in PATH. It must be installed to run this script.'; www='/srv/www/unit/index.html'; if test -e "$www" && ! test -v force || ! test -w /srv; then www="$HOME/srv/www/unit/index.html"; fi; if test -e "$www" && ! test -v force; then www="$(mktemp)"; mv "$www" "$www.html"; www="$www.html" fi; unit_ps -t m \ | wc -l \ | read -r nprocs \ ||: if test 0 -eq "$nprocs"; then warn "welcome: NGINX Unit isn't running."; warn 'For help with starting NGINX Unit, see:'; err " "; elif test 1 -ne "$nprocs"; then err 'welcome: Only one NGINX Unit instance should be running.'; fi; local sock="$(unit_sock_find)"; local curl_opt="$(unit_sock_find | unit_sock_filter -c)"; curl $curl_opt/ >/dev/null 2>&1 \ || err "welcome: Can't reach the control API socket."; if ! test -v force; then unit_cmd \ | read -r cmd; # Check unitd is not configured already. echo "$cmd" \ | if grep '\--statedir' >/dev/null; then echo "$cmd" \ | sed 's/ --/\n--/g' \ | grep '\--statedir' \ | cut -d' ' -f2; else $cmd --help \ | sed -n '/\--statedir/,+1p' \ | grep 'default:' \ | sed 's/ *default: "\(.*\)"/\1/'; fi \ | sed 's,$,/conf.json,' \ | read -r conffile \ ||:; if test -e $conffile; then if ! unit_ctl_http ---s "$sock" 'GET' '/config' /dev/null | grep -q '^{}.\?$'; # The '.\?' is for the possible carriage return. then warn 'welcome: NGINX Unit is already configured. To overwrite'; err 'its current configuration, run the script again with --force.'; fi; fi; fi; ( unit_freeport \ || err "welcome: Can't find an available port."; ) \ | read -r port; dry_run_echo 'Create a file to serve:'; dry_run_eval "mkdir -p $(dirname $www);"; dry_run_eval "tee '$www' >/dev/null"' <<__EOF__; Welcome to NGINX Unit

Welcome to NGINX Unit

Congratulations! NGINX Unit is installed and running.

Useful Links

Next steps

Check Current Configuration

Unit'"'"'s control API is currently listening for configuration changes on the '"$(unit_sock_find | grep -q '^unix:' && echo 'Unix socket' || echo 'socket')"' at '"$(unit_sock_find)"'
To see the current configuration:

'"${SUDO_USER:+sudo }"'curl '"$curl_opt"'/config

Change Listener Port

This page is served over a random TCP high port. To choose the default HTTP port (80), replace the "listeners" object:

echo '"'"'{"*:80": {"pass": "routes"}}'"'"' | '"${SUDO_USER:+sudo }"'curl -X PUT -d@- '"$curl_opt"'/config/listeners
Then remove the port number from the address bar and reload the page.

NGINX Unit — the universal web app server
NGINX, Inc. © 2024

__EOF__'; dry_run_echo; dry_run_echo 'Give it appropriate permissions:'; dry_run_eval "chmod 644 '$www';"; dry_run_echo; dry_run_echo 'Configure unitd:' dry_run_eval "cat <<__EOF__ \\ | sed 's/8080/$port/' \\ | curl -X PUT -d@- $curl_opt/config; { \"listeners\": { \"*:8080\": { \"pass\": \"routes\" } }, \"routes\": [{ \"action\": { \"share\": \"$www\" } }] } __EOF__"; dry_run_echo; echo; echo 'You may want to try the following commands now:'; echo; echo 'Check out current unitd configuration:'; echo " ${SUDO_USER:+sudo} curl $curl_opt/config"; echo; echo 'Browse the welcome page:'; echo " curl http://localhost:$port/"; } help_unit_freeport() { cat <<__EOF__ ; SYNOPSIS $0 freeport [-h] DESCRIPTION Print an available TCP port. OPTIONS -h, --help Print this help. __EOF__ } unit_freeport() { while test $# -ge 1; do case "$1" in -h | --help) help_unit_freeport; exit 0; ;; -*) err "freeport: $1: Unknown option."; ;; *) err "freeport: $1: Unknown argument."; ;; esac; shift; done; freeport="$(mktemp -t freeport-XXXXXX)"; cat <<__EOF__ \ | cc -x c -o $freeport -; #include #include #include #include #include #include int32_t get_free_port(void); int main(void) { int32_t port; port = get_free_port(); if (port == -1) exit(EXIT_FAILURE); printf("%d\n", port); exit(EXIT_SUCCESS); } int32_t get_free_port(void) { int sfd; int32_t port; socklen_t len; struct sockaddr_in addr; port = -1; sfd = socket(PF_INET, SOCK_STREAM, 0); if (sfd == -1) { perror("socket()"); return -1; } bzero(&addr, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr.sin_port = htons(0); // random port len = sizeof(addr); if (bind(sfd, (struct sockaddr *) &addr, len)) { perror("bind()"); goto fail; } if (getsockname(sfd, (struct sockaddr *) &addr, &len)) { perror("getsockname()"); goto fail; } port = ntohs(addr.sin_port); fail: close(sfd); return port; } __EOF__ $freeport; } help_unit_json_ins() { cat <<__EOF__ ; SYNOPSIS $0 json-ins [-hn] JSON INDEX ARGUMENTS JSON Path to a JSON file containing a top-level array. INDEX Position in the array to insert the element at. DESCRIPTION Insert a JSON element read from standard input into a JSON array read from a file at a given INDEX. The resulting array is printed to standard output. OPTIONS -h, --help Print this help. -n, --dry-run Dry run. Print the command to be run instead of actually running it. __EOF__ } unit_json_ins() { while test $# -ge 1; do case "$1" in -h | --help) help_unit_json_ins; exit 0; ;; -n | --dry-run) dry_run='yes'; ;; -*) err "json-ins: $1: Unknown option."; ;; *) break; ;; esac; shift; done; if ! test $# -ge 1; then err 'json-ins: JSON: Missing argument.'; fi; local arr=$1; if ! test $# -ge 2; then err 'json-ins: INDEX: Missing argument.'; fi; local idx=$2; dry_run_eval "( jq '.[0:$idx]' <'$arr'; echo '['; jq .; echo ']'; jq '.[$idx:]' <'$arr'; ) \\ | sed '/^\[]$/d' \\ | sed '/^]$/{N;s/^]\n\[$/,/}' \\ | jq .;" } help_unit_os_probe() { cat <<__EOF__ ; SYNOPSIS $0 os-probe [-h] DESCRIPTION This script probes the OS and prints three fields, delimited by ':'; the first is the package manager, the second is the OS name, the third is the OS version. OPTIONS -h, --help Print this help. __EOF__ } unit_os_probe() { while test $# -ge 1; do case "$1" in -h | --help) help_unit_os_probe; exit 0; ;; -*) err "os-probe: $1: Unknown option."; ;; *) err "os-probe: $1: Unknown argument."; ;; esac; shift; done; local os=$(uname | tr '[:upper:]' '[:lower:]') if [ "$os" != 'linux' ] && [ "$os" != 'freebsd' ]; then err "os-probe: The OS isn't Linux or FreeBSD; can't proceed." fi if [ "$os" = 'linux' ]; then if command -v apt-get >/dev/null; then local pkgMngr='apt'; elif command -v dnf >/dev/null; then local pkgMngr='dnf'; elif command -v yum >/dev/null; then local pkgMngr='yum'; else local pkgMngr=''; fi; local osRelease='/etc/os-release'; if [ -f "$osRelease" ]; then # The value for the ID and VERSION_ID may or may not be in quotes local osName=$(grep "^ID=" "$osRelease" | sed s/\"//g | awk -F= '{ print $2 }' ||:) local osVersion=$(grep '^VERSION_ID=' "$osRelease" | sed s/\"//g | awk -F= '{ print $2 }' || lsb_release -cs) else err "os-probe: Unable to determine OS and version, or the OS isn't supported." fi else local pkgMngr='pkg'; local osName=$os local osVersion=$(uname -rs | awk -F '[ -]' '{print $2}' ||:) if [ -z "$osVersion" ]; then err 'os-probe: Unable to get the FreeBSD version.' fi fi osName=$(echo "$osName" | tr '[:upper:]' '[:lower:]') echo "$pkgMngr:$osName:$osVersion" } help_unit_ps() { cat <<__EOF__ ; SYNOPSIS $0 ps [-h] [-t TYPE] DESCRIPTION List unitd(8) processes. OPTIONS -h, --help Print this help. -t, --type TYPE List only processes of type TYPE. The available types are: - controller (c) - main (m) - router (r) __EOF__ } unit_ps() { while test $# -ge 1; do case "$1" in -h | --help) help_unit_ps; exit 0; ;; -t | --type) if ! test $# -ge 2; then err "ps: $1: Missing argument."; fi; local type=; case "$2" in c | controller) local type_c='c'; ;; m | main) local type_m='m'; ;; r | router) local type_r='r'; ;; esac; shift; ;; -*) err "ps: $1: Unknown option."; ;; *) err "ps: $1: Unknown argument."; ;; esac; shift; done; ps awwx \ | if test -v type; then grep ${type_c:+-e 'unit: controller'} \ ${type_m:+-e 'unit: main'} \ ${type_r:+-e 'unit: router'}; else grep 'unit: '; fi \ | grep -v grep \ ||: } help_unit_repo_config() { cat <<__EOF__ ; SYNOPSIS $0 repo-config [-hn] [PKG-MANAGER OS-NAME OS-VERSION] DESCRIPTION This script configures the NGINX Unit repository for the system package manager. The script automatically detects the OS and proceeds accordingly. However, if this automatic selection fails, you may specify the package manager and the OS name and version. ARGUMENTS PKG-MANAGER Supported: 'apt', 'dnf', and 'yum'. OS-NAME Supported: 'debian', 'ubuntu', 'fedora', 'rhel', and 'amzn2'. OS-VERSION For most distributions, this should be a numeric value; for Debian derivatives, use the codename instead. OPTIONS -h, --help Print this help. -n, --dry-run Dry run. Print the commands to be run instead of actually running them. Each command is preceded by a line explaining what it does. EXAMPLES $ $(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__ } unit_repo_config() { installAPT () { local os_name="$2"; dry_run_echo "Install on $os_name"; dry_run_echo; dry_run_eval 'curl --output /usr/share/keyrings/nginx-keyring.gpg https://unit.nginx.org/keys/nginx-keyring.gpg;'; dry_run_echo; dry_run_eval 'apt-get install -y apt-transport-https lsb-release ca-certificates;'; if test $# -ge 3; then local os_version="$3"; else local os_version='$(lsb_release -cs)'; fi; dry_run_echo; dry_run_eval "printf 'deb [signed-by=/usr/share/keyrings/nginx-keyring.gpg] https://packages.nginx.org/unit/$os_name/ %s unit\n' \"$os_version\" | tee /etc/apt/sources.list.d/unit.list;"; dry_run_eval "printf 'deb-src [signed-by=/usr/share/keyrings/nginx-keyring.gpg] https://packages.nginx.org/unit/$os_name/ %s unit\n' \"$os_version\" | tee -a /etc/apt/sources.list.d/unit.list;"; dry_run_echo; dry_run_eval 'apt-get update;'; } installYumDnf () { local pkg_mngr="$1"; local os_name="$2"; if test $# -ge 3; then local os_version="$3"; else local os_version='\$releasever'; fi; dry_run_echo "Install on $os_name"; dry_run_echo; dry_run_eval "cat >/etc/yum.repos.d/unit.repo <<__EOF__ [unit] name=unit repo baseurl=https://packages.nginx.org/unit/$os_name/$os_version/\\\$basearch/ gpgcheck=0 enabled=1 __EOF__"; dry_run_echo; dry_run_eval "$pkg_mngr makecache;"; } while test $# -ge 1; do case "$1" in -h | --help) help_unit_repo_config; exit 0; ;; -n | --dry-run) dry_run='yes'; ;; -*) err "repo-config: $1: Unknown option."; ;; *) break; ;; esac; shift; done; if test $# -ge 1; then local pkg_mngr="$1"; if ! test $# -ge 2; then err "repo-config: OS-NAME: Missing argument."; fi; local os_name="$2"; if ! test $# -ge 3; then err "repo-config: OS-VERSION: Missing argument."; fi; local os_version="$3"; fi; command -v curl >/dev/null \ || err 'repo-config: curl(1) not found in PATH. It must be installed to run this script.'; echo 'This script sets up the NGINX Unit repository'; if ! test $# -ge 3; then local os_pkg_name_version=$(unit_os_probe || warn "On macOS, try 'brew install nginx/unit/unit'.") local pkg_mngr=$(echo "$os_pkg_name_version" | awk -F: '{print $1}') local os_name=$(echo "$os_pkg_name_version" | awk -F: '{print $2}') local os_version=$(echo "$os_pkg_name_version" | awk -F: '{print $3}') fi; # Call the appropriate installation function case "$pkg_mngr" in apt) case "$os_name" in debian | ubuntu) installAPT "$pkg_mngr" "$os_name" ${3:+$os_version}; ;; *) err "repo-config: $os_name: The OS isn't supported."; ;; esac ;; yum | dnf) case "$os_name" in rhel | amzn | fedora) installYumDnf "$pkg_mngr" "$os_name" "$os_version" ${3:+ovr}; ;; *) err "repo-config: $os_name: The OS isn't supported."; ;; esac; ;; *) err "repo-config: $pkg_mngr: The package manager isn't supported."; ;; esac; echo echo 'All done; the NGINX Unit repository is set up.'; echo "Configured with '$pkg_mngr' on '$os_name' '$os_version'."; echo 'Further steps: ' } 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 $0 sock [-h] SUBCOMMAND [ARGS] Subcommands ├── filter [-ch] └── find [-h] DESCRIPTION Print the control API socket address of running unitd(8) instances. Run '$0 sock SUBCOMMAND -h' for more information on a subcommand. SUBCOMMANDS filter Filter the output of the 'find' subcommand and transform it to something suitable for running other commands, such as curl(1) or ssh(1). find Find and print the control API socket address of running unitd(8) instances. OPTIONS -h, --help Print this help. __EOF__ } unit_sock() { while test $# -ge 1; do case "$1" in -h | --help) help_unit_sock; exit 0; ;; -*) err "sock: $1: Unknown option."; ;; *) break; ;; esac; shift; done; if ! test $# -ge 1; then err 'sock: Missing subcommand.'; fi; case $1 in filter) shift; unit_sock_filter $@; ;; find) shift; unit_sock_find $@; ;; *) err "sock: $1: Unknown subcommand."; ;; esac; } help_unit_sock_filter() { cat <<__EOF__ ; SYNOPSIS $0 sock filter [-chs] DESCRIPTION Filter the output of the 'sock find' command and transform it to something suitable for running other commands, such as curl(1) or ssh(1). OPTIONS -c, --curl Print an argument suitable for curl(1). -h, --help Print this help. -s, --ssh Print a socket address suitable for use in an ssh(1) tunnel. __EOF__ } unit_sock_filter() { while test $# -ge 1; do case "$1" in -c | --curl) if test -v ssh_flag; then err "sock: filter: $1: Missing argument."; fi; local curl_flag='yes'; ;; -h | --help) help_unit_sock_filter; exit 0; ;; -s | --ssh) if test -v curl_flag; then err "sock: filter: $1: Missing argument."; fi; local ssh_flag='yes'; ;; -*) err "sock: filter: $1: Unknown option."; ;; *) err "sock: filter: $1: Unknown argument."; ;; esac; shift; done; while read -r control; do if test -v curl_flag; then if echo "$control" | grep '^unix:' >/dev/null; then unix_socket="$(echo "$control" | sed 's/unix:/--unix-socket /')"; host='http://localhost'; else unix_socket=''; host="$control"; fi; echo "$unix_socket $host"; elif test -v ssh_flag; then echo "$control" \ | sed -E 's,^(unix:|http://|https://),,'; else echo "$control"; fi; done; } help_unit_sock_find() { cat <<__EOF__ ; SYNOPSIS $0 sock find [-h] DESCRIPTION Find and print the control API socket address of running unitd(8) instances. OPTIONS -h, --help Print this help. __EOF__ } unit_sock_find() { while test $# -ge 1; do case "$1" in -h | --help) help_unit_sock_find; exit 0; ;; -*) err "sock: find: $1: Unknown option."; ;; *) err "sock: find: $1: Unknown argument."; ;; esac; shift; done; unit_cmd \ | while read -r cmd; do if echo "$cmd" | grep '\--control' >/dev/null; then echo "$cmd" \ | sed 's/ --/\n--/g' \ | grep '\--control' \ | cut -d' ' -f2; else if ! command -v $cmd >/dev/null; then local cmd='unitd'; fi; $cmd --help \ | sed -n '/\--control/,+1p' \ | grep 'default:' \ | sed 's/ *default: "\(.*\)"/\1/'; fi; done; } while test $# -ge 1; do case "$1" in -h | --help) help_unit; exit 0; ;; -hh | --help-more) help_more_unit; exit 0; ;; -*) err "$1: Unknown option."; ;; *) break; ;; esac; shift; done; if ! test $# -ge 1; then err "Missing command."; fi; case $1 in cmd) shift; unit_cmd $@; ;; ctl) shift; unit_ctl $@; ;; freeport) shift; unit_freeport $@; ;; json-ins) shift; unit_json_ins $@; ;; os-probe) shift; unit_os_probe $@; ;; ps) shift; unit_ps $@; ;; repo-config) shift; unit_repo_config $@; ;; restart) shift; unit_restart $@; ;; sock) shift; unit_sock $@; ;; welcome) shift; unit_ctl_welcome $@; ;; *) err "$1: Unknown command."; ;; esac;