| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029 | #!/usr/bin/env bash# SPDX-License-Identifier: AGPL-3.0-or-later# shellcheck disable=SC2001# Script options from the environment:SEARXNG_UWSGI_USE_SOCKET="${SEARXNG_UWSGI_USE_SOCKET:-true}"# shellcheck source=utils/lib.shsource "$(dirname "${BASH_SOURCE[0]}")/lib.sh"# shellcheck source=utils/lib_redis.shsource "$(dirname "${BASH_SOURCE[0]}")/lib_redis.sh"# shellcheck source=utils/brand.envsource "${REPO_ROOT}/utils/brand.env"SERVICE_NAME="searxng"SERVICE_USER="searxng"SERVICE_HOME="/usr/local/searxng"SERVICE_GROUP="searxng"SEARXNG_SRC="${SERVICE_HOME}/searxng-src"# shellcheck disable=SC2034SEARXNG_STATIC="${SEARXNG_SRC}/searx/static"SEARXNG_PYENV="${SERVICE_HOME}/searx-pyenv"SEARXNG_SETTINGS_PATH="/etc/searxng/settings.yml"SEARXNG_UWSGI_APP="searxng.ini"SEARXNG_INTERNAL_HTTP="${SEARXNG_BIND_ADDRESS}:${SEARXNG_PORT}"if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then    SEARXNG_UWSGI_SOCKET="${SERVICE_HOME}/run/socket"else    SEARXNG_UWSGI_SOCKET=fi# SEARXNG_URL: the public URL of the instance (https://example.org/searxng).  The# value is taken from environment ${SEARXNG_URL} in ./utils/brand.env.  This# variable is an empty string if server.base_url in the settings.yml is set to# 'false'.SEARXNG_URL="${SEARXNG_URL:-http://$(uname -n)/searxng}"SEARXNG_URL="${SEARXNG_URL%/}" # if exists, remove trailing slashif in_container; then    # hint: Linux containers do not have DNS entries, lets use IPs    SEARXNG_URL="http://$(primary_ip)/searxng"fiSEARXNG_URL_PATH="$(echo "${SEARXNG_URL}" | sed -e 's,^.*://[^/]*\(/.*\),\1,g')"[[ "${SEARXNG_URL_PATH}" == "${SEARXNG_URL}" ]] && SEARXNG_URL_PATH=/# Apache settingsAPACHE_SEARXNG_SITE="searxng.conf"# nginx settingsNGINX_SEARXNG_SITE="searxng.conf"# apt packagesSEARXNG_PACKAGES_debian="\python3-dev python3-babel python3-venvuwsgi uwsgi-plugin-python3git build-essential libxslt-dev zlib1g-dev libffi-dev libssl-dev"SEARXNG_BUILD_PACKAGES_debian="\firefox graphviz imagemagick texlive-xetex librsvg2-bintexlive-latex-recommended texlive-extra-utils fonts-dejavulatexmk shellcheck"# pacman packagesSEARXNG_PACKAGES_arch="\python python-pip python-lxml python-babeluwsgi uwsgi-plugin-pythongit base-devel libxml2"SEARXNG_BUILD_PACKAGES_arch="\firefox graphviz imagemagick texlive-bin extra/librsvgtexlive-core texlive-latexextra ttf-dejavu shellcheck"# dnf packagesSEARXNG_PACKAGES_fedora="\python python-pip python-lxml python-babel python3-develuwsgi uwsgi-plugin-python3git @development-tools libxml2 openssl"SEARXNG_BUILD_PACKAGES_fedora="\firefox graphviz graphviz-gd ImageMagick librsvg2-toolstexlive-xetex-bin texlive-collection-fontsrecommendedtexlive-collection-latex dejavu-sans-fonts dejavu-serif-fontsdejavu-sans-mono-fonts ShellCheck"case $DIST_ID-$DIST_VERS in    ubuntu-18.04)        SEARXNG_PACKAGES="${SEARXNG_PACKAGES_debian}"        SEARXNG_BUILD_PACKAGES="${SEARXNG_BUILD_PACKAGES_debian}"        APACHE_PACKAGES="$APACHE_PACKAGES libapache2-mod-proxy-uwsgi"        ;;    ubuntu-20.04)        # https://wiki.ubuntu.com/FocalFossa/ReleaseNotes#Python3_by_default        SEARXNG_PACKAGES="${SEARXNG_PACKAGES_debian} python-is-python3"        SEARXNG_BUILD_PACKAGES="${SEARXNG_BUILD_PACKAGES_debian}"        ;;    ubuntu-*|debian-*)        SEARXNG_PACKAGES="${SEARXNG_PACKAGES_debian}"        SEARXNG_BUILD_PACKAGES="${SEARXNG_BUILD_PACKAGES_debian}"        ;;    arch-*)        SEARXNG_PACKAGES="${SEARXNG_PACKAGES_arch}"        SEARXNG_BUILD_PACKAGES="${SEARXNG_BUILD_PACKAGES_arch}"        ;;    fedora-*)        SEARXNG_PACKAGES="${SEARXNG_PACKAGES_fedora}"        SEARXNG_BUILD_PACKAGES="${SEARXNG_BUILD_PACKAGES_fedora}"        ;;esac_service_prefix="  ${_Yellow}|${SERVICE_USER}|${_creset} "# ----------------------------------------------------------------------------usage() {# ----------------------------------------------------------------------------    # shellcheck disable=SC1117    cat <<EOFusage:  $(basename "$0") install    [all|user|pyenv|settings|uwsgi|redis|nginx|apache|searxng-src|packages|buildhost]  $(basename "$0") remove     [all|user|pyenv|settings|uwsgi|redis|nginx|apache]  $(basename "$0") instance   [cmd|update|check|localtest|inspect]install|remove:  all           : complete (de-) installation of the SearXNG service  user          : service user '${SERVICE_USER}' (${SERVICE_HOME})  pyenv         : virtualenv (python) in ${SEARXNG_PYENV}  settings      : settings from ${SEARXNG_SETTINGS_PATH}  uwsgi         : SearXNG's uWSGI app ${SEARXNG_UWSGI_APP}  redis         : build & install or remove a local redis server ${REDIS_HOME}/run/redis.sock  nginx         : HTTP site ${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE}  apache        : HTTP site ${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}install:  searxng-src   : clone ${GIT_URL} into ${SEARXNG_SRC}  packages      : installs packages from OS package manager required by SearXNG  buildhost     : installs packages from OS package manager required by a SearXNG buildhostinstance:  update        : update SearXNG instance (git fetch + reset & update settings.yml)  check         : run checks from utils/searxng_check.py in the active installation  inspect       : run some small tests and inspect SearXNG's server status and log  get_setting   : get settings value from running SearXNG instance  cmd           : run command in SearXNG instance's environment (e.g. bash)EOF    searxng.instance.env    [[ -n ${1} ]] &&  err_msg "$1"}searxng.instance.env() {    echo "uWSGI:"    if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then        echo "  SEARXNG_UWSGI_SOCKET : ${SEARXNG_UWSGI_SOCKET}"    else        echo "  SEARXNG_INTERNAL_HTTP: ${SEARXNG_INTERNAL_HTTP}"    fi    cat <<EOFenvironment ${SEARXNG_SRC}/utils/brand.env:  GIT_URL              : ${GIT_URL}  GIT_BRANCH           : ${GIT_BRANCH}  SEARXNG_URL          : ${SEARXNG_URL}  SEARXNG_PORT         : ${SEARXNG_PORT}  SEARXNG_BIND_ADDRESS : ${SEARXNG_BIND_ADDRESS}EOF}main() {    required_commands \        sudo systemctl install git wget curl \        || exit    local _usage="unknown or missing $1 command $2"    case $1 in        --getenv)  var="$2"; echo "${!var}"; exit 0;;        -h|--help) usage; exit 0;;        install)            sudo_or_exit            case $2 in                all) searxng.install.all;;                user) searxng.install.user;;                pyenv) searxng.install.pyenv;;                searxng-src) searxng.install.clone;;                settings) searxng.install.settings;;                uwsgi) searxng.install.uwsgi;;                packages) searxng.install.packages;;                buildhost) searxng.install.buildhost;;                nginx) searxng.nginx.install;;                apache) searxng.apache.install;;                redis) searxng.install.redis;;                *) usage "$_usage"; exit 42;;            esac            ;;        remove)            sudo_or_exit            case $2 in                all) searxng.remove.all;;                user) drop_service_account "${SERVICE_USER}";;                pyenv) searxng.remove.pyenv;;                settings) searxng.remove.settings;;                uwsgi) searxng.remove.uwsgi;;                apache) searxng.apache.remove;;                remove) searxng.nginx.remove;;                redis) searxng.remove.redis;;                *) usage "$_usage"; exit 42;;            esac            ;;        instance)            case $2 in                update)                    sudo_or_exit                    searxng.instance.update                    ;;                check)                    sudo_or_exit                    searxng.instance.self.call searxng.check                    ;;                inspect)                    sudo_or_exit                    searxng.instance.inspect                    ;;                cmd)                    sudo_or_exit                    shift; shift; searxng.instance.exec "$@"                    ;;                get_setting)                    shift; shift; searxng.instance.get_setting "$@"                    ;;                call)                    # call a function in instance's environment                    shift; shift; searxng.instance.self.call "$@"                    ;;                _call)                    shift; shift; "$@"                    ;;                *) usage "$_usage"; exit 42;;            esac            ;;        *)            local cmd="$1"            _type="$(type -t "$cmd")"            if [ "$_type" != 'function' ]; then                usage "unknown or missing command $1"                exit 42            else                "$cmd" "$@"            fi            ;;    esac}searxng.install.all() {    rst_title "SearXNG installation" part    local redis_url    rst_title "SearXNG"    searxng.install.packages    wait_key 10    searxng.install.user    wait_key 10    searxng.install.clone    wait_key    searxng.install.pyenv    wait_key    searxng.install.settings    wait_key    searxng.instance.localtest    wait_key    searxng.install.uwsgi    wait_key    rst_title "Redis DB"    searxng.install.redis.db    rst_title "HTTP Server"    searxng.install.http.site    rst_title "Finalize installation"    if ask_yn "Do you want to run some checks?" Yn; then        searxng.instance.self.call searxng.check    fi}searxng.install.redis.db() {    local redis_url    redis_url=$(searxng.instance.get_setting redis.url)    rst_para "\In your instance, redis DB connector is configured at:    ${redis_url}"    if searxng.instance.exec python -c "from searx import redisdb; redisdb.initialize() or exit(42)"; then        info_msg "SearXNG instance is able to connect redis DB."        return    fi    if ! [[ ${redis_url} = unix://${REDIS_HOME}/run/redis.sock* ]]; then        err_msg "SearXNG instance can't connect redis DB / check redis & your settings"        return    fi    rst_para ".. but this redis DB is not installed yet."    case $DIST_ID-$DIST_VERS in        fedora-*)            # Fedora runs uWSGI in emperor-tyrant mode: in Tyrant mode the            # Emperor will run the vassal using the UID/GID of the vassal            # configuration file [1] (user and group of the app .ini file).            #            # HINT: without option ``emperor-tyrant-initgroups=true`` in            # ``/etc/uwsgi.ini`` the process won't get the additional groups,            # but this option is not available in 2.0.x branch [2][3] / on            # fedora35 there is v2.0.20 installed --> no way to get additional            # groups on fedora's tyrant mode.            #            # ERROR:searx.redisdb: [searxng (993)] can't connect redis DB ...            # ERROR:searx.redisdb:   Error 13 connecting to unix socket: /usr/local/searxng-redis/run/redis.sock. Permission denied.            # ERROR:searx.plugins.limiter: init limiter DB failed!!!            #            # $ ps -aef | grep '/usr/sbin/uwsgi --ini searxng.ini'            # searxng       93      92  0 12:43 ?        00:00:00 /usr/sbin/uwsgi --ini searxng.ini            # searxng      186      93  0 12:44 ?        00:00:01 /usr/sbin/uwsgi --ini searxng.ini            #            # Additional groups:            #            # $ groups searxng            # searxng : searxng searxng-redis            #            # Here you can see that the additional "Groups" of PID 186 are unset            # (missing gid of searxng-redis)            #            # $ cat /proc/186/task/186/status            # ...            # Uid:      993     993     993     993            # Gid:      993     993     993     993            # FDSize:   128            # Groups:            # ...            #            # [1] https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html#tyrant-mode-secure-multi-user-hosting            # [2] https://github.com/unbit/uwsgi/issues/2099            # [3] https://github.com/unbit/uwsgi/pull/752            rst_para "\Fedora uses emperor-tyrant mode / in this mode we had a lot of trouble withsockets and permissions of the vasals.  We recommend to setup a redis DBand using redis:// TCP protocol in the settings.yml configuration."            ;;        *)            if ask_yn "Do you want to install the redis DB now?" Yn; then                searxng.install.redis                uWSGI_restart "$SEARXNG_UWSGI_APP"            fi            ;;    esac}searxng.install.http.site() {    if apache_is_installed; then        info_msg "Apache is installed on this host."        if ask_yn "Do you want to install a reverse proxy" Yn; then            searxng.apache.install        fi    elif nginx_is_installed; then        info_msg "Nginx is installed on this host."        if ask_yn "Do you want to install a reverse proxy" Yn; then            searxng.nginx.install        fi    else        info_msg "Don't forget to install HTTP site."    fi}searxng.remove.all() {    local redis_url    rst_title "De-Install SearXNG (service)"    if ! ask_yn "Do you really want to deinstall SearXNG?"; then        return    fi    redis_url=$(searxng.instance.get_setting redis.url)    if ! [[ ${redis_url} = unix://${REDIS_HOME}/run/redis.sock* ]]; then        searxng.remove.redis    fi    searxng.remove.uwsgi    drop_service_account "${SERVICE_USER}"    searxng.remove.settings    wait_key    if service_is_available "${SEARXNG_URL}"; then        MSG="** Don't forget to remove your public site! (${SEARXNG_URL}) **" wait_key 10    fi}searxng.install.user() {    rst_title "SearXNG -- install user" section    echo    if getent passwd "${SERVICE_USER}"  > /dev/null; then       echo "user already exists"       return 0    fi    tee_stderr 1 <<EOF | bash | prefix_stdoutuseradd --shell /bin/bash --system \ --home-dir "${SERVICE_HOME}" \ --comment 'Privacy-respecting metasearch engine' ${SERVICE_USER}mkdir "${SERVICE_HOME}"chown -R "${SERVICE_GROUP}:${SERVICE_GROUP}" "${SERVICE_HOME}"groups ${SERVICE_USER}EOF}searxng.install.packages() {    TITLE="SearXNG -- install packages" pkg_install "${SEARXNG_PACKAGES}"}searxng.install.buildhost() {    TITLE="SearXNG -- install buildhost packages" pkg_install \         "${SEARXNG_PACKAGES} ${SEARXNG_BUILD_PACKAGES}"}searxng.install.clone() {    rst_title "Clone SearXNG sources" section    if ! service_account_is_available "${SERVICE_USER}"; then        die 42 "To clone SearXNG, first install user ${SERVICE_USER}."    fi    echo    if ! sudo -i -u "${SERVICE_USER}" ls -d "$REPO_ROOT" > /dev/null; then        die 42 "user '${SERVICE_USER}' missed read permission: $REPO_ROOT"    fi    # SERVICE_HOME="$(sudo -i -u "${SERVICE_USER}" echo \$HOME 2>/dev/null)"    if [[ ! "${SERVICE_HOME}" ]]; then        err_msg "to clone SearXNG sources, user ${SERVICE_USER} hast to be created first"        return 42    fi    if [[ ! $(git show-ref "refs/heads/${GIT_BRANCH}") ]]; then        warn_msg "missing local branch ${GIT_BRANCH}"        info_msg "create local branch ${GIT_BRANCH} from start point: origin/${GIT_BRANCH}"        git branch "${GIT_BRANCH}" "origin/${GIT_BRANCH}"    fi    if [[ ! $(git rev-parse --abbrev-ref HEAD) == "${GIT_BRANCH}" ]]; then        warn_msg "take into account, installing branch $GIT_BRANCH while current branch is $(git rev-parse --abbrev-ref HEAD)"    fi    # export SERVICE_HOME    # clone repo and add a safe.directory entry to git's system config / see    # https://github.com/searxng/searxng/issues/1251    git_clone "$REPO_ROOT" "${SEARXNG_SRC}" \              "$GIT_BRANCH" "${SERVICE_USER}"    git config --system --add safe.directory "${SEARXNG_SRC}"    pushd "${SEARXNG_SRC}" > /dev/null    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"cd "${SEARXNG_SRC}"git remote set-url origin ${GIT_URL}git config user.email "${ADMIN_EMAIL}"git config user.name "${ADMIN_NAME}"git config --listEOF    popd > /dev/null}searxng.install.link_src() {    rst_title "link SearXNG's sources to: $2" chapter    echo    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"mv -f "${SEARXNG_SRC}" "${SEARXNG_SRC}.backup"ln -s "${2}" "${SEARXNG_SRC}"ls -ld /usr/local/searxng/searxng-srcEOF    echo    uWSGI_restart "$SEARXNG_UWSGI_APP"}searxng.install.pyenv() {    rst_title "Create virtualenv (python)" section    echo    if [[ ! -f "${SEARXNG_SRC}/manage" ]]; then        die 42 "To create pyenv for SearXNG, first install searxng-src."    fi    info_msg "create pyenv in ${SEARXNG_PYENV}"    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"rm -rf "${SEARXNG_PYENV}"python3 -m venv "${SEARXNG_PYENV}"grep -qFs -- 'source ${SEARXNG_PYENV}/bin/activate' ~/.profile \  || echo 'source ${SEARXNG_PYENV}/bin/activate' >> ~/.profileEOF    info_msg "inspect python's virtual environment"    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"command -v python && python --versionEOF    wait_key    info_msg "install needed python packages"    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"pip install -U pippip install -U setuptoolspip install -U wheelpip install -U pyyamlcd ${SEARXNG_SRC}pip install -e .EOF}searxng.remove.pyenv() {    rst_title "Remove virtualenv (python)" section    if ! ask_yn "Do you really want to drop ${SEARXNG_PYENV} ?"; then        return    fi    info_msg "remove pyenv activation from ~/.profile"    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"grep -v 'source ${SEARXNG_PYENV}/bin/activate' ~/.profile > ~/.profile.##mv ~/.profile.## ~/.profileEOF    rm -rf "${SEARXNG_PYENV}"}searxng.install.settings() {    rst_title "install ${SEARXNG_SETTINGS_PATH}" section    if ! [[ -f "${SEARXNG_SRC}/.git/config" ]]; then        die "Before install settings, first install SearXNG."        exit 42    fi    mkdir -p "$(dirname "${SEARXNG_SETTINGS_PATH}")"    DEFAULT_SELECT=1 \                  install_template --no-eval \                  "${SEARXNG_SETTINGS_PATH}" \                  "${SERVICE_USER}" "${SERVICE_GROUP}"    tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 | prefix_stdout "root"sed -i -e "s/ultrasecretkey/$(openssl rand -hex 16)/g" "${SEARXNG_SETTINGS_PATH}"EOF}searxng.remove.settings() {    rst_title "remove ${SEARXNG_SETTINGS_PATH}" section    if ask_yn "Do you want to delete the SearXNG settings?" Yn; then        rm -f "${SEARXNG_SETTINGS_PATH}"    fi}searxng.check() {    rst_title "SearXNG checks" section    for NAME in "searx" "filtron" "morty"; do        if service_account_is_available "${NAME}"; then            err_msg "There exists an old '${NAME}' account from a previous installation."        else            info_msg "[OK] (old) account '${NAME}' does not exists"        fi    done    "${SEARXNG_PYENV}/bin/python" "${SEARXNG_SRC}/utils/searxng_check.py"}searxng.instance.update() {    rst_title "Update SearXNG instance"    rst_para "fetch from $GIT_URL and reset to origin/$GIT_BRANCH"    tee_stderr 0.3 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"cd ${SEARXNG_SRC}git fetch origin "$GIT_BRANCH"git reset --hard "origin/$GIT_BRANCH"pip install -U pippip install -U setuptoolspip install -U wheelpip install -U pyyamlpip install -U -e .EOF    rst_para "update instance's settings.yml from ${SEARXNG_SETTINGS_PATH}"    DEFAULT_SELECT=2 \                  install_template --no-eval \                  "${SEARXNG_SETTINGS_PATH}" \                  "${SERVICE_USER}" "${SERVICE_GROUP}"    sudo -H -i <<EOFsed -i -e "s/ultrasecretkey/$(openssl rand -hex 16)/g" "${SEARXNG_SETTINGS_PATH}"EOF    uWSGI_restart "${SEARXNG_UWSGI_APP}"}searxng.install.uwsgi() {    rst_title "SearXNG (install uwsgi)"    install_uwsgi    if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then        searxng.install.uwsgi.socket    else        searxng.install.uwsgi.http    fi}searxng.install.uwsgi.http() {    rst_para "Install ${SEARXNG_UWSGI_APP} at: http://${SEARXNG_INTERNAL_HTTP}"    uWSGI_install_app "${SEARXNG_UWSGI_APP}"    if ! searxng.uwsgi.available; then        err_msg "URL http://${SEARXNG_INTERNAL_HTTP} not available, check SearXNG & uwsgi setup!"    fi}searxng.install.uwsgi.socket() {    rst_para "Install ${SEARXNG_UWSGI_APP} using socket at: ${SEARXNG_UWSGI_SOCKET}"    mkdir -p "$(dirname ${SEARXNG_UWSGI_SOCKET})"    chown -R "${SERVICE_USER}:${SERVICE_GROUP}" "$(dirname ${SEARXNG_UWSGI_SOCKET})"    case $DIST_ID-$DIST_VERS in        fedora-*)            # Fedora runs uWSGI in emperor-tyrant mode: in Tyrant mode the            # Emperor will run the vassal using the UID/GID of the vassal            # configuration file [1] (user and group of the app .ini file).            # [1] https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html#tyrant-mode-secure-multi-user-hosting            uWSGI_install_app --variant=socket  "${SEARXNG_UWSGI_APP}" "${SERVICE_USER}" "${SERVICE_GROUP}"            ;;        *)            uWSGI_install_app --variant=socket  "${SEARXNG_UWSGI_APP}"            ;;    esac    sleep 5    if ! searxng.uwsgi.available; then        err_msg "uWSGI socket not available at: ${SEARXNG_UWSGI_SOCKET}"    fi}searxng.uwsgi.available() {    if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then        [[ -S "${SEARXNG_UWSGI_SOCKET}" ]]        exit_val=$?        if [[ $exit_val = 0 ]]; then            info_msg "uWSGI socket is located at: ${SEARXNG_UWSGI_SOCKET}"        fi    else        service_is_available "http://${SEARXNG_INTERNAL_HTTP}"        exit_val=$?    fi    return "$exit_val"}searxng.remove.uwsgi() {    rst_title "Remove SearXNG's uWSGI app (${SEARXNG_UWSGI_APP})" section    echo    uWSGI_remove_app "${SEARXNG_UWSGI_APP}"}searxng.install.redis() {    rst_title "SearXNG (install redis)"    redis.build    redis.install    redis.addgrp "${SERVICE_USER}"}searxng.remove.redis() {    rst_title "SearXNG (remove redis)"    redis.rmgrp "${SERVICE_USER}"    redis.remove}searxng.instance.localtest() {    rst_title "Test SearXNG instance locally" section    rst_para "Activate debug mode, start a minimal SearXNG "\             "service and debug a HTTP request/response cycle."    if service_is_available "http://${SEARXNG_INTERNAL_HTTP}" &>/dev/null; then        err_msg "URL/port http://${SEARXNG_INTERNAL_HTTP} is already in use, you"        err_msg "should stop that service before starting local tests!"        if ! ask_yn "Continue with local tests?"; then            return        fi    fi    echo    searxng.instance.debug.on    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"export SEARXNG_SETTINGS_PATH="${SEARXNG_SETTINGS_PATH}"cd ${SEARXNG_SRC}timeout 10 python searx/webapp.py &sleep 3curl --location --verbose --head --insecure ${SEARXNG_INTERNAL_HTTP}EOF    echo    searxng.instance.debug.off}searxng.install.http.pre() {    if ! searxng.uwsgi.available; then        rst_para "\To install uWSGI use::    $(basename "$0") install uwsgi"        die 42 "SearXNG's uWSGI app not available"    fi    if ! searxng.instance.exec python -c "from searx.shared import redisdb; redisdb.initialize() or exit(42)"; then        rst_para "\The configured redis DB is not available: If your server is public to theinternet, you should setup a bot protection to block excessively bot queries.Bot protection requires a redis DB.  About bot protection visit the officialSearXNG documentation and query for the word 'limiter'."    fi}searxng.apache.install() {    rst_title "Install Apache site ${APACHE_SEARXNG_SITE}"    rst_para "\This installs SearXNG's uWSGI app as apache site.  The apache site is located at:${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}."    searxng.install.http.pre    if ! apache_is_installed; then        err_msg "Apache packages are not installed"        if ! ask_yn "Do you really want to continue and install apache packages?" Yn; then            return        else            FORCE_SELECTION=Y install_apache        fi    else        info_msg "Apache packages are installed [OK]"    fi    if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then        apache_install_site --variant=socket "${APACHE_SEARXNG_SITE}"    else        apache_install_site "${APACHE_SEARXNG_SITE}"    fi    if ! service_is_available "${SEARXNG_URL}"; then        err_msg "Public service at ${SEARXNG_URL} is not available!"    fi}searxng.apache.remove() {    rst_title "Remove Apache site ${APACHE_SEARXNG_SITE}"    rst_para "\This removes apache site ${APACHE_SEARXNG_SITE}::  ${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}"    ! apache_is_installed && err_msg "Apache is not installed."    if ! ask_yn "Do you really want to continue?" Yn; then        return    fi    apache_remove_site "${APACHE_SEARXNG_SITE}"}searxng.nginx.install() {    rst_title "Install nginx site ${NGINX_SEARXNG_SITE}"    rst_para "\This installs SearXNG's uWSGI app as Nginx site.  The Nginx site is located at:${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE} and requires a uWSGI."    searxng.install.http.pre    if ! nginx_is_installed ; then        err_msg "Nginx packages are not installed"        if ! ask_yn "Do you really want to continue and install Nginx packages?" Yn; then            return        else            FORCE_SELECTION=Y install_nginx        fi    else        info_msg "Nginx packages are installed [OK]"    fi    if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then        nginx_install_app --variant=socket "${NGINX_SEARXNG_SITE}"    else        nginx_install_app "${NGINX_SEARXNG_SITE}"    fi    if ! service_is_available "${SEARXNG_URL}"; then        err_msg "Public service at ${SEARXNG_URL} is not available!"    fi}searxng.nginx.remove() {    rst_title "Remove Nginx site ${NGINX_SEARXNG_SITE}"    rst_para "\This removes Nginx site ${NGINX_SEARXNG_SITE}::  ${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE}"    ! nginx_is_installed && err_msg "Nginx is not installed."    if ! ask_yn "Do you really want to continue?" Yn; then        return    fi    nginx_remove_app "${NGINX_SEARXNG_SITE}"}searxng.instance.exec() {    if ! service_account_is_available "${SERVICE_USER}"; then        die 42 "can't execute: instance does not exist (missed account ${SERVICE_USER})"    fi    sudo -H -i -u "${SERVICE_USER}" \         SEARXNG_UWSGI_USE_SOCKET="${SEARXNG_UWSGI_USE_SOCKET}" \         "$@"}searxng.instance.self.call() {    # wrapper to call a function in instance's environment    info_msg "wrapper:  utils/searxng.sh instance _call $*"    searxng.instance.exec "${SEARXNG_SRC}/utils/searxng.sh" instance _call "$@"}searxng.instance.get_setting() {    searxng.instance.exec python <<EOFfrom searx import get_settingprint(get_setting('$1'))EOF}searxng.instance.debug.on() {    warn_msg "Do not enable debug in a production environment!"    info_msg "try to enable debug mode ..."    tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 |  prefix_stdout "$_service_prefix"cd ${SEARXNG_SRC}sed -i -e "s/debug: false/debug: true/g" "$SEARXNG_SETTINGS_PATH"EOF    uWSGI_restart "$SEARXNG_UWSGI_APP"}searxng.instance.debug.off() {    info_msg "try to disable debug mode ..."    tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 |  prefix_stdout "$_service_prefix"cd ${SEARXNG_SRC}sed -i -e "s/debug: true/debug: false/g" "$SEARXNG_SETTINGS_PATH"EOF    uWSGI_restart "$SEARXNG_UWSGI_APP"}searxng.instance.inspect() {    rst_title "Inspect SearXNG instance"    echo    searxng.instance.self.call _searxng.instance.inspect    local _debug_on    if ask_yn "Enable SearXNG debug mode?"; then        searxng.instance.debug.on        _debug_on=1    fi    echo    case $DIST_ID-$DIST_VERS in        ubuntu-*|debian-*)            # For uWSGI debian uses the LSB init process; for each configuration            # file new uWSGI daemon instance is started with additional option.            service uwsgi status "${SERVICE_NAME}"            ;;        arch-*)            systemctl --no-pager -l status "uwsgi@${SERVICE_NAME%.*}"            ;;        fedora-*)            systemctl --no-pager -l status uwsgi            ;;    esac    echo -e  "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"    read -r -s -n1 -t 5    echo    while true;  do        trap break 2        case $DIST_ID-$DIST_VERS in            ubuntu-*|debian-*) tail -f "/var/log/uwsgi/app/${SERVICE_NAME%.*}.log" ;;            arch-*)  journalctl -f -u "uwsgi@${SERVICE_NAME%.*}" ;;            fedora-*)  journalctl -f -u uwsgi ;;        esac    done    if [[ $_debug_on == 1 ]]; then        searxng.instance.debug.off    fi    return 0}_searxng.instance.inspect() {    searxng.instance.env    if in_container; then        # shellcheck source=utils/lxc-searxng.env        source "${REPO_ROOT}/utils/lxc-searxng.env"        lxc_suite_info    fi    MSG="${_Green}[${_BCyan}CTRL-C${_Green}] to stop or [${_BCyan}KEY${_Green}] to continue${_creset}"    if ! searxng.uwsgi.available; then        err_msg "SearXNG's uWSGI app not available"        wait_key    fi    if ! service_is_available "${SEARXNG_URL}"; then        err_msg "Public service at ${SEARXNG_URL} is not available!"        wait_key    fi}searxng.doc.rst() {    local debian="${SEARXNG_PACKAGES_debian}"    local arch="${SEARXNG_PACKAGES_arch}"    local fedora="${SEARXNG_PACKAGES_fedora}"    local debian_build="${SEARXNG_BUILD_PACKAGES_debian}"    local arch_build="${SEARXNG_BUILD_PACKAGES_arch}"    local fedora_build="${SEARXNG_BUILD_PACKAGES_fedora}"    debian="$(echo "${debian}" | sed 's/.*/          & \\/' | sed '$ s/.$//')"    arch="$(echo "${arch}"     | sed 's/.*/          & \\/' | sed '$ s/.$//')"    fedora="$(echo "${fedora}" | sed 's/.*/          & \\/' | sed '$ s/.$//')"    debian_build="$(echo "${debian_build}" | sed 's/.*/          & \\/' | sed '$ s/.$//')"    arch_build="$(echo "${arch_build}"     | sed 's/.*/          & \\/' | sed '$ s/.$//')"    fedora_build="$(echo "${fedora_build}" | sed 's/.*/          & \\/' | sed '$ s/.$//')"    if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then        uwsgi_variant=':socket'    else        uwsgi_variant=':socket'    fi    eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/searxng.rst")\""    # I use ubuntu-20.04 here to demonstrate that versions are also supported,    # normally debian-* and ubuntu-* are most the same.    for DIST_NAME in ubuntu-20.04 arch fedora; do        (            DIST_ID=${DIST_NAME%-*}            DIST_VERS=${DIST_NAME#*-}            [[ $DIST_VERS =~ $DIST_ID ]] && DIST_VERS=            uWSGI_distro_setup            echo -e "\n.. START searxng uwsgi-description $DIST_NAME"            case $DIST_ID-$DIST_VERS in                ubuntu-*|debian-*)  cat <<EOF.. code:: bash   # init.d --> /usr/share/doc/uwsgi/README.Debian.gz   # For uWSGI debian uses the LSB init process, this might be changed   # one day, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=833067   create     ${uWSGI_APPS_AVAILABLE}/${SEARXNG_UWSGI_APP}   enable:    sudo -H ln -s ${uWSGI_APPS_AVAILABLE}/${SEARXNG_UWSGI_APP} ${uWSGI_APPS_ENABLED}/   start:     sudo -H service uwsgi start   ${SEARXNG_UWSGI_APP%.*}   restart:   sudo -H service uwsgi restart ${SEARXNG_UWSGI_APP%.*}   stop:      sudo -H service uwsgi stop    ${SEARXNG_UWSGI_APP%.*}   disable:   sudo -H rm ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}EOF                ;;                arch-*) cat <<EOF.. code:: bash   # systemd --> /usr/lib/systemd/system/uwsgi@.service   # For uWSGI archlinux uses systemd template units, see   # - http://0pointer.de/blog/projects/instances.html   # - https://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd   create:    ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}   enable:    sudo -H systemctl enable   uwsgi@${SEARXNG_UWSGI_APP%.*}   start:     sudo -H systemctl start    uwsgi@${SEARXNG_UWSGI_APP%.*}   restart:   sudo -H systemctl restart  uwsgi@${SEARXNG_UWSGI_APP%.*}   stop:      sudo -H systemctl stop     uwsgi@${SEARXNG_UWSGI_APP%.*}   disable:   sudo -H systemctl disable  uwsgi@${SEARXNG_UWSGI_APP%.*}EOF                ;;                fedora-*|centos-7) cat <<EOF.. code:: bash   # systemd --> /usr/lib/systemd/system/uwsgi.service   # The unit file starts uWSGI in emperor mode (/etc/uwsgi.ini), see   # - https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html   create:    ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}   restart:   sudo -H touch ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}   disable:   sudo -H rm ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}EOF                ;;            esac            echo -e ".. END searxng uwsgi-description $DIST_NAME"            local _show_cursor=""  # prevent from prefix_stdout's trailing show-cursor            echo -e "\n.. START searxng uwsgi-appini $DIST_NAME"            echo ".. code:: bash"            echo            eval "echo \"$(< "${TEMPLATES}/${uWSGI_APPS_AVAILABLE}/${SEARXNG_UWSGI_APP}${uwsgi_variant}")\"" | prefix_stdout "  "            echo -e "\n.. END searxng uwsgi-appini $DIST_NAME"            echo -e "\n.. START nginx socket"            echo ".. code:: nginx"            echo            eval "echo \"$(< "${TEMPLATES}/${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE}:socket")\"" | prefix_stdout "  "            echo -e "\n.. END nginx socket"            echo -e "\n.. START nginx http"            echo ".. code:: nginx"            echo            eval "echo \"$(< "${TEMPLATES}/${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE}")\"" | prefix_stdout "  "            echo -e "\n.. END nginx http"            echo -e "\n.. START apache socket"            echo ".. code:: apache"            echo            eval "echo \"$(< "${TEMPLATES}/${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}:socket")\"" | prefix_stdout "  "            echo -e "\n.. END apache socket"            echo -e "\n.. START apache http"            echo ".. code:: apache"            echo            eval "echo \"$(< "${TEMPLATES}/${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}")\"" | prefix_stdout "  "            echo -e "\n.. END apache http"        )    done}# ----------------------------------------------------------------------------main "$@"# ----------------------------------------------------------------------------
 |