diff options
Diffstat (limited to 'tools/setup-unit')
-rwxr-xr-x | tools/setup-unit | 1497 |
1 files changed, 1497 insertions, 0 deletions
diff --git a/tools/setup-unit b/tools/setup-unit new file mode 100755 index 00000000..79dab850 --- /dev/null +++ b/tools/setup-unit @@ -0,0 +1,1497 @@ +#!/usr/bin/env bash + +##################################################################### +# +# Copyright (C) NGINX, Inc. +# Author: NGINX Unit Team, F5 Inc. +# +##################################################################### + + +if test -n ${BASH_VERSION} && test "${BASH_VERSINFO[0]}" -eq 3; then + >&2 echo 'Your version of bash(1) isn't supported by this script.'; + >&2 echo "You're probably running on macOS. We recommend that you either"; + >&2 echo 'install a newer version of bash(1) or run this script with'; + >&2 echo 'another shell, such as zsh(1):'; + >&2 echo " $ zsh ${SUDO_USER:+sudo }$0 ..."; + exit 1; +fi; + + +set -Eefuo pipefail; + +test -v BASH_VERSION \ +&& shopt -s lastpipe; + +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] + + 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 '$program_name 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. + + --help-more + Print help for more commands. They are experimental. Using + these isn't recommended, unless you know what you're doing. + +__EOF__ +} + +help_more_unit() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name [-h] COMMAND [ARGS] + + Subcommands + +-- cmd [-h] + +-- ctl [-h] [-s SOCK] SUBCOMMAND [ARGS] + | +-- 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] + +-- 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 '$program_name 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. + + 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). + + --help-more + Print the hidden help with more commands. + +__EOF__ +} + +warn() +{ + >&2 echo "$prog_name: error: $*"; +} + +err() +{ + >&2 echo "$prog_name: 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; +} + + +help_unit_cmd() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name 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 + $program_name ctl [-h] [-s SOCK] SUBCOMMAND [ARGS] + + Subcommands + +-- 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 + subcommand. + +SUBCOMMANDS + 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: + <https://unit.nginx.org/howto/security/#secure-socket-and-stat> + +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 && echo $sock | grep '^ssh://' >/dev/null; then + local remote="$(echo $sock | sed 's,\(ssh://[^/]*\).*,\1,')"; + local sock="$(echo $sock | sed 's,ssh://[^/]*\(.*\),unix:\1,')"; + fi; + + case $1 in + http) + shift; + unit_ctl_http ${remote:+ ---r $remote} ${sock:+ ---s $sock} $@; + ;; + insert) + shift; + unit_ctl_insert ${remote:+ ---r $remote} ${sock:+ ---s $sock} $@; + ;; + *) + err "ctl: $1: Unknown argument."; + ;; + esac; +} + + +help_unit_ctl_http() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name 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 + $program_name ctl http -c --no-progress-meter GET /config >tmp; + +SEE ALSO + <https://unit.nginx.org/controlapi/#api-manipulation> + +__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; + ;; + ---r | ----remote) + local remote="$2"; + shift; + ;; + ---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"; + + 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"; + + elif ! test -v sock; then + local sock="$(unit_sock_find)"; + fi; + + curl $curl_options -X $method -d@- \ + $(echo "$sock" | unit_sock_filter -c)${req_path} \ + ||:; + + if test -v remote; then + ssh -S "$ssh_ctrl" -O exit "$remote" 2>/dev/null; + unlink "$local_sock"; + fi; +} + + +help_unit_ctl_insert() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name 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 + $program_name ctl http -h; + +__EOF__ +} + + +unit_ctl_insert() +{ + while test $# -ge 1; do + case "$1" in + -h | --help) + help_unit_ctl_insert; + exit 0; + ;; + ---r | ----remote) + local remote="$2"; + shift; + ;; + ---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"; + + 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"; + + elif ! test -v sock; then + local sock="$(unit_sock_find)"; + fi; + + local old="$(mktemp ||:)"; + + unit_ctl_http ---s "$sock" -c --no-progress-meter GET "$req_path" \ + </dev/null >"$old" \ + ||:; + + unit_json_ins "$old" "$idx" \ + | unit_ctl_http ---s "$sock" PUT "$req_path" \ + ||:; + + if test -v remote; then + ssh -S "$ssh_ctrl" -O exit "$remote" 2>/dev/null; + unlink "$local_sock"; + fi; +} + + +help_unit_ctl_welcome() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name 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; + + id -u \ + | xargs test 0 -ne \ + && err 'welcome: This script requires root privileges to run.'; + + 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="$(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 " <https://unit.nginx.org/installation/#startup-and-shutdown>"; + 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 '\--state' >/dev/null; then + echo "$cmd" \ + | sed 's/ --/\n--/g' \ + | grep '\--state' \ + | cut -d' ' -f2; + else + $cmd --help \ + | sed -n '/\--state/,+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 2>/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 "cat >'$www'"' <<__EOF__; + <!DOCTYPE html> + <html> + <head> + <title>Welcome to NGINX Unit</title> + <style type="text/css"> + body { background: white; color: black; font-family: sans-serif; margin: 2em; line-height: 1.5; } + h1,h2 { color: #00974d; } + li { margin-bottom: 0.5em; } + pre { background-color: beige; padding: 0.4em; } + hr { margin-top: 2em; border: 1px solid #00974d; } + .indent { margin-left: 1.5em; } + </style> + </head> + <body> + <h1>Welcome to NGINX Unit</h1> + <p>Congratulations! NGINX Unit is installed and running.</p> + <h3>Useful Links</h3> + <ul> + <li><b><a href="https://unit.nginx.org/configuration/?referer=welcome">https://unit.nginx.org/configuration/</a></b><br> + To get started with Unit, see the <em>Configuration</em> docs, starting with + the <em>Quick Start</em> guide.</li> + <li><b><a href="https://github.com/nginx/unit">https://github.com/nginx/unit</a></b><br> + See our GitHub repo to browse the code, contribute, or seek help from the + <a href="https://github.com/nginx/unit#community">community</a>.</li> + </ul> + + <h2>Next steps</h2> + + <h3>Check Current Configuration</h3> + <div class="indent"> + <p>Unit'"'"'s control API is currently listening for configuration changes + on the '"$(unit_sock_find | grep -q '^unix:' && echo '<a href="https://en.wikipedia.org/wiki/Unix_domain_socket">Unix socket</a>' || echo 'socket')"' at + <b>'"$(unit_sock_find)"'</b><br> + To see the current configuration:</p> + <pre>'"${SUDO_USER:+sudo }"'curl '"$curl_opt"'/config</pre> + </div> + + <h3>Change Listener Port</h3> + <div class="indent"> + <p>This page is served over a random TCP high port. To choose the default HTTP port (80), + replace the <b>"listeners"</b> object:</p> + <pre>echo '"'"'{"*:80": {"pass": "routes"}}'"'"' | '"${SUDO_USER:+sudo }"'curl -X PUT -d@- '"$curl_opt"'/config/listeners</pre> + Then remove the port number from the address bar and reload the page. + </div> + + <hr> + <p><a href="https://unit.nginx.org/?referer=welcome">NGINX Unit — the universal web app server</a><br> + NGINX, Inc. © 2022</p> + </body> + </html> +__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 + $program_name 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 <netinet/in.h> + #include <stdio.h> + #include <stdlib.h> + #include <strings.h> + #include <sys/socket.h> + #include <unistd.h> + + + 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 + $program_name 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 + $program_name 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 + $program_name 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 ax \ + | 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 + $program_name 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 + $ $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; + +__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.'; + + id -u \ + | xargs test 0 -ne \ + && err 'repo-config: This script requires root privileges to run.'; + + 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: <https://unit.nginx.org/installation/#official-packages>' +} + + +help_unit_sock() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name sock [-h] SUBCOMMAND [ARGS] + + Subcommands + +-- filter [-ch] + +-- find [-h] + +DESCRIPTION + Print the control API socket address of running unitd(8) + instances. + + Run '$program_name 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 + $program_name 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 + $program_name 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; + ;; + --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 $@; + ;; +sock) + shift; + unit_sock $@; + ;; +welcome) + shift; + unit_ctl_welcome $@; + ;; +*) + err "$1: Unknown command."; + ;; +esac; |