diff --git a/.env.scripts b/.env.scripts index 835d1a1..cb3e2f0 100644 --- a/.env.scripts +++ b/.env.scripts @@ -1,10 +1,10 @@ # - - - - - - - - - - - - - - - - - - - - - - - - - -##@Version : 202509161149-git +##@Version : 202611060818-git # @@Author : CasjaysDev # @@Contact : CasjaysDev -# @@License : MIT -# @@Copyright : Copyright 2025 CasjaysDev -# @@Created : Tue Sep 16 11:49:53 AM EDT 2025 +# @@License : WTFPL +# @@Copyright : Copyright 2026 CasjaysDev +# @@Created : Fri Nov 6 08:18:16 AM EST 2026 # @@File : .env.scripts # @@Description : Variables for gen-dockerfile and buildx scripts # @@Changelog : newScript @@ -26,7 +26,7 @@ DOCKER_ENTYPOINT_HEALTH_ENDPOINTS="$DOCKER_ENTYPOINT_HEALTH_ENDPOINTS" # Dockerfile info ENV_DOCKERFILE="Dockerfile" # ENV_REGISTRY_REPO: Registry repository/image name -ENV_REGISTRY_REPO="proftp" +ENV_REGISTRY_REPO="proftpd" ENV_USE_TEMPLATE="alpine" # - - - - - - - - - - - - - - - - - - - - - - - - - # Maintainer info @@ -37,13 +37,13 @@ ENV_MAINTAINER="CasjaysDev " # - - - - - - - - - - - - - - - - - - - - - - - - - # Repository URLs (Full URLs) # ENV_GIT_REPO_URL: Complete Git repository URL for source code -ENV_GIT_REPO_URL="https://github.com/casjaysdevdocker/proftp" +ENV_GIT_REPO_URL="https://github.com/casjaysdevdocker/proftpd" # ENV_REGISTRY_URL: Registry provider base URL (for example https://docker.io) ENV_REGISTRY_URL="https://docker.io" # - - - - - - - - - - - - - - - - - - - - - - - - - # Push Configuration # ENV_REGISTRY_PUSH: Complete push destination derived from registry/org/repo -ENV_REGISTRY_PUSH="casjaysdevdocker/proftp" +ENV_REGISTRY_PUSH="casjaysdevdocker/proftpd" # ENV_IMAGE_TAG: Default tag for the image ENV_IMAGE_TAG="latest" # ENV_ADD_TAGS: Additional tags, comma-separated (USE_DATE = auto date tag) @@ -59,8 +59,8 @@ ENV_PULL_URL="casjaysdev/alpine" ENV_DISTRO_TAG="${IMAGE_VERSION}" # - - - - - - - - - - - - - - - - - - - - - - - - - # Env -SERVICE_PORT="" -EXPOSE_PORTS="" +SERVICE_PORT="21" +EXPOSE_PORTS="49152-65534" # - - - - - - - - - - - - - - - - - - - - - - - - - # IF using a lanuage such as go, php, rust, ruby, etc set the version here. LANG_VERSION="" @@ -72,12 +72,9 @@ NODE_MANAGER="system" # - - - - - - - - - - - - - - - - - - - - - - - - - # Default directories WWW_ROOT_DIR="/usr/local/share/httpd/default" -DEFAULT_FILE_DIR="/usr/local/share/template-files" -DEFAULT_DATA_DIR="/usr/local/share/template-files/data" -DEFAULT_CONF_DIR="/usr/local/share/template-files/config" -DEFAULT_TEMPLATE_DIR="/usr/local/share/template-files/defaults" # - - - - - - - - - - - - - - - - - - - - - - - - - -ENV_PACKAGES="" +ENV_PACKAGES="proftpd proftpd-mod_tls proftpd-mod_readme proftpd-utils openssl" # - - - - - - - - - - - - - - - - - - - - - - - - - # ex: ts=2 sw=2 et filetype=sh # - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/Dockerfile b/Dockerfile index ed52e85..407d33d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ -# Docker image for proftp using the alpine template -ARG IMAGE_NAME="proftp" -ARG PHP_SERVER="proftp" +# Docker image for proftpd using the alpine template +ARG IMAGE_NAME="proftpd" +ARG PHP_SERVER="proftpd" ARG BUILD_DATE="202509161149" ARG LANGUAGE="en_US.UTF-8" ARG TIMEZONE="America/New_York" @@ -14,13 +14,13 @@ ARG PATH="/usr/local/etc/docker/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/us ARG USER="root" ARG SHELL_OPTS="set -e -o pipefail" -ARG SERVICE_PORT="" -ARG EXPOSE_PORTS="" +ARG SERVICE_PORT="21" +ARG EXPOSE_PORTS="49152-65534" ARG PHP_VERSION="system" ARG NODE_VERSION="system" ARG NODE_MANAGER="system" -ARG IMAGE_REPO="casjaysdevdocker/proftp" +ARG IMAGE_REPO="casjaysdevdocker/proftpd" ARG IMAGE_VERSION="latest" ARG CONTAINER_VERSION="" @@ -54,7 +54,7 @@ ARG PHP_SERVER ARG SHELL_OPTS ARG PATH -ARG PACK_LIST=" " +ARG PACK_LIST="proftpd proftpd-mod_tls proftpd-mod_readme proftpd-utils openssl " ENV ENV=~/.profile ENV SHELL="/bin/sh" @@ -63,7 +63,7 @@ ENV TZ="${TIMEZONE}" ENV TIMEZONE="${TZ}" ENV LANG="${LANGUAGE}" ENV TERM="xterm-256color" -ENV HOSTNAME="casjaysdevdocker-proftp" +ENV HOSTNAME="casjaysdevdocker-proftpd" USER ${USER} WORKDIR /root @@ -230,8 +230,8 @@ LABEL org.opencontainers.image.authors="${LICENSE}" LABEL org.opencontainers.image.created="${BUILD_DATE}" LABEL org.opencontainers.image.version="${BUILD_VERSION}" LABEL org.opencontainers.image.schema-version="${BUILD_VERSION}" -LABEL org.opencontainers.image.url="https://docker.io/casjaysdevdocker/proftpd" -LABEL org.opencontainers.image.source="https://docker.io/casjaysdevdocker/proftpd" +LABEL org.opencontainers.image.url="https://hub.docker.com/r/casjaysdevdocker/proftpd" +LABEL org.opencontainers.image.source="https://github.com/casjaysdevdocker/proftpd" LABEL org.opencontainers.image.vcs-type="Git" LABEL org.opencontainers.image.revision="${GIT_COMMIT}" LABEL org.opencontainers.image.source="https://github.com/casjaysdevdocker/proftpd" diff --git a/README.md b/README.md index 8d33692..6d23085 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,10 @@ docker run -d \ -e TZ=${TIMEZONE:-America/New_York} \ -v "$dockerHome/data:/data:z" \ -v "$dockerHome/config:/config:z" \ --p 80:80 \ +-v "/srv/ftp:/var/ftp:z" \ +-v "/etc/letsencrypt:/etc/letsencrypt:ro" \ +-p 21:21 \ +-p 49152-65534:49152-65534 \ casjaysdevdocker/proftpd:latest ``` @@ -49,8 +52,11 @@ services: volumes: - "/srv/$USER/docker/casjaysdevdocker/proftpd/proftpd/latest/rootfs/data:/data:z" - "/srv/$USER/docker/casjaysdevdocker/proftpd/proftpd/latest/rootfs/config:/config:z" + - "/srv/ftp:/var/ftp:z" + - "/etc/letsencrypt:/etc/letsencrypt:ro" ports: - - 80:80 + - 21:21 + - 49152-65534:49152-65534 restart: always ``` diff --git a/TODO.AI.md b/TODO.AI.md new file mode 100644 index 0000000..e02717f --- /dev/null +++ b/TODO.AI.md @@ -0,0 +1,2 @@ +- Upstream gen-script `other/start-service` template: `__run_pre_execute_checks ... || return 20` uses exit code 20, outside standard ranges; change to a sysexits code in the template, then regenerate rootfs/usr/local/etc/docker/init.d/99-proftpd.sh +- Upstream functions/entrypoint.sh template: function-final `[ "$DEBUGGER" = "on" ] && ...` lines return 1 under set -e (fixed locally in __symlink and the msmtp/ssmtp/postfix setup); fix in the template and audit remaining `[ ] && cmd` tails diff --git a/rootfs/.gitea/workflows/docker.yaml b/rootfs/.gitea/workflows/docker.yaml index 14121a2..f9f3e8b 100644 --- a/rootfs/.gitea/workflows/docker.yaml +++ b/rootfs/.gitea/workflows/docker.yaml @@ -1,9 +1,9 @@ -name: proftp +name: proftpd on: push jobs: - release-proftp: + release-proftpd: runs-on: act_runner container: image: catthehacker/ubuntu:act-latest diff --git a/rootfs/tmp/etc/proftpd/proftpd.conf b/rootfs/tmp/etc/proftpd/proftpd.conf index 4bd0419..f0483f1 100644 --- a/rootfs/tmp/etc/proftpd/proftpd.conf +++ b/rootfs/tmp/etc/proftpd/proftpd.conf @@ -1,27 +1,53 @@ -# proftpd config +# ProFTPD configuration - single file: server, anonymous FTP and TLS +ModulePath /usr/lib/proftpd +LoadModule mod_tls.c +LoadModule mod_readme.c +ServerName ProFTPD +ServerAdmin root@localhost +DefaultServer on +PidFile /run/proftpd/proftpd.pid +DefaultRoot ~ +AuthOrder mod_auth_unix.c +PersistentPasswd off +UseReverseDNS off +User ftp +Group ftp +MaxInstances 20 +UseSendfile off +AllowForeignAddress on +AllowRetrieveRestart on +AllowStoreRestart on +DeferWelcome on +RootLogin off +RequireValidShell off +PassivePorts 49152 65534 +SystemLog /data/logs/proftpd/error.log +LogFormat default "%h %l %u %t \"%r\" %s %b" +LogFormat auth "%v [%P] %h %t \"%r\" %s" + Umask 022 AllowOverwrite yes -TransferLog /dev/stdout -ExtendedLog /dev/stdout -DefaultRoot /data/ftp +TransferLog /data/logs/proftpd/xfers.log +ExtendedLog /data/logs/proftpd/proftpd.log +DefaultRoot ~ DisplayConnect "/etc/motd" - + - -User root -Group root + +User ftp +Group ftp AccessGrantMsg "Anonymous login ok, restrictions apply." UserAlias anonymous ftp -MaxClients 2000 "Sorry, max %m users -- try again later" -DisplayLogin /data/welcome.msg +MaxClients 20 "Sorry, max %m users -- try again later" +DisplayLogin /var/ftp/welcome.msg DisplayChdir .message DisplayReadme README* DirFakeUser on ftp DirFakeGroup on ftp WtmpLog off -ExtendedLog /dev/stdout WRITE,READ default -ExtendedLog /dev/stdout AUTH auth +ExtendedLog /data/logs/proftpd/access.log WRITE,READ default +ExtendedLog /data/logs/proftpd/auth.log AUTH auth AllowRetrieveRestart on AnonRequirePassword off @@ -29,15 +55,15 @@ AnonRequirePassword off -PassivePorts 49152 65534 +# TLS - certs at the literal letsencrypt "domain" dir; a self-signed pair is created if missing + TLSEngine on TLSRequired off -TLSProtocol TLSv1 TLSv1.1 TLSv1.2 +TLSProtocol TLSv1.2 TLSv1.3 TLSOptions NoSessionReuseRequired TLSVerifyClient off TLSRenegotiate none -TLSLog /dev/stdout - -TLSCACertificateFile /config/ssl/ca.crt -TLSRSACertificateFile /config/ssl/localhost.crt -TLSRSACertificateKeyFile /config/ssl/localhost.key +TLSLog /data/logs/proftpd/tls.log +TLSRSACertificateFile /etc/letsencrypt/live/domain/fullchain.pem +TLSRSACertificateKeyFile /etc/letsencrypt/live/domain/privkey.pem + diff --git a/rootfs/usr/local/etc/docker/functions/entrypoint.sh b/rootfs/usr/local/etc/docker/functions/entrypoint.sh index eebaf00..0069d15 100644 --- a/rootfs/usr/local/etc/docker/functions/entrypoint.sh +++ b/rootfs/usr/local/etc/docker/functions/entrypoint.sh @@ -645,7 +645,7 @@ __symlink() { [ "$from" = "$to" ] && return 0 __rm "$from" [ -d "${from%/*}" ] || mkdir -p "${from%/*}" 2>/dev/null - ln -sf "$to" "$from" && [ "$DEBUGGER" = "on" ] && echo "Created symlink: $from -> $to" + ln -sf "$to" "$from" && if [ "$DEBUGGER" = "on" ]; then echo "Created symlink: $from -> $to"; fi } # - - - - - - - - - - - - - - - - - - - - - - - - - __file_copy() { @@ -1180,7 +1180,7 @@ EOF if [ ! -e "/usr/sbin/sendmail" ] && command -v msmtp &>/dev/null; then __symlink "/usr/sbin/sendmail" "$(command -v msmtp)" fi - [ "$DEBUGGER" = "on" ] && echo "Done setting up msmtp" + if [ "$DEBUGGER" = "on" ]; then echo "Done setting up msmtp"; fi fi ################# sSMTP relay setup @@ -1221,7 +1221,7 @@ EOF __symlink "/etc/ssmtp/revaliases" "/config/ssmtp/revaliases" __initialize_replace_variables "/etc/ssmtp/revaliases" fi - [ "$DEBUGGER" = "on" ] && echo "Done setting up ssmtp" + if [ "$DEBUGGER" = "on" ]; then echo "Done setting up ssmtp"; fi fi ################# postfix relay setup @@ -1275,7 +1275,7 @@ EOF if [ ! -f "/run/init.d/postfix.pid" ]; then __exec_service postfix start fi - [ "$DEBUGGER" = "on" ] && echo "Done setting up postfix" + if [ "$DEBUGGER" = "on" ]; then echo "Done setting up postfix"; fi fi fi [ -f "/root/dead.letter" ] && __rm "/root/dead.letter" diff --git a/rootfs/usr/local/etc/docker/init.d/99-proftpd.sh b/rootfs/usr/local/etc/docker/init.d/99-proftpd.sh old mode 100644 new mode 100755 index 12b9ade..fa8dd87 --- a/rootfs/usr/local/etc/docker/init.d/99-proftpd.sh +++ b/rootfs/usr/local/etc/docker/init.d/99-proftpd.sh @@ -1,36 +1,59 @@ #!/usr/bin/env bash # shellcheck shell=bash -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -##@Version : 202606261600-git +# - - - - - - - - - - - - - - - - - - - - - - - - - +##@Version : 202611060817-git # @@Author : Jason Hempstead # @@Contact : jason@casjaysdev.pro # @@License : LICENSE.md # @@ReadME : 99-proftpd.sh --help -# @@Copyright : Copyright: (c) 2024 Jason Hempstead, Casjays Developments -# @@Created : Wednesday, Jun 26, 2024 10:30 EDT +# @@Copyright : Copyright: (c) 2026 Jason Hempstead, Casjays Developments +# @@Created : Friday, Nov 06, 2026 08:17 EST # @@File : 99-proftpd.sh -# @@Description : +# @@Description : # @@Changelog : New script # @@TODO : Better documentation -# @@Other : -# @@Resource : +# @@Other : +# @@Resource : # @@Terminal App : no # @@sudo/root : no # @@Template : other/start-service -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# shellcheck disable=SC2016 -# shellcheck disable=SC2031 -# shellcheck disable=SC2120 -# shellcheck disable=SC2155 -# shellcheck disable=SC2199 -# shellcheck disable=SC2317 -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html -[ "$DEBUGGER" = "on" ] && echo "Enabling debugging" && set -o pipefail -x$DEBUGGER_OPTIONS || set -o pipefail -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -printf '%s\n' "# - - - Initializing proftpd - - - #" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - +# shellcheck disable=SC1001,SC1003,SC2001,SC2003,SC2016,SC2031,SC2090,SC2115,SC2120,SC2155,SC2199,SC2229,SC2317,SC2329 +# - - - - - - - - - - - - - - - - - - - - - - - - - +set -eo pipefail +# - - - - - - - - - - - - - - - - - - - - - - - - - +# run trap command on exit +trap '__trap_err_handler' ERR +trap 'retVal=$?;if [ "$SERVICE_IS_RUNNING" != "yes" ] && [ -f "$SERVICE_PID_FILE" ]; then rm -Rf "$SERVICE_PID_FILE"; fi;exit $retVal' SIGINT SIGTERM +trap 'retVal=$?;if [ "$SERVICE_IS_RUNNING" != "yes" ] && [ -f "$SERVICE_PID_FILE" ]; then rm -Rf "$SERVICE_PID_FILE"; fi;exit $retVal' SIGPWR 2>/dev/null || true +# - - - - - - - - - - - - - - - - - - - - - - - - - +# ERR trap handler - smart about critical vs non-critical errors +__trap_err_handler() { + local retVal=$? + local command="$BASH_COMMAND" + # Ignore SIGPIPE and user interrupts + [ $retVal -eq 130 ] || [ $retVal -eq 141 ] && return $retVal + # Non-critical: file operations, text processing, user/group operations + if [[ "$command" =~ (mkdir|touch|chmod|chown|chgrp|ln|cp|mv|rm|echo|printf|cat|tee|sed|awk|grep|find|sort|uniq|adduser|addgroup|usermod|groupmod|id|getent) ]]; then + return 0 + fi + # Non-critical: conditional checks that might fail + if [[ "$command" =~ (test|\[|\[\[|kill -0|pgrep|pidof|ps) ]]; then + return 0 + fi + # Critical error - but only fail if service hasn't started yet + if [ "$SERVICE_IS_RUNNING" != "yes" ]; then + echo "❌ Critical error (exit $retVal): $command" >&2 + kill -TERM 1 2>/dev/null || exit $retVal + fi + return 0 +} +# - - - - - - - - - - - - - - - - - - - - - - - - - +VERSION="202611060817-git" +SCRIPT_FILE="$0" SERVICE_NAME="proftpd" +SCRIPT_NAME="${SCRIPT_FILE##*/}" +# - - - - - - - - - - - - - - - - - - - - - - - - - # Function to exit appropriately based on context __script_exit() { local exit_code="${1:-0}" @@ -42,299 +65,424 @@ __script_exit() { exit "$exit_code" fi } -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -SCRIPT_NAME="${0##*/}" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Exit if service is disabled +if [ -n "$PROFTPD_APPNAME_ENABLED" ]; then + if [ "$PROFTPD_APPNAME_ENABLED" != "yes" ]; then + export SERVICE_DISABLED="$SERVICE_NAME" + __script_exit 0 + fi +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - +# setup debugging - https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html +[ -f "/config/.debug" ] && [ -z "$DEBUGGER_OPTIONS" ] && export DEBUGGER_OPTIONS="$(<"/config/.debug")" || DEBUGGER_OPTIONS="${DEBUGGER_OPTIONS:-}" +if [ "$DEBUGGER" = "on" ] || [ -f "/config/.debug" ]; then + echo "Enabling debugging" + [ -n "$DEBUGGER_OPTIONS" ] && set -"$DEBUGGER_OPTIONS" + export DEBUGGER="on" +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - export PATH="/usr/local/etc/docker/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# run trap command on exit -trap 'retVal=$?;[ "$SERVICE_IS_RUNNING" != "true" ] && [ -f "$SERVICE_PID_FILE" ] && rm -Rf "$SERVICE_PID_FILE";exit $retVal' SIGINT SIGTERM EXIT -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # import the functions file if [ -f "/usr/local/etc/docker/functions/entrypoint.sh" ]; then . "/usr/local/etc/docker/functions/entrypoint.sh" fi -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - +# fallback in case entrypoint.sh is missing or didn't define this helper - +# every guard below the "Allow ENV_ variable" sections depends on it existing +if ! builtin type -t __file_exists_with_content | grep -q -- 'function'; then + __file_exists_with_content() { [ -n "$1" ] && [ -f "$1" ] && [ -s "$1" ] || return 2; } +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - # import variables for set_env in "/root/env.sh" "/usr/local/etc/docker/env"/*.sh "/config/env"/*.sh; do - [ -f "$set_env" ] && . "$set_env" -done -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# Run any pre-execution checks -__run_pre_execute_checks() { - local exitStatus=0 - - true - exitStatus=$? - if [ $exitStatus -ne 0 ]; then - echo "The pre-execution check has failed" - exit ${exitStatus:-20} + if [ -f "$set_env" ]; then + . "$set_env" fi - return $exitStatus -} -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +done +# - - - - - - - - - - - - - - - - - - - - - - - - - +# exit if __start_init_scripts function hasn't been Initialized +if [ ! -f "/run/.start_init_scripts.pid" ]; then + echo "__start_init_scripts function hasn't been Initialized" >&2 + SERVICE_IS_RUNNING="no" + __script_exit 1 +fi +# Clean up any stale PID file for this service on startup +if [ -n "$SERVICE_NAME" ] && [ -f "/run/init.d/$SERVICE_NAME.pid" ]; then + old_pid=$(<"/run/init.d/$SERVICE_NAME.pid") 2>/dev/null + if [ -n "$old_pid" ] && ! kill -0 "$old_pid" 2>/dev/null; then + rm -f "/run/init.d/$SERVICE_NAME.pid" + [ "$DEBUGGER" = "on" ] && echo "Removed stale PID file for $SERVICE_NAME" + fi +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - # Custom functions -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Script to execute +START_SCRIPT="/usr/local/etc/docker/exec/$SERVICE_NAME" +# - - - - - - - - - - - - - - - - - - - - - - - - - # Reset environment before executing service -RESET_ENV="yes" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# Show message before execute -PRE_EXEC_MESSAGE="" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# Set the database root dir -DATABASE_BASE_DIR="${DATABASE_BASE_DIR:-/data/db}" -# set the database directory -DATABASE_DIR="${DATABASE_DIR_PROFTPD:-/data/db/sqlite}" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +RESET_ENV="no" +# - - - - - - - - - - - - - - - - - - - - - - - - - # Set webroot WWW_ROOT_DIR="/usr/local/share/httpd/default" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # Default predefined variables # set data directory DATA_DIR="/data/proftpd" # set config directory CONF_DIR="/config/proftpd" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # set the containers etc directory ETC_DIR="/etc/proftpd" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - +# set the var dir +VAR_DIR="" +# - - - - - - - - - - - - - - - - - - - - - - - - - +# set the temp dir TMP_DIR="/tmp/proftpd" # set scripts pid dir RUN_DIR="/run/proftpd" # set log directory LOG_DIR="/data/logs/proftpd" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # Set the working dir -# set working directory WORK_DIR="" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# Where to save passwords to -# directory to save username/password for root user -ROOT_FILE_PREFIX="/config/secure/auth/root" -# directory to save username/password for normal user -USER_FILE_PREFIX="/config/secure/auth/user" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# root/admin user info password/random] -# root user name -root_user_name="${PROFTPD_ROOT_USER_NAME:-}" -# root user password -root_user_pass="${PROFTPD_ROOT_PASS_WORD:-}" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# Normal user info [password/random] -# normal user name -user_name="${PROFTPD_USER_NAME:-}" -# normal user password -user_pass="${PROFTPD_USER_PASS_WORD:-}" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # port which service is listening on SERVICE_PORT="21" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # User to use to launch service - IE: postgres # normally root RUNAS_USER="root" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # User and group in which the service switches to - IE: nginx,apache,mysql,postgres # execute command as another user -SERVICE_USER="proftpd" +#SERVICE_USER="proftpd" # Set the service group -SERVICE_GROUP="proftpd" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +#SERVICE_GROUP="proftpd" +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Set password length +RANDOM_PASS_USER="" +RANDOM_PASS_ROOT="" +# - - - - - - - - - - - - - - - - - - - - - - - - - # Set user and group ID # set the user id SERVICE_UID="0" # set the group id SERVICE_GID="0" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # execute command variables - keep single quotes variables will be expanded later -# command to execute +# command to execute — set to the service binary (e.g. 'nginx'); leave empty for init-only scripts EXEC_CMD_BIN='proftpd' # command arguments -EXEC_CMD_ARGS='--nodaemon -c /etc/proftpd/proftpd.conf' +EXEC_CMD_ARGS='--nodaemon --config $ETC_DIR/proftpd.conf' # execute script before EXEC_PRE_SCRIPT='' -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Set to 'no' for configuration services (no daemon process), leave blank for actual services +SERVICE_USES_PID='' +# - - - - - - - - - - - - - - - - - - - - - - - - - # Is this service a web server IS_WEB_SERVER="no" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # Is this service a database server IS_DATABASE_SERVICE="no" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Does this service use a database server +USES_DATABASE_SERVICE="no" +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Set defualt type - [custom,sqlite,redis,postgres,mariadb,mysql,couchdb,mongodb,supabase] +DATABASE_SERVICE_TYPE="sqlite" +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Show message before execute +PRE_EXEC_MESSAGE="" +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Set the wait time to execute __post_execute function - minutes +POST_EXECUTE_WAIT_TIME="1" +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Update path var +PATH="$PATH:." +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Where to save passwords to +# directory to save username/password for root user +ROOT_FILE_PREFIX="/config/secure/auth/root" +# directory to save username/password for normal user +USER_FILE_PREFIX="/config/secure/auth/user" +# - - - - - - - - - - - - - - - - - - - - - - - - - +# root/admin user info password/random] +# root user name +root_user_name="${PROFTPD_ROOT_USER_NAME:-}" +# root user password +root_user_pass="${PROFTPD_ROOT_PASS_WORD:-}" +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Normal user info [password/random] +# normal user name +user_name="${PROFTPD_USER_NAME:-}" +# normal user password +user_pass="${PROFTPD_USER_PASS_WORD:-}" +# - - - - - - - - - - - - - - - - - - - - - - - - - # Load variables from config # Generated by my dockermgr script -[ -f "$CONF_DIR/env/proftpd.script.sh" ] && . "$CONF_DIR/env/proftpd.script.sh" -# Overwrite the variabes -[ -f "$CONF_DIR/env/proftpd.sh" ] && . "$CONF_DIR/env/proftpd.sh" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +if [ -f "/config/env/proftpd.script.sh" ]; then + . "/config/env/proftpd.script.sh" +fi +# Overwrite the variables +if [ -f "/config/env/proftpd.sh" ]; then + . "/config/env/proftpd.sh" +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - # Additional predefined variables -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # Additional variables -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # Specifiy custom directories to be created ADD_APPLICATION_FILES="" -ADD_APPLICATION_DIRS="" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -APPLICATION_FILES="$LOG_DIR/proftpd.log" -APPLICATION_DIRS="$RUN_DIR $ETC_DIR $CONF_DIR $LOG_DIR $TMP_DIR" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +ADD_APPLICATION_DIRS="/var/ftp" +# - - - - - - - - - - - - - - - - - - - - - - - - - +APPLICATION_FILES="$LOG_DIR/$SERVICE_NAME.log" +APPLICATION_DIRS="$ETC_DIR $CONF_DIR $DATA_DIR $LOG_DIR $TMP_DIR $RUN_DIR $VAR_DIR" +# - - - - - - - - - - - - - - - - - - - - - - - - - # Additional config dirs - will be Copied to /etc/$name ADDITIONAL_CONFIG_DIRS="" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # define variables that need to be loaded into the service - escape quotes - var=\"value\",other=\"test\" CMD_ENV="" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # Overwrite based on file/directory -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # Per Application Variables or imports +# - - - - - - - - - - - - - - - - - - - - - - - - - +# create a self-signed fallback when no Let's Encrypt certificate is present +__update_ssl_conf_local() { + local sysname="${SERVER_NAME:-${FULL_DOMAIN_NAME:-$HOSTNAME}}" + local tls_dir="/etc/letsencrypt/live/domain" + if [ -s "$tls_dir/fullchain.pem" ] && [ -s "$tls_dir/privkey.pem" ]; then + return 0 + fi + mkdir -p "$tls_dir" || return 0 + echo "No certificate found in $tls_dir: creating a self-signed fallback for $sysname" + openssl req -new -newkey rsa:4096 -days 3650 -nodes -x509 \ + -subj "/CN=$sysname" -addext "subjectAltName=DNS:$sysname" \ + -keyout "$tls_dir/privkey.pem" -out "$tls_dir/fullchain.pem" 2>/dev/null || true + chmod 600 "$tls_dir/privkey.pem" 2>/dev/null || true + return 0 +} +# - - - - - - - - - - - - - - - - - - - - - - - - - +# reload proftpd whenever the certificate or key changes (Let's Encrypt renewal) +__watch_tls_cert() { + local tls_dir="/etc/letsencrypt/live/domain" + local interval="${PROFTPD_CERT_CHECK_INTERVAL:-60}" + local last="" now="" + last="$(cat "$tls_dir/fullchain.pem" "$tls_dir/privkey.pem" 2>/dev/null | cksum)" + while sleep "$interval"; do + now="$(cat "$tls_dir/fullchain.pem" "$tls_dir/privkey.pem" 2>/dev/null | cksum)" + if [ "$now" != "$last" ]; then + last="$now" + echo "$(date '+%Y-%m-%dT%H:%M:%S%z') TLS certificate changed: reloading proftpd" + if [ -s "$RUN_DIR/proftpd.pid" ]; then kill -HUP "$(<"$RUN_DIR/proftpd.pid")" 2>/dev/null || true; fi + fi + done +} +__post_execute_local() { + __watch_tls_cert >>"$LOG_DIR/tls-watch.log" 2>&1 & + return 0 +} -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Custom commands to run before copying to /config +__run_precopy() { + # Define environment + local hostname=${HOSTNAME} + if [ ! -d "/run/healthcheck" ]; then + mkdir -p "/run/healthcheck" + fi + # Define actions/commands + + # allow custom functions + if builtin type -t __run_precopy_local | grep -q -- 'function'; then + __run_precopy_local + fi +} +# - - - - - - - - - - - - - - - - - - - - - - - - - # Custom prerun functions - IE setup WWW_ROOT_DIR +__execute_prerun() { + # Define environment + local hostname=${HOSTNAME} + # Define actions/commands -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # allow custom functions + if builtin type -t __execute_prerun_local | grep -q -- 'function'; then + __execute_prerun_local + fi +} +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Run any pre-execution checks +__run_pre_execute_checks() { + # Set variables + local exitStatus=0 + # message to show at start + local pre_execute_checks_MessageST="Running preexecute check for $SERVICE_NAME" + # message to show at completion + local pre_execute_checks_MessageEnd="Finished preexecute check for $SERVICE_NAME" + [ "$DEBUGGER" = "on" ] && __banner "$pre_execute_checks_MessageST" + # Put command to execute in parentheses + { + # proftpd refuses to write logs in a world-writable directory + chmod 755 "$LOG_DIR" 2>/dev/null || true + } + exitStatus=$? + [ "$DEBUGGER" = "on" ] && __banner "$pre_execute_checks_MessageEnd: Status $exitStatus" + + # show exit message + if [ $exitStatus -ne 0 ]; then + echo "The pre-execution check has failed" >&2 + if [ -f "$SERVICE_PID_FILE" ]; then + rm -Rf "$SERVICE_PID_FILE" + fi + __script_exit 1 + fi + # allow custom functions + if builtin type -t __run_pre_execute_checks_local | grep -q -- 'function'; then + __run_pre_execute_checks_local + fi + # exit function + return $exitStatus +} +# - - - - - - - - - - - - - - - - - - - - - - - - - # use this function to update config files - IE: change port __update_conf_files() { # default exit code local exitCode=0 # set hostname local sysname="${SERVER_NAME:-${FULL_DOMAIN_NAME:-$HOSTNAME}}" - - # CD into temp to bybass any permission errors - # lets keep shellcheck happy by adding false - cd /tmp || false - + # - - - - - - - - - - - - - - - - - - - - - - - - - # delete files #__rm "" - # execute if directory is empty - #__is_dir_empty "" && true || false - - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Create base directories - __setup_directories - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Initialize templates - if [ ! -d "$CONF_DIR" ] || __is_dir_empty "$CONF_DIR"; then - if [ -d "$ETC_DIR" ]; then - mkdir -p "$CONF_DIR" - __copy_templates "$ETC_DIR/." "$CONF_DIR/" - else - __copy_templates "$ETC_DIR" "$CONF_DIR" - fi - fi - - # define actions + # - - - - - - - - - - - - - - - - - - - - - - - - - + # custom commands + # - - - - - - - - - - - - - - - - - - - - - - - - - # replace variables # __replace "" "" "$CONF_DIR/proftpd.conf" # replace variables recursively - # __find_replace "" "" "$CONF_DIR" + # __find_replace "" "" "$CONF_DIR" - # custom commands + # - - - - - - - - - - - - - - - - - - - - - - - - - + # define actions + # allow custom functions + if builtin type -t __update_conf_files_local | grep -q -- 'function'; then + __update_conf_files_local + fi + # exit function return $exitCode } -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # function to run before executing __pre_execute() { # default exit code local exitCode=0 # set hostname local sysname="${SERVER_NAME:-${FULL_DOMAIN_NAME:-$HOSTNAME}}" - - # define commands - # execute if directories is empty - #__is_dir_empty "" && true || false - - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # create user if needed - __create_service_user "$SERVICE_USER" "$SERVICE_GROUP" "${WORK_DIR:-/home/$SERVICE_USER}" "${SERVICE_UID:-}" "${SERVICE_GID:-}" - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Modify user if needed - __set_user_group_id $SERVICE_USER ${SERVICE_UID:-} ${SERVICE_GID:-} - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Set permissions - __fix_permissions "$SERVICE_USER" "$SERVICE_GROUP" - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Create directories - __setup_directories - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Run Custom command - - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Copy /config to /etc - for config_2_etc in $CONF_DIR $ADDITIONAL_CONFIG_DIRS; do - __initialize_system_etc "$config_2_etc" |& tee -p -a "$LOG_DIR/init.txt" &>/dev/null - done - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Replace the applications user and group - - # Replace variables - HOSTNAME="$sysname" __initialize_replace_variables "$ETC_DIR" "$CONF_DIR" "$WWW_ROOT_DIR" - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Run checks - __run_pre_execute_checks + # __is_dir_empty "$CONF_DIR" && true + # - - - - - - - - - - - - - - - - - - - - - - - - - + # define actions to run after copying to /config + # - - - - - - - - - - - - - - - - - - - - - - - - - # unset unneeded variables - unset filesperms filename config_2_etc change_user change_user ADDITIONAL_CONFIG_DIRS application_files filedirs + unset sysname # Lets wait a few seconds before continuing - sleep 10 + sleep 2 + # allow custom functions + if builtin type -t __pre_execute_local | grep -q -- 'function'; then + __pre_execute_local + fi + # exit function return $exitCode } -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # function to run after executing __post_execute() { - # default exit code - local exitCode=0 - # set hostname - local sysname="${SERVER_NAME:-${FULL_DOMAIN_NAME:-$HOSTNAME}}" - + # init pid var + local pid="" + # set default exit code + local retVal=0 # how long to wait before executing - sleep 60 - # message - echo "Running post commands" - # execute commands + local ctime=${POST_EXECUTE_WAIT_TIME:-1} + # convert minutes to seconds + local waitTime=$((ctime * 60)) + # message to show at start + local postMessageST="Running post commands for $SERVICE_NAME" + # message to show at completion + local postMessageEnd="Finished post commands for $SERVICE_NAME" + # wait + sleep $waitTime + # execute commands after waiting ( - sleep 20 - true - ) |& tee -p -a "$LOG_DIR/init.txt" &>/dev/null & - return $exitCode + [ "$DEBUGGER" = "on" ] && __banner "$postMessageST" + # commands to execute + sleep 5 + [ "$DEBUGGER" = "on" ] && __banner "$postMessageEnd: Status $retVal" + ) 2>"/dev/stderr" | tee -p -a "/data/logs/init.txt" & + # fire-and-forget: backgrounded subshell always succeeds at launch + retVal=0 + # allow custom functions + if builtin type -t __post_execute_local | grep -q -- 'function'; then + __post_execute_local + fi + # exit function + return $retVal } -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # use this function to update config files - IE: change port __pre_message() { local exitCode=0 - [ -n "$user_name" ] && echo "username: $user_name" && echo "$user_name" >"${USER_FILE_PREFIX}/${SERVICE_NAME}_name" - [ -n "$user_pass" ] && __printf_space "40" "password:" "saved to ${USER_FILE_PREFIX}/${SERVICE_NAME}_pass" && echo "$user_pass" >"${USER_FILE_PREFIX}/${SERVICE_NAME}_pass" - [ -n "$root_user_name" ] && echo "root username: $root_user_name" && echo "$root_user_name" >"${ROOT_FILE_PREFIX}/${SERVICE_NAME}_name" - [ -n "$root_user_pass" ] && __printf_space "40" "root password:" "saved to ${ROOT_FILE_PREFIX}/${SERVICE_NAME}_pass" && echo "$root_user_pass" >"${ROOT_FILE_PREFIX}/${SERVICE_NAME}_pass" - [ -n "$PRE_EXEC_MESSAGE" ] && eval echo "$PRE_EXEC_MESSAGE" + if [ -n "$PRE_EXEC_MESSAGE" ]; then + eval echo "$PRE_EXEC_MESSAGE" + fi + # execute commands + # allow custom functions + if builtin type -t __pre_message_local | grep -q -- 'function'; then + __pre_message_local + fi + # exit function return $exitCode } -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # use this function to setup ssl support __update_ssl_conf() { local exitCode=0 - # set hostname local sysname="${SERVER_NAME:-${FULL_DOMAIN_NAME:-$HOSTNAME}}" + # execute commands + # allow custom functions + if builtin type -t __update_ssl_conf_local | grep -q -- 'function'; then + __update_ssl_conf_local + fi + # set exitCode return $exitCode } -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - __create_service_env() { local exitCode=0 - cat </dev/null -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + [ -d "/config/env" ] || mkdir -p "/config/env" 2>/dev/null + if [ ! -f "/config/env/${SERVICE_NAME:-$SCRIPT_NAME}.sh" ]; then + cat </dev/null +# - - - - - - - - - - - - - - - - - - - - - - - - - # root/admin user info [password/random] #ENV_ROOT_USER_NAME="${ENV_ROOT_USER_NAME:-$PROFTPD_ROOT_USER_NAME}" # root user name #ENV_ROOT_USER_PASS="${ENV_ROOT_USER_NAME:-$PROFTPD_ROOT_PASS_WORD}" # root user password #root_user_name="${ENV_ROOT_USER_NAME:-$root_user_name}" # #root_user_pass="${ENV_ROOT_USER_PASS:-$root_user_pass}" # -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - #Normal user info [password/random] #ENV_USER_NAME="${ENV_USER_NAME:-$PROFTPD_USER_NAME}" # #ENV_USER_PASS="${ENV_USER_PASS:-$PROFTPD_USER_PASS_WORD}" # @@ -342,11 +490,11 @@ __create_service_env() { #user_pass="${ENV_USER_PASS:-$user_pass}" # normal user password EOF - __file_exists_with_content "/config/env/${SERVICE_NAME:-$SCRIPT_NAME}.sh" || return 1 + fi if [ ! -f "/config/env/${SERVICE_NAME:-$SCRIPT_NAME}.local.sh" ]; then - cat <<'EOF' >"/config/env/${SERVICE_NAME:-$SCRIPT_NAME}.local.sh" + cat <<'EOF' >"/config/env/${SERVICE_NAME:-$SCRIPT_NAME}.local.sh" # - - - - - - - - - - - - - - - - - - - - - - - - - -# Local overrides - sourced after the main env file. +# Local overrides — sourced after the main env file. # Redefine any of these functions to customise behaviour. # - - - - - - - - - - - - - - - - - - - - - - - - - __run_precopy_local() { true; } @@ -359,18 +507,19 @@ __update_conf_files_local() { true; } # - - - - - - - - - - - - - - - - - - - - - - - - - __pre_execute_local() { true; } # - - - - - - - - - - - - - - - - - - - - - - - - - -__post_execute_local() { true; } -# - - - - - - - - - - - - - - - - - - - - - - - - - __pre_message_local() { true; } # - - - - - - - - - - - - - - - - - - - - - - - - - -__update_ssl_conf_local() { true; } -# - - - - - - - - - - - - - - - - - - - - - - - - - EOF fi - __file_exists_with_content "/config/env/${SERVICE_NAME:-$SCRIPT_NAME}.local.sh" || exitCode=$((exitCode + 1)) + if ! __file_exists_with_content "/config/env/${SERVICE_NAME:-$SCRIPT_NAME}.sh"; then + exitCode=$((exitCode + 1)) + fi + if ! __file_exists_with_content "/config/env/${SERVICE_NAME:-$SCRIPT_NAME}.local.sh"; then + exitCode=$((exitCode + 1)) + fi return $exitCode } -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # script to start server __run_start_script() { local runExitCode=0 @@ -394,196 +543,461 @@ __run_start_script() { local path="$(eval echo "$PATH")" # expand variables local message="$(eval echo "")" - # set hostname local sysname="${SERVER_NAME:-${FULL_DOMAIN_NAME:-$HOSTNAME}}" - [ -f "$CONF_DIR/$SERVICE_NAME.exec_cmd.sh" ] && . "$CONF_DIR/$SERVICE_NAME.exec_cmd.sh" + if [ -f "$CONF_DIR/$SERVICE_NAME.exec_cmd.sh" ]; then + . "$CONF_DIR/$SERVICE_NAME.exec_cmd.sh" + fi + # if [ -z "$cmd" ]; then - __post_execute 2>"/dev/stderr" |& tee -p -a "$LOG_DIR/init.txt" &>/dev/null - echo "Initializing $SCRIPT_NAME has completed" + __log_info "Initialization of $SCRIPT_NAME has completed (no daemon configured)" + return 0 else # ensure the command exists if [ ! -x "$cmd" ]; then - echo "$name is not a valid executable" - exit 2 + __log_error "$name is not a valid executable" + return 2 fi - # set working directories - [ -z "$home" ] && home="${workdir:-/tmp/docker}" - [ "$home" = "/root" ] && home="/tmp/docker" - [ "$home" = "$workdir" ] && workdir="" - # create needed directories - [ -n "$home" ] && { [ -d "$home" ] || { mkdir -p "$home" && chown -Rf $SERVICE_USER:$SERVICE_GROUP "$home"; }; } - [ -n "$workdir" ] && { [ -d "$workdir" ] || { mkdir -p "$workdir" && chown -Rf $SERVICE_USER:$SERVICE_GROUP "$workdir"; }; } - - [ "$user" != "root " ] && [ -d "$home" ] && chmod -f 777 "$home" - [ "$user" != "root " ] && [ -d "$workdir" ] && chmod -f 777 "$workdir" - # check and exit if already running + # check and exit if already running (respects SERVICE_USES_PID in __proc_check) if __proc_check "$name" || __proc_check "$cmd"; then - echo "$name is already running" >&2 - exit 0 + __log_debug "Service $name is already running" + return 0 else - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # - - - - - - - - - - - - - - - - - - - - - - - - - # show message if env exists - if [ -n "$cmd_exec" ]; then - [ -n "$SERVICE_USER" ] && echo "Setting up $cmd_exec to run as $SERVICE_USER" || SERVICE_USER="root" - [ -n "$SERVICE_PORT" ] && echo "$name will be running on $SERVICE_PORT" || SERVICE_PORT="" + if [ -n "$cmd" ]; then + if [ -n "$SERVICE_USER" ]; then + [ "$DEBUGGER" = "on" ] && __log_info "Setting up $cmd to run as $SERVICE_USER" + else + SERVICE_USER="root" + fi + if [ -n "$SERVICE_PORT" ]; then + [ "$DEBUGGER" = "on" ] && __log_info "$name will be running on port $SERVICE_PORT" + else + SERVICE_PORT="" + fi fi - [ -n "$su_exec" ] && message="using $su_exec" - if [ -n "$pre" ] && [ -n "$(command -v "$pre" 2>/dev/null)" ]; then + if [ -n "$pre" ] && command -v "$pre" &>/dev/null; then export cmd_exec="$pre $cmd $args" - message="Starting service: $name $args through $pre $message" + message="Starting service: $name $args through $pre" else export cmd_exec="$cmd $args" - message="Starting service: $name $args $message" + message="Starting service: $name $args" fi - __cd "${workdir:-$home}" - echo "$message" + if [ -n "$su_exec" ]; then + __log_debug "Using $su_exec" | tee -a -p "/data/logs/init.txt" + fi + __log_info "$message" | tee -a -p "/data/logs/init.txt" su_cmd touch "$SERVICE_PID_FILE" - __post_execute |& tee -p -a "$LOG_DIR/init.txt" &>/dev/null & + # W14: invalidate cached START_SCRIPT if key variables changed + local _script_hash_src="$cmd $args $SERVICE_USER $RESET_ENV $su_exec" + local _script_hash + _script_hash=$(md5sum <<<"$_script_hash_src" 2>/dev/null | cut -c1-8 || true) + if [ -f "${START_SCRIPT}.hash" ] && [ -f "$START_SCRIPT" ]; then + if [ "$(<"${START_SCRIPT}.hash")" != "$_script_hash" ]; then + rm -f "$START_SCRIPT" "${START_SCRIPT}.hash" + fi + fi if [ "$RESET_ENV" = "yes" ]; then - su_cmd env -i HOME="$home" LC_CTYPE="$lc_type" PATH="$path" HOSTNAME="$sysname" USER="${SERVICE_USER:-$RUNAS_USER}" $extra_env sh -c "$cmd_exec" || - eval env -i HOME="$home" LC_CTYPE="$lc_type" PATH="$path" HOSTNAME="$sysname" USER="${SERVICE_USER:-$RUNAS_USER}" $extra_env sh -c "$cmd_exec" || - return 10 + # RESET_ENV=yes intentionally strips all inherited vars; only explicit vars are passed + if [ ! -f "$START_SCRIPT" ]; then + # Use printf %q to safely quote each env component for embedding in the script + local _q_home _q_lc _q_path _q_sysname _q_svcuser _q_su _q_cmd _q_args _q_extra + _q_home=$(printf '%q' "$home") + _q_lc=$(printf '%q' "$lc_type") + _q_path=$(printf '%q' "$path") + _q_sysname=$(printf '%q' "$sysname") + _q_svcuser=$(printf '%q' "${SERVICE_USER:-$RUNAS_USER}") + _q_su='' + [ -n "$su_exec" ] && _q_su=$(printf '%q ' $su_exec) + _q_cmd=$(printf '%q' "$cmd") + _q_args='' + [ -n "$args" ] && _q_args=$(printf '%q ' $args) + _q_extra='' + [ -n "$extra_env" ] && _q_extra=$(printf '%q ' $extra_env) + { + printf '#!/usr/bin/env bash\n' + printf "trap 'exitCode=\$?;[ \$exitCode -ne 0 ] && [ -f \"\$SERVICE_PID_FILE\" ] && rm -Rf \"\$SERVICE_PID_FILE\";exit \$exitCode' EXIT\n" + printf 'set -Eeo pipefail\n' + printf '# Setting up %s to run as %s with env\n' "$cmd" "${SERVICE_USER:-root}" + printf 'retVal=10\n' + printf 'SERVICE_NAME=%q\n' "$SERVICE_NAME" + printf 'SERVICE_PID_FILE=%q\n' "$SERVICE_PID_FILE" + printf 'LOG_DIR=%q\n' "$LOG_DIR" + printf '%s env -i HOME=%s LC_CTYPE=%s PATH=%s HOSTNAME=%s USER=%s %s %s %s 2>>"/dev/stderr" >>"$LOG_DIR/$SERVICE_NAME.log" &\n' \ + "$_q_su" "$_q_home" "$_q_lc" "$_q_path" "$_q_sysname" "$_q_svcuser" "$_q_extra" "$_q_cmd" "$_q_args" + printf 'execPid=$!\n' + printf 'sleep 1\n' + printf 'if [ -n "$execPid" ] && kill -0 "$execPid" 2>/dev/null; then\n' + printf ' echo "$execPid" >"$SERVICE_PID_FILE"\n' + printf ' retVal=0\n' + printf ' printf '"'"'%%s\n'"'"' "$SERVICE_NAME: $execPid" >"/run/healthcheck/$SERVICE_NAME"\n' + printf 'else\n' + printf ' retVal=10\n' + printf ' echo "Failed to start service %s" >&2\n' "$cmd" + printf 'fi\n' + printf 'exit $retVal\n' + } >"$START_SCRIPT" + printf '%s' "$_script_hash" >"${START_SCRIPT}.hash" + fi else - su_cmd "$cmd_exec" || eval "$cmd_exec" || return 10 + if [ ! -f "$START_SCRIPT" ]; then + local _q_su _q_cmd _q_args + _q_su='' + [ -n "$su_exec" ] && _q_su=$(printf '%q ' $su_exec) + _q_cmd=$(printf '%q' "$cmd") + _q_args='' + [ -n "$args" ] && _q_args=$(printf '%q ' $args) + { + printf '#!/usr/bin/env bash\n' + printf "trap 'exitCode=\$?;[ \$exitCode -ne 0 ] && [ -f \"\$SERVICE_PID_FILE\" ] && rm -Rf \"\$SERVICE_PID_FILE\";exit \$exitCode' EXIT\n" + printf 'set -Eeo pipefail\n' + printf '# Setting up %s to run as %s\n' "$cmd" "${SERVICE_USER:-root}" + printf 'retVal=10\n' + printf 'SERVICE_NAME=%q\n' "$SERVICE_NAME" + printf 'SERVICE_PID_FILE=%q\n' "$SERVICE_PID_FILE" + printf 'LOG_DIR=%q\n' "$LOG_DIR" + printf '%s %s %s 2>>"/dev/stderr" >>"$LOG_DIR/$SERVICE_NAME.log" &\n' \ + "$_q_su" "$_q_cmd" "$_q_args" + printf 'execPid=$!\n' + printf 'sleep 1\n' + printf 'if [ -n "$execPid" ] && kill -0 "$execPid" 2>/dev/null; then\n' + printf ' echo "$execPid" >"$SERVICE_PID_FILE"\n' + printf ' retVal=0\n' + printf 'else\n' + printf ' retVal=10\n' + printf ' echo "Failed to start service %s" >&2\n' "$cmd" + printf 'fi\n' + printf 'exit $retVal\n' + } >"$START_SCRIPT" + printf '%s' "$_script_hash" >"${START_SCRIPT}.hash" + fi fi fi + if [ ! -x "$START_SCRIPT" ]; then + chmod 755 -Rf "$START_SCRIPT" + fi + if [ "$CONTAINER_INIT" != "yes" ]; then + # W15: launch as bash, not sh, since the generated script uses bash-specific features + bash "$START_SCRIPT" + runExitCode=$? + fi fi + return $runExitCode } -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # username and password actions __run_secure_function() { + local filesperms if [ -n "$user_name" ] || [ -n "$user_pass" ]; then for filesperms in "${USER_FILE_PREFIX}"/*; do if [ -e "$filesperms" ]; then chmod -Rf 600 "$filesperms" - chown -Rf $SERVICE_USER:$SERVICE_USER "$filesperms" + [ -n "$SERVICE_USER" ] && chown -Rf "$SERVICE_USER:$SERVICE_USER" "$filesperms" 2>/dev/null fi - done |& tee -p -a "$LOG_DIR/init.txt" &>/dev/null + done 2>/dev/null | tee -p -a "/data/logs/init.txt" fi if [ -n "$root_user_name" ] || [ -n "$root_user_pass" ]; then for filesperms in "${ROOT_FILE_PREFIX}"/*; do if [ -e "$filesperms" ]; then chmod -Rf 600 "$filesperms" - chown -Rf $SERVICE_USER:$SERVICE_USER "$filesperms" + [ -n "$SERVICE_USER" ] && chown -Rf "$SERVICE_USER:$SERVICE_USER" "$filesperms" 2>/dev/null fi - done |& tee -p -a "$LOG_DIR/init.txt" &>/dev/null + done 2>/dev/null | tee -p -a "/data/logs/init.txt" fi + unset filesperms } -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - # Allow ENV_ variable - Import env file -__file_exists_with_content "/config/env/${SERVICE_NAME:-$SCRIPT_NAME}.sh" && . "/config/env/${SERVICE_NAME:-$SCRIPT_NAME}.sh" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +if __file_exists_with_content "/config/env/${SERVICE_NAME:-$SCRIPT_NAME}.sh"; then + . "/config/env/${SERVICE_NAME:-$SCRIPT_NAME}.sh" +fi +if __file_exists_with_content "/config/env/${SERVICE_NAME:-$SCRIPT_NAME}.local.sh"; then + . "/config/env/${SERVICE_NAME:-$SCRIPT_NAME}.local.sh" +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - # default exit code SERVICE_EXIT_CODE=0 # application specific -# set the binary name EXEC_CMD_NAME="${EXEC_CMD_BIN##*/}" -# set the pid file location SERVICE_PID_FILE="/run/init.d/$EXEC_CMD_NAME.pid" -# check if running -SERVICE_PID_NUMBER="$(__pgrep)" -# set full path -EXEC_CMD_BIN="$(type -P "$EXEC_CMD_BIN" || echo "$EXEC_CMD_BIN")" -# set full path -EXEC_PRE_SCRIPT="$(type -P "$EXEC_PRE_SCRIPT" || echo "$EXEC_PRE_SCRIPT")" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +if [ -n "$EXEC_CMD_BIN" ]; then + _resolved="$(type -P "$EXEC_CMD_BIN" 2>/dev/null || true)" || _resolved="" + [ -n "$_resolved" ] && EXEC_CMD_BIN="$_resolved" +fi +if [ -n "$EXEC_PRE_SCRIPT" ]; then + _resolved="$(type -P "$EXEC_PRE_SCRIPT" 2>/dev/null || true)" || _resolved="" + [ -n "$_resolved" ] && EXEC_PRE_SCRIPT="$_resolved" +fi +unset _resolved +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Only run check when explicitly requested +if [ "$1" = "check" ] && __check_service "$1"; then + SERVICE_IS_RUNNING=yes +elif [ "$1" = "check" ]; then + SERVICE_IS_RUNNING="no" +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - +# ensure needed directories exists +if [ ! -d "$LOG_DIR" ]; then + mkdir -p "$LOG_DIR" +fi +if [ ! -d "$RUN_DIR" ]; then + mkdir -p "$RUN_DIR" +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - # create auth directories -[ -n "$USER_FILE_PREFIX" ] && { [ -d "$USER_FILE_PREFIX" ] || mkdir -p "$USER_FILE_PREFIX"; } -[ -n "$ROOT_FILE_PREFIX" ] && { [ -d "$ROOT_FILE_PREFIX" ] || mkdir -p "$ROOT_FILE_PREFIX"; } -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[ "$IS_WEB_SERVER" = "yes" ] && RESET_ENV="yes" -[ -n "$RUNAS_USER" ] || RUNAS_USER="root" -[ -n "$SERVICE_USER" ] || SERVICE_USER="${RUNAS_USER:-root}" -[ -n "$SERVICE_GROUP" ] || SERVICE_GROUP="${RUNAS_USER:-root}" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +if [ -n "$USER_FILE_PREFIX" ]; then + if [ ! -d "$USER_FILE_PREFIX" ]; then + mkdir -p "$USER_FILE_PREFIX" + fi +fi +if [ -n "$ROOT_FILE_PREFIX" ]; then + if [ ! -d "$ROOT_FILE_PREFIX" ]; then + mkdir -p "$ROOT_FILE_PREFIX" + fi +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - +if [ -z "$RUNAS_USER" ]; then + RUNAS_USER="root" +fi +if [ -z "$SERVICE_USER" ]; then + SERVICE_USER="$RUNAS_USER" +fi +if [ -z "$SERVICE_GROUP" ]; then + SERVICE_GROUP="${SERVICE_USER:-$RUNAS_USER}" +fi +if [ "$IS_WEB_SERVER" = "yes" ]; then + RESET_ENV="yes" + __is_htdocs_mounted +fi +if [ "$IS_WEB_SERVER" = "yes" ] && [ -z "$SERVICE_PORT" ]; then + SERVICE_PORT="80" +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - # Database env -if [ "$IS_DATABASE_SERVICE" = "yes" ]; then +if [ "$IS_DATABASE_SERVICE" = "yes" ] || [ "$USES_DATABASE_SERVICE" = "yes" ]; then RESET_ENV="no" DATABASE_CREATE="${ENV_DATABASE_CREATE:-$DATABASE_CREATE}" - DATABASE_USER="${ENV_DATABASE_USER:-${DATABASE_USER:-$user_name}}" - DATABASE_PASSWORD="${ENV_DATABASE_PASSWORD:-${DATABASE_PASSWORD:-$user_pass}}" - DATABASE_ROOT_USER="${ENV_DATABASE_ROOT_USER:-${DATABASE_ROOT_USER:-$root_user_name}}" - DATABASE_ROOT_PASSWORD="${ENV_DATABASE_ROOT_PASSWORD:-${DATABASE_ROOT_PASSWORD:-$root_user_pass}}" + DATABASE_USER_NORMAL="${ENV_DATABASE_USER:-${DATABASE_USER_NORMAL:-$user_name}}" + DATABASE_PASS_NORMAL="${ENV_DATABASE_PASSWORD:-${DATABASE_PASS_NORMAL:-$user_pass}}" + DATABASE_USER_ROOT="${ENV_DATABASE_ROOT_USER:-${DATABASE_USER_ROOT:-$root_user_name}}" + DATABASE_PASS_ROOT="${ENV_DATABASE_ROOT_PASSWORD:-${DATABASE_PASS_ROOT:-$root_user_pass}}" + if [ -n "$DATABASE_PASS_NORMAL" ]; then + if [ ! -f "${USER_FILE_PREFIX}/db_pass_user" ]; then + echo "$DATABASE_PASS_NORMAL" >"${USER_FILE_PREFIX}/db_pass_user" + fi + fi + if [ -n "$DATABASE_PASS_ROOT" ]; then + if [ ! -f "${ROOT_FILE_PREFIX}/db_pass_root" ]; then + echo "$DATABASE_PASS_ROOT" >"${ROOT_FILE_PREFIX}/db_pass_root" + fi + fi fi -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# Allow per init script usernames and passwords -__file_exists_with_content "$ETC_DIR/auth/user/name" && user_name="$(<"$ETC_DIR/auth/user/name")" -__file_exists_with_content "$ETC_DIR/auth/user/pass" && user_pass="$(<"$ETC_DIR/auth/user/pass")" -__file_exists_with_content "$ETC_DIR/auth/root/name" && root_user_name="$(<"$ETC_DIR/auth/root/name")" -__file_exists_with_content "$ETC_DIR/auth/root/pass" && root_user_pass="$(<"$ETC_DIR/auth/root/pass")" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# set password to random if variable is random -[ "$user_pass" = "random" ] && user_pass="$(__random_password)" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[ "$root_user_pass" = "random" ] && root_user_pass="$(__random_password)" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# Allow setting initial users and passwords via environment -user_name="$(eval echo "${ENV_USER_NAME:-$user_name}")" -user_pass="$(eval echo "${ENV_USER_PASS:-$user_pass}")" -root_user_name="$(eval echo "${ENV_ROOT_USER_NAME:-$root_user_name}")" -root_user_pass="$(eval echo "${ENV_ROOT_USER_PASS:-$root_user_pass}")" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - +# [DATABASE_DIR_[SQLITE,REDIS,POSTGRES,MARIADB,COUCHDB,MONGODB,SUPABASE]] +if [ "$DATABASE_SERVICE_TYPE" = "custom" ]; then + DATABASE_DIR="${DATABASE_DIR_CUSTOM:-/data/db/custom}" + DATABASE_BASE_DIR="${DATABASE_DIR_CUSTOM:-/data/db/custom}" + DATABASE_ADMIN_WWW_ROOT="${DATABASE_ADMIN_WWW_ROOT_CUSTOM:-/usr/local/share/httpd/admin/databases}" + if [ -d "$DATABASE_ADMIN_WWW_ROOT" ]; then + SERVER_ADMIN_URL="${SERVER_ADMIN_URL_CUSTOM:-/admin/dbadmin}" + fi +elif [ "$SERVICE_NAME" = "redis" ] || [ "$DATABASE_SERVICE_TYPE" = "redis" ]; then + DATABASE_DIR="${DATABASE_DIR_REDIS:-/data/db/redis}" + DATABASE_BASE_DIR="${DATABASE_DIR_REDIS:-/data/db/redis}" + DATABASE_ADMIN_WWW_ROOT="${DATABASE_ADMIN_WWW_ROOT_REDIS:-/usr/local/share/httpd/admin/redis}" + if [ -d "$DATABASE_ADMIN_WWW_ROOT" ]; then + SERVER_ADMIN_URL="${SERVER_ADMIN_URL_REDIS:-/admin/redis}" + fi +elif [ "$SERVICE_NAME" = "postgres" ] || [ "$DATABASE_SERVICE_TYPE" = "postgres" ]; then + DATABASE_DIR="${DATABASE_DIR_POSTGRES:-/data/db/postgres}" + DATABASE_BASE_DIR="${DATABASE_DIR_POSTGRES:-/data/db/postgres}" + DATABASE_ADMIN_WWW_ROOT="${DATABASE_ADMIN_WWW_ROOT_POSTGRES:-/usr/local/share/httpd/admin/postgres}" + if [ -d "$DATABASE_ADMIN_WWW_ROOT" ]; then + SERVER_ADMIN_URL="${SERVER_ADMIN_URL_POSTGRES:-/admin/postgres}" + fi +elif [ "$SERVICE_NAME" = "mariadb" ] || [ "$DATABASE_SERVICE_TYPE" = "mariadb" ]; then + DATABASE_DIR="${DATABASE_DIR_MARIADB:-/data/db/mariadb}" + DATABASE_BASE_DIR="${DATABASE_DIR_MARIADB:-/data/db/mariadb}" + DATABASE_ADMIN_WWW_ROOT="${DATABASE_ADMIN_WWW_ROOT_MARIADB:-/usr/local/share/httpd/admin/mysql}" + if [ -d "$DATABASE_ADMIN_WWW_ROOT" ]; then + SERVER_ADMIN_URL="${SERVER_ADMIN_URL_MARIADB:-/admin/mysql}" + fi +elif [ "$SERVICE_NAME" = "mysql" ] || [ "$DATABASE_SERVICE_TYPE" = "mysql" ]; then + DATABASE_DIR="${DATABASE_DIR_MYSQL:-/data/db/mysql}" + DATABASE_BASE_DIR="${DATABASE_DIR_MYSQL:-/data/db/mysql}" + DATABASE_ADMIN_WWW_ROOT="${DATABASE_ADMIN_WWW_ROOT_MYSQL:-/usr/local/share/httpd/admin/mysql}" + if [ -d "$DATABASE_ADMIN_WWW_ROOT" ]; then + SERVER_ADMIN_URL="${SERVER_ADMIN_URL_MYSQL:-/admin/mysql}" + fi +elif [ "$SERVICE_NAME" = "couchdb" ] || [ "$DATABASE_SERVICE_TYPE" = "couchdb" ]; then + DATABASE_DIR="${DATABASE_DIR_COUCHDB:-/data/db/couchdb}" + DATABASE_BASE_DIR="${DATABASE_DIR_COUCHDB:-/data/db/couchdb}" + DATABASE_ADMIN_WWW_ROOT="${DATABASE_ADMIN_WWW_ROOT_COUCHDB:-/usr/local/share/httpd/admin/couchdb}" + if [ -d "$DATABASE_ADMIN_WWW_ROOT" ]; then + SERVER_ADMIN_URL="${SERVER_ADMIN_URL_COUCHDB:-/admin/couchdb}" + fi +elif [ "$SERVICE_NAME" = "mongodb" ] || [ "$DATABASE_SERVICE_TYPE" = "mongodb" ]; then + DATABASE_DIR="${DATABASE_DIR_MONGODB:-/data/db/mongodb}" + DATABASE_BASE_DIR="${DATABASE_DIR_MONGODB:-/data/db/mongodb}" + DATABASE_ADMIN_WWW_ROOT="${DATABASE_ADMIN_WWW_ROOT_MONGODB:-/usr/local/share/httpd/admin/mongodb}" + if [ -d "$DATABASE_ADMIN_WWW_ROOT" ]; then + SERVER_ADMIN_URL="${SERVER_ADMIN_URL_MONGODB:-/admin/mongodb}" + fi +elif [ "$SERVICE_NAME" = "supabase" ] || [ "$DATABASE_SERVICE_TYPE" = "supabase" ]; then + DATABASE_DIR="${DATABASE_DIR_SUPABASE:-/data/db/supabase}" + DATABASE_BASE_DIR="${DATABASE_DIR_SUPABASE:-/data/db/supabase}" + DATABASE_ADMIN_WWW_ROOT="${DATABASE_ADMIN_WWW_ROOT_SUPABASE:-/usr/local/share/httpd/admin/supabase}" + if [ -d "$DATABASE_ADMIN_WWW_ROOT" ]; then + SERVER_ADMIN_URL="${SERVER_ADMIN_URL_SUPBASE:-/admin/supabase}" + fi +elif [ "$SERVICE_NAME" = "sqlite" ] || [ "$DATABASE_SERVICE_TYPE" = "sqlite" ]; then + DATABASE_DIR="${DATABASE_DIR_SQLITE:-/data/db/sqlite}/$SERVER_NAME" + DATABASE_BASE_DIR="${DATABASE_DIR_SQLITE:-/data/db/sqlite}/$SERVER_NAME" + DATABASE_ADMIN_WWW_ROOT="${DATABASE_ADMIN_WWW_ROOT_SQLITE:-/usr/local/share/httpd/admin/sqlite}" + if [ -d "$DATABASE_ADMIN_WWW_ROOT" ]; then + SERVER_ADMIN_URL="${SERVER_ADMIN_URL_SQLITE:-/admin/sqlite}" + fi + if [ ! -d "$DATABASE_DIR" ]; then + mkdir -p "$DATABASE_DIR" + fi + chmod 777 "$DATABASE_DIR" +fi +if [ -n "$DATABASE_ADMIN_WWW_ROOT" ]; then + if [ ! -d "$DATABASE_ADMIN_WWW_ROOT" ]; then + mkdir -p "${DATABASE_ADMIN_WWW_ROOT}" + fi +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - # Allow variables via imports - Overwrite existing -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[ -f "/config/env/${SERVICE_NAME:-$SCRIPT_NAME}.sh" ] && . "/config/env/${SERVICE_NAME:-$SCRIPT_NAME}.sh" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -__initialize_db_users -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# Only run check -if [ "$1" = "check" ]; then - __proc_check "$EXEC_CMD_NAME" || __proc_check "$EXEC_CMD_BIN" - exit $? +if [ -f "/config/env/${SERVICE_NAME:-$SCRIPT_NAME}.sh" ]; then + . "/config/env/${SERVICE_NAME:-$SCRIPT_NAME}.sh" fi -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - +# set password to random if variable is random +if [ "$user_pass" = "random" ]; then + user_pass="$(__random_password ${RANDOM_PASS_USER:-16})" +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - +if [ "$root_user_pass" = "random" ]; then + root_user_pass="$(__random_password ${RANDOM_PASS_ROOT:-16})" +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Allow setting initial users and passwords via environment and save to file +if [ -n "$user_name" ]; then + echo "$user_name" >"${USER_FILE_PREFIX}/${SERVICE_NAME}_name" +fi +if [ -n "$user_pass" ]; then + echo "$user_pass" >"${USER_FILE_PREFIX}/${SERVICE_NAME}_pass" +fi +if [ -n "$root_user_name" ]; then + echo "$root_user_name" >"${ROOT_FILE_PREFIX}/${SERVICE_NAME}_name" +fi +if [ -n "$root_user_pass" ]; then + echo "$root_user_pass" >"${ROOT_FILE_PREFIX}/${SERVICE_NAME}_pass" +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Allow per init script usernames and passwords +if __file_exists_with_content "${USER_FILE_PREFIX}/${SERVICE_NAME}_name"; then + user_name="$(<"${USER_FILE_PREFIX}/${SERVICE_NAME}_name")" +fi +if __file_exists_with_content "${USER_FILE_PREFIX}/${SERVICE_NAME}_pass"; then + user_pass="$(<"${USER_FILE_PREFIX}/${SERVICE_NAME}_pass")" +fi +if __file_exists_with_content "${ROOT_FILE_PREFIX}/${SERVICE_NAME}_name"; then + root_user_name="$(<"${ROOT_FILE_PREFIX}/${SERVICE_NAME}_name")" +fi +if __file_exists_with_content "${ROOT_FILE_PREFIX}/${SERVICE_NAME}_pass"; then + root_user_pass="$(<"${ROOT_FILE_PREFIX}/${SERVICE_NAME}_pass")" +fi +if __file_exists_with_content "${USER_FILE_PREFIX}/db_pass_user"; then + DATABASE_PASS_NORMAL="$(<"${USER_FILE_PREFIX}/db_pass_user")" +fi +if __file_exists_with_content "${ROOT_FILE_PREFIX}/db_pass_root"; then + DATABASE_PASS_ROOT="$(<"${ROOT_FILE_PREFIX}/db_pass_root")" +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - +# set hostname for script +sysname="${SERVER_NAME:-${FULL_DOMAIN_NAME:-$HOSTNAME}}" +# - - - - - - - - - - - - - - - - - - - - - - - - - +__create_service_env +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Setup /config directories +__init_config_etc +# - - - - - - - - - - - - - - - - - - - - - - - - - +# pre-run function +__execute_prerun +# - - - - - - - - - - - - - - - - - - - - - - - - - +# create user if needed +__create_service_user "$SERVICE_USER" "$SERVICE_GROUP" "${WORK_DIR:-/home/$SERVICE_USER}" "${SERVICE_UID:-}" "${SERVICE_GID:-}" +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Modify user if needed +__set_user_group_id $SERVICE_USER ${SERVICE_UID:-} ${SERVICE_GID:-} +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Create base directories +__setup_directories +# - - - - - - - - - - - - - - - - - - - - - - - - - # set switch user command -if [ "$RUNAS_USER" = "root" ]; then - su_cmd() { eval "$@" || return 1; } -elif [ "$(builtin type -P gosu)" ]; then - su_exec="gosu $RUNAS_USER" - su_cmd() { gosu $RUNAS_USER "$@" || return 1; } -elif [ "$(builtin type -P runuser)" ]; then - su_exec="runuser -u $RUNAS_USER" - su_cmd() { runuser -u $RUNAS_USER "$@" || return 1; } -elif [ "$(builtin type -P sudo)" ]; then - su_exec="sudo -u $RUNAS_USER" - su_cmd() { sudo -u $RUNAS_USER "$@" || return 1; } -elif [ "$(builtin type -P su)" ]; then - su_exec="su -s /bin/sh - $RUNAS_USER" - su_cmd() { su -s /bin/sh - $RUNAS_USER -c "$@" || return 1; } -else - su_cmd() { echo "Can not switch to $RUNAS_USER: attempting to run as root" && eval "$*" || return 1; } -fi -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# Change to working directory -[ "$HOME" = "/root" ] && [ "$RUNAS_USER" != "root" ] && [ "$PWD" != "/tmp" ] && __cd "/tmp" && echo "Changed to $PWD" -[ "$HOME" = "/root" ] && [ "$SERVICE_USER" != "root" ] && [ "$PWD" != "/tmp" ] && __cd "/tmp" && echo "Changed to $PWD" -[ -n "$WORK_DIR" ] && [ -n "$EXEC_CMD_BIN" ] && [ "$PWD" != "$WORK_DIR" ] && __cd "$WORK_DIR" && echo "Changed to $PWD" -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +__switch_to_user +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Initialize the home/working dir +__init_working_dir +# - - - - - - - - - - - - - - - - - - - - - - - - - # show init message __pre_message -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - +# +__initialize_db_users +# - - - - - - - - - - - - - - - - - - - - - - - - - # Initialize ssl __update_ssl_conf __update_ssl_certs -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# Updating config files -__create_service_env -__update_conf_files +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Set permissions in ${USER_FILE_PREFIX} and ${ROOT_FILE_PREFIX} +__run_secure_function +# - - - - - - - - - - - - - - - - - - - - - - - - - +__run_precopy +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Copy /config to /etc +for config_2_etc in $CONF_DIR $ADDITIONAL_CONFIG_DIRS; do + __initialize_system_etc "$config_2_etc" 2>/dev/stderr | tee -p -a "/data/logs/init.txt" +done +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Replace variables +__initialize_replace_variables "$ETC_DIR" "$CONF_DIR" "$ADDITIONAL_CONFIG_DIRS" "$WWW_ROOT_DIR" +# - - - - - - - - - - - - - - - - - - - - - - - - - +# __initialize_database -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Updating config files +__update_conf_files +# - - - - - - - - - - - - - - - - - - - - - - - - - # run the pre execute commands __pre_execute -__run_secure_function -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -__run_start_script "$@" |& tee -p -a "/data/logs/entrypoint.log" &>/dev/null -if [ "$?" -ne 0 ] && [ -n "$EXEC_CMD_BIN" ]; then - eval echo "Failed to execute: ${cmd_exec:-$EXEC_CMD_BIN $EXEC_CMD_ARGS}" |& tee -p -a "/data/logs/entrypoint.log" "$LOG_DIR/init.txt" - rm -Rf "$SERVICE_PID_FILE" - SERVICE_EXIT_CODE=10 - SERVICE_IS_RUNNING="false" +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Set permissions +__fix_permissions "$SERVICE_USER" "$SERVICE_GROUP" +# - - - - - - - - - - - - - - - - - - - - - - - - - +# +__run_pre_execute_checks 2>/dev/stderr | tee -a -p "/data/logs/entrypoint.log" "/data/logs/init.txt" || return 20 +# - - - - - - - - - - - - - - - - - - - - - - - - - +__run_start_script 2>>/dev/stderr | tee -p -a "/data/logs/entrypoint.log" +errorCode=${PIPESTATUS[0]} +if [ -n "$EXEC_CMD_BIN" ]; then + if [ "$errorCode" -eq 0 ]; then + SERVICE_EXIT_CODE=0 + SERVICE_IS_RUNNING="yes" + else + SERVICE_EXIT_CODE=$errorCode + SERVICE_IS_RUNNING="${SERVICE_IS_RUNNING:-no}" + if [ ! -s "$SERVICE_PID_FILE" ]; then + rm -Rf "$SERVICE_PID_FILE" + fi + fi fi -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# - - - - - - - - - - - - - - - - - - - - - - - - - +# start the post execute function in background +__post_execute 2>"/dev/stderr" | tee -p -a "/data/logs/init.txt" & +# - - - - - - - - - - - - - - - - - - - - - - - - - __script_exit $SERVICE_EXIT_CODE +# vim: set ft=sh ts=4 sw=4 st=4 et :