#!/usr/bin/env bash set -euo pipefail # ============================================================================== # Konfiguracja instalacji alternatywnej instancji MariaDB dla serwera DirectAdmin # ============================================================================== MARIADB_VERSION="10.11" MARIADB_DIR="/usr/local/mariadb-$MARIADB_VERSION" DATADIR="/var/lib/mariadb_data" PORT="33033" SOCKET="/var/lib/mariadb_data/mariadb.sock" MYSQL_USER_SOURCE_FILE="/usr/local/directadmin/conf/mysql.conf" MYSQL_CLIENT_SOURCE_FILE="/usr/local/directadmin/conf/my.cnf" ALT_MYSQL_CONF="/usr/local/directadmin/conf/alt-mysql.conf" ALT_MY_CNF="/usr/local/directadmin/conf/alt-my.cnf" ALT_METADATA_ENV="/usr/local/directadmin/conf/alt-mariadb.env" INSTANCE_CNF="/etc/alt-mariadb.cnf" NATIVE_TAKEOVER_SCRIPT="${NATIVE_TAKEOVER_SCRIPT:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/setup/native_mariadb_port_takeover.sh}" SERVICE_NAME="alt-mariadb" SERVICE_FILE="/etc/systemd/system/alt-mariadb.service" LOG_DIR="/var/log/alt-mariadb" RUN_DIR="/run/alt-mariadb" SYSTEM_USER="mysql" SYSTEM_GROUP="mysql" BIND_ADDRESS="127.0.0.1" FORCE_INSTALL="no" FORCE_REINIT="no" FORCE_SERVICE_REWRITE="yes" # Jeżeli automatyczne wykrywanie adresu tarballa zawiedzie, ustaw pełny oficjalny URL ręcznie. MARIADB_TARBALL_URL="" # Katalog roboczy pobierania. Skrypt nie używa pakietów RPM/DNF MariaDB ani MySQL. DOWNLOAD_DIR="/usr/local/src" ALT_ADMIN_USER="" ALT_ADMIN_PASS="" DA_PORT="" DA_SOCKET="" DA_HOST="" DA_DATADIR="" TARBALL_PATH="" TMP_DIR="" RESOLVED_MARIADB_RELEASE="" DOWNLOAD_URL="" log() { printf '[INFO] %s\n' "$*" } warn() { printf '[UWAGA] %s\n' "$*" >&2 } die() { printf '[BŁĄD] %s\n' "$*" >&2 exit 1 } cleanup() { if [[ -n "${TMP_DIR:-}" && -d "$TMP_DIR" ]]; then rm -rf "$TMP_DIR" fi } trap cleanup EXIT usage() { cat < [PORT] Przykłady: $0 10.11 $0 10.11 33033 $0 11.4 3020 $0 11.8 3021 Dozwolone gałęzie MariaDB: 10.11, 11.4, 11.8. Jeżeli PORT zostanie pominięty, przyjmowana jest wartość domyślna 3306. W takim przypadku skrypt najpierw przenosi natywną instancję MariaDB zarządzaną przez CustomBuild na inny port (patrz scripts/setup/native_mariadb_port_takeover.sh), aby zwolnić port 3306 dla alt-mariadb. To automatyczne przejęcie portu działa wyłącznie na AlmaLinux 8/9 - na innych systemach podaj PORT jawnie (inny niż 3306). EOF } usage_error() { usage >&2 printf '[BŁĄD] %s\n' "$*" >&2 exit 2 } apply_cli_args() { if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then usage exit 0 fi if [[ $# -eq 0 ]]; then usage exit 2 fi if [[ $# -gt 2 ]]; then usage_error "Podano zbyt wiele argumentów. Oczekiwano: MARIADB_VERSION [PORT]." fi MARIADB_VERSION="$1" if [[ $# -ge 2 && -n "${2:-}" ]]; then PORT="$2" else PORT="3306" fi MARIADB_DIR="/usr/local/mariadb-$MARIADB_VERSION" } require_root() { if [[ "$(id -u)" -ne 0 ]]; then die "Ten skrypt musi zostać uruchomiony jako root." fi } maybe_takeover_native_port() { if [[ "$PORT" != "3306" ]]; then return 0 fi [[ -x "$NATIVE_TAKEOVER_SCRIPT" ]] || die "Nie znaleziono skryptu przejęcia portu natywnego MariaDB: $NATIVE_TAKEOVER_SCRIPT" log "PORT=3306 wybrany dla alt-mariadb - uruchamiam przejęcie portu natywnej instancji MariaDB." "$NATIVE_TAKEOVER_SCRIPT" || die "Przejęcie portu natywnej instancji MariaDB nie powiodło się. Przerwano instalację alt-mariadb." } detect_os() { [[ -r /etc/os-release ]] || die "Nie można odczytać /etc/os-release." . /etc/os-release local os_id="${ID:-}" local os_like="${ID_LIKE:-}" case "$os_id" in almalinux|rhel|rocky|centos|ol) log "Wykryto zgodny system: ${PRETTY_NAME:-$os_id}." ;; *) if [[ "$os_like" == *"rhel"* || "$os_like" == *"fedora"* ]]; then warn "System wygląda na zgodny z RHEL: ${PRETTY_NAME:-$os_id}." else die "Ten skrypt jest przeznaczony dla AlmaLinux lub systemów zgodnych z RHEL." fi ;; esac } read_kv_file_value() { local file="$1" local key="$2" awk -F= -v key="$key" ' $1 == key { sub(/^[^=]*=/, "") print exit } ' "$file" } load_da_credentials() { [[ -d /usr/local/directadmin ]] || die "Nie znaleziono katalogu /usr/local/directadmin." [[ -f "$MYSQL_USER_SOURCE_FILE" ]] || die "Nie znaleziono pliku $MYSQL_USER_SOURCE_FILE." [[ -f "$MYSQL_CLIENT_SOURCE_FILE" ]] || die "Nie znaleziono pliku $MYSQL_CLIENT_SOURCE_FILE." ALT_ADMIN_USER="$(read_kv_file_value "$MYSQL_USER_SOURCE_FILE" "user")" ALT_ADMIN_PASS="$(read_kv_file_value "$MYSQL_USER_SOURCE_FILE" "passwd")" DA_PORT="$(read_kv_file_value "$MYSQL_USER_SOURCE_FILE" "port")" DA_SOCKET="$(read_kv_file_value "$MYSQL_USER_SOURCE_FILE" "socket")" DA_HOST="$(read_kv_file_value "$MYSQL_USER_SOURCE_FILE" "host")" [[ -n "$ALT_ADMIN_USER" ]] || die "Nie udało się odczytać pola user z $MYSQL_USER_SOURCE_FILE." [[ -n "$ALT_ADMIN_PASS" ]] || die "Nie udało się odczytać pola passwd z $MYSQL_USER_SOURCE_FILE." DA_DATADIR="$(detect_da_datadir || true)" log "Odczytano dane administratora DirectAdmin bez wyświetlania hasła." } detect_da_datadir() { local candidate_files=( "/etc/my.cnf" "/etc/mysql/my.cnf" ) local file="" for file in "${candidate_files[@]}"; do [[ -r "$file" ]] || continue awk -F= ' /^[[:space:]]*datadir[[:space:]]*=/ { value=$2 gsub(/^[[:space:]]+|[[:space:]]+$/, "", value) gsub(/^"|"$/, "", value) print value exit } ' "$file" return 0 done if [[ -d /var/lib/mysql ]]; then printf '/var/lib/mysql\n' fi } validate_version() { case "$MARIADB_VERSION" in 10.11|11.4|11.8) log "Wybrano obsługiwaną wersję MariaDB: $MARIADB_VERSION." ;; *) die "Nieobsługiwana wersja MariaDB: $MARIADB_VERSION. Dozwolone wartości: 10.11, 11.4, 11.8." ;; esac } validate_port() { local numeric_port="" [[ "$PORT" =~ ^[0-9]+$ ]] || die "Nieprawidłowy port MariaDB: $PORT. Podaj liczbę od 1 do 65535." numeric_port=$((10#$PORT)) (( numeric_port >= 1 && numeric_port <= 65535 )) || die "Nieprawidłowy port MariaDB: $PORT. Podaj liczbę od 1 do 65535." log "Wybrano port TCP alternatywnej instancji MariaDB: $PORT." } canonical_path() { local path="$1" if command -v realpath >/dev/null 2>&1; then realpath -m "$path" else printf '%s\n' "$path" fi } port_is_listening() { local port="$1" if command -v ss >/dev/null 2>&1; then ss -ltn "( sport = :$port )" 2>/dev/null | awk 'NR > 1 { found=1 } END { exit !found }' return $? fi if command -v lsof >/dev/null 2>&1; then lsof -nP -iTCP:"$port" -sTCP:LISTEN >/dev/null 2>&1 return $? fi warn "Nie znaleziono narzędzia ss ani lsof, więc nie można pewnie sprawdzić zajętości portu." return 1 } validate_paths() { local alt_socket_canon da_socket_canon alt_datadir_canon da_datadir_canon alt_socket_canon="$(canonical_path "$SOCKET")" da_socket_canon="$(canonical_path "${DA_SOCKET:-}")" alt_datadir_canon="$(canonical_path "$DATADIR")" da_datadir_canon="$(canonical_path "${DA_DATADIR:-}")" [[ "$alt_socket_canon" != "$da_socket_canon" ]] || die "Alternatywny socket jest taki sam jak socket DirectAdmin: $SOCKET." [[ "$alt_datadir_canon" != "$(canonical_path /var/lib/mysql)" ]] || die "Alternatywny katalog danych nie może wskazywać na /var/lib/mysql." if [[ -n "${DA_DATADIR:-}" ]]; then [[ "$alt_datadir_canon" != "$da_datadir_canon" ]] || die "Alternatywny katalog danych jest taki sam jak wykryty katalog danych DirectAdmin: $DATADIR." fi if [[ -n "${DA_PORT:-}" ]]; then [[ "$PORT" != "$DA_PORT" ]] || die "Alternatywny port $PORT jest taki sam jak port MariaDB DirectAdmin." fi if port_is_listening "$PORT"; then die "Port $PORT jest już zajęty. Zmień PORT w sekcji konfiguracji." fi if [[ -S "$SOCKET" ]] && ! systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null; then die "Socket $SOCKET już istnieje, ale usługa $SERVICE_NAME nie jest aktywna. Przerwano dla bezpieczeństwa." fi [[ "$SOCKET" == "$DATADIR"* || "$(dirname "$SOCKET")" != "/var/lib/mysql" ]] || die "Socket nie może znajdować się w katalogu DirectAdmin MariaDB." log "Sprawdzono rozdzielenie portu, socketu i katalogu danych od instancji DirectAdmin." } install_dependencies() { local deps=(curl tar gzip xz libaio ncurses-compat-libs) local dep="" local safe_deps=() command -v dnf >/dev/null 2>&1 || die "Nie znaleziono dnf. Ten skrypt instaluje neutralne zależności wyłącznie przez dnf." for dep in "${deps[@]}"; do if [[ "$dep" == *mariadb* || "$dep" == *mysql* || "$dep" == *MariaDB* || "$dep" == *MySQL* ]]; then die "Wewnętrzny błąd bezpieczeństwa: próba instalacji niedozwolonego pakietu $dep." fi safe_deps+=("$dep") done log "Instaluję neutralne zależności systemowe bez pakietów MariaDB/MySQL." dnf install -y "${safe_deps[@]}" } detect_architecture() { local machine machine="$(uname -m)" case "$machine" in x86_64|amd64) printf 'x86_64\n' ;; *) die "Nieobsługiwana architektura: $machine. Skrypt obsługuje automatycznie tylko x86_64/amd64." ;; esac } resolve_latest_release_from_archive() { local series="$1" local listing="" local release="" listing="$(curl -fsSL "https://archive.mariadb.org/" || true)" release="$(printf '%s\n' "$listing" \ | grep -Eo "mariadb-${series//./\\.}\.[0-9]+/" \ | sed 's#^mariadb-##; s#/$##' \ | sort -V \ | tail -n 1)" [[ -n "$release" ]] || return 1 printf '%s\n' "$release" } url_exists() { local url="$1" curl -fsIL --retry 2 --connect-timeout 15 "$url" >/dev/null 2>&1 } resolve_tarball_from_directory() { local release="$1" local arch="$2" local dir_url="https://archive.mariadb.org/mariadb-${release}/bintar-linux-systemd-${arch}/" local listing="" local file="" listing="$(curl -fsSL "$dir_url" || true)" file="$(printf '%s\n' "$listing" \ | grep -Eo "mariadb-${release}-linux-systemd-${arch}\.tar\.(gz|xz)" \ | head -n 1)" [[ -n "$file" ]] || return 1 printf '%s%s\n' "$dir_url" "$file" } resolve_download_url() { local arch="" local candidate="" local candidates=() arch="$(detect_architecture)" if [[ -n "$MARIADB_TARBALL_URL" ]]; then DOWNLOAD_URL="$MARIADB_TARBALL_URL" log "Używam ręcznie ustawionego adresu tarballa MariaDB." return 0 fi log "Wyszukuję oficjalny tarball MariaDB w archiwum MariaDB dla gałęzi $MARIADB_VERSION." warn "Nazwy tarballi mogą zależeć od schematu mirrorów MariaDB; w razie błędu ustaw MARIADB_TARBALL_URL ręcznie." RESOLVED_MARIADB_RELEASE="$(resolve_latest_release_from_archive "$MARIADB_VERSION" || true)" [[ -n "$RESOLVED_MARIADB_RELEASE" ]] || die "Nie udało się wykryć najnowszego wydania dla gałęzi $MARIADB_VERSION." if candidate="$(resolve_tarball_from_directory "$RESOLVED_MARIADB_RELEASE" "$arch" || true)"; then DOWNLOAD_URL="$candidate" log "Wykryto tarball MariaDB $RESOLVED_MARIADB_RELEASE." return 0 fi candidates=( "https://archive.mariadb.org/mariadb-${RESOLVED_MARIADB_RELEASE}/bintar-linux-systemd-${arch}/mariadb-${RESOLVED_MARIADB_RELEASE}-linux-systemd-${arch}.tar.gz" "https://archive.mariadb.org/mariadb-${RESOLVED_MARIADB_RELEASE}/bintar-linux-systemd-${arch}/mariadb-${RESOLVED_MARIADB_RELEASE}-linux-systemd-${arch}.tar.xz" "https://downloads.mariadb.org/f/mariadb-${RESOLVED_MARIADB_RELEASE}/bintar-linux-systemd-${arch}/mariadb-${RESOLVED_MARIADB_RELEASE}-linux-systemd-${arch}.tar.gz/from/https%3A//archive.mariadb.org/" ) for candidate in "${candidates[@]}"; do if url_exists "$candidate"; then DOWNLOAD_URL="$candidate" log "Wykryto działający adres tarballa MariaDB." return 0 fi done die "Nie udało się automatycznie ustalić URL tarballa. Ustaw MARIADB_TARBALL_URL w sekcji konfiguracji." } download_tarball() { local filename="" [[ -n "$DOWNLOAD_URL" ]] || die "Brak adresu pobierania tarballa." install -d -m 0755 "$DOWNLOAD_DIR" filename="$(basename "${DOWNLOAD_URL%%\?*}")" [[ "$filename" == *.tar.gz || "$filename" == *.tar.xz || "$filename" == *.tgz ]] || filename="mariadb-${RESOLVED_MARIADB_RELEASE:-$MARIADB_VERSION}-linux-systemd.tar.gz" TARBALL_PATH="$DOWNLOAD_DIR/$filename" log "Pobieram oficjalny tarball MariaDB do $TARBALL_PATH." curl -fL --retry 3 --connect-timeout 20 -o "$TARBALL_PATH" "$DOWNLOAD_URL" [[ -s "$TARBALL_PATH" ]] || die "Pobrany tarball jest pusty albo nie istnieje: $TARBALL_PATH." } extract_tarball() { local parent_dir="" local extracted_dir="" local backup_dir="" [[ -s "$TARBALL_PATH" ]] || die "Nie znaleziono tarballa do rozpakowania." if [[ -d "$MARIADB_DIR" ]] && [[ -n "$(find "$MARIADB_DIR" -mindepth 1 -maxdepth 1 -print -quit)" ]]; then if [[ -x "$MARIADB_DIR/bin/mariadbd" && "$FORCE_INSTALL" != "yes" ]]; then log "Katalog $MARIADB_DIR już zawiera binaria MariaDB, używam istniejącej instalacji programu." return 0 fi if [[ "$FORCE_INSTALL" != "yes" ]]; then die "Katalog $MARIADB_DIR już istnieje i nie jest pusty. Ustaw FORCE_INSTALL=\"yes\", aby pozwolić na bezpieczne zastąpienie." fi backup_dir="${MARIADB_DIR}.backup.$(date +%Y%m%d%H%M%S)" log "Tworzę kopię istniejącego katalogu programu: $backup_dir." mv "$MARIADB_DIR" "$backup_dir" fi parent_dir="$(dirname "$MARIADB_DIR")" install -d -m 0755 "$parent_dir" TMP_DIR="$(mktemp -d)" log "Rozpakowuję tarball MariaDB do katalogu tymczasowego." tar -xf "$TARBALL_PATH" -C "$TMP_DIR" extracted_dir="$(find "$TMP_DIR" -mindepth 1 -maxdepth 1 -type d -print -quit)" [[ -n "$extracted_dir" ]] || die "Tarball nie zawiera oczekiwanego katalogu głównego MariaDB." rm -rf "$MARIADB_DIR" mv "$extracted_dir" "$MARIADB_DIR" chown -R root:root "$MARIADB_DIR" log "MariaDB została rozpakowana do $MARIADB_DIR." } prepare_user_and_dirs() { local socket_dir="" if getent passwd "$SYSTEM_USER" >/dev/null 2>&1 && getent group "$SYSTEM_GROUP" >/dev/null 2>&1; then log "Używam istniejącego użytkownika systemowego $SYSTEM_USER:$SYSTEM_GROUP." elif getent passwd mysql >/dev/null 2>&1 && getent group mysql >/dev/null 2>&1; then SYSTEM_USER="mysql" SYSTEM_GROUP="mysql" log "Używam istniejącego użytkownika systemowego mysql:mysql." else SYSTEM_USER="altmysql" SYSTEM_GROUP="altmysql" if ! getent group "$SYSTEM_GROUP" >/dev/null 2>&1; then groupadd --system "$SYSTEM_GROUP" fi if ! getent passwd "$SYSTEM_USER" >/dev/null 2>&1; then useradd --system --gid "$SYSTEM_GROUP" --home-dir "$DATADIR" --shell /sbin/nologin "$SYSTEM_USER" fi log "Utworzono dedykowanego użytkownika systemowego $SYSTEM_USER:$SYSTEM_GROUP." fi socket_dir="$(dirname "$SOCKET")" install -d -m 0750 -o "$SYSTEM_USER" -g "$SYSTEM_GROUP" "$DATADIR" install -d -m 0750 -o "$SYSTEM_USER" -g "$SYSTEM_GROUP" "$LOG_DIR" install -d -m 0750 -o "$SYSTEM_USER" -g "$SYSTEM_GROUP" "$socket_dir" install -d -m 0750 -o "$SYSTEM_USER" -g "$SYSTEM_GROUP" "$RUN_DIR" chown -R "$SYSTEM_USER:$SYSTEM_GROUP" "$DATADIR" "$LOG_DIR" "$socket_dir" } backup_existing_file() { local file="$1" local backup="" if [[ -e "$file" ]]; then backup="${file}.backup.$(date +%Y%m%d%H%M%S)" cp -a "$file" "$backup" log "Utworzono kopię pliku $file jako $backup." fi } write_file_atomic() { local target="$1" local mode="$2" local owner="$3" local group="$4" local tmp_file="" tmp_file="$(mktemp)" cat > "$tmp_file" install -m "$mode" -o "$owner" -g "$group" "$tmp_file" "$target" rm -f "$tmp_file" } cnf_double_quote() { local value="$1" value="${value//\\/\\\\}" value="${value//\"/\\\"}" printf '%s' "$value" } write_instance_config() { backup_existing_file "$INSTANCE_CNF" write_file_atomic "$INSTANCE_CNF" 0640 root "$SYSTEM_GROUP" </dev/null 2>&1; then printf 'diradmin\n' else printf 'root\n' fi } write_da_style_credential_files() { local group_name="" local cnf_user="" local cnf_pass="" local cnf_socket="" local credential_mode="0600" group_name="$(credential_group)" if [[ "$group_name" == "diradmin" ]]; then credential_mode="0640" fi cnf_user="$(cnf_double_quote "$ALT_ADMIN_USER")" cnf_pass="$(cnf_double_quote "$ALT_ADMIN_PASS")" cnf_socket="$(cnf_double_quote "$SOCKET")" backup_existing_file "$ALT_MYSQL_CONF" backup_existing_file "$ALT_MY_CNF" umask 077 write_file_atomic "$ALT_MYSQL_CONF" "$credential_mode" root "$group_name" <&2 || true else warn "Plik logu $LOG_DIR/alt-mariadb.log jeszcze nie istnieje." fi if command -v journalctl >/dev/null 2>&1; then warn "Ostatnie wpisy journalctl dla $SERVICE_NAME:" journalctl -u "$SERVICE_NAME" -n 80 --no-pager >&2 || true fi } start_service() { local i="" log "Włączam i uruchamiam usługę $SERVICE_NAME." systemctl enable "$SERVICE_NAME" systemctl restart "$SERVICE_NAME" for i in $(seq 1 60); do if [[ -S "$SOCKET" ]]; then log "Socket alternatywnej instancji jest dostępny." return 0 fi sleep 1 done systemctl status "$SERVICE_NAME" --no-pager || true print_service_diagnostics die "Socket $SOCKET nie pojawił się w ciągu 60 sekund." } sql_quote() { local value="$1" value="${value//\\/\\\\}" value="${value//\'/\'\'}" printf "'%s'" "$value" } run_sql_as_root_or_admin() { local sql="$1" if "$MARIADB_DIR/bin/mariadb" --defaults-extra-file="$ALT_MY_CNF" -e "SELECT 1;" >/dev/null 2>&1; then printf '%s\n' "$sql" | "$MARIADB_DIR/bin/mariadb" --defaults-extra-file="$ALT_MY_CNF" return 0 fi if "$MARIADB_DIR/bin/mariadb" --protocol=socket --socket="$SOCKET" -uroot -e "SELECT 1;" >/dev/null 2>&1; then printf '%s\n' "$sql" | "$MARIADB_DIR/bin/mariadb" --protocol=socket --socket="$SOCKET" -uroot return 0 fi die "Nie można zalogować się do alternatywnej instancji jako root bez hasła ani przez plik $ALT_MY_CNF." } create_admin_user() { local quoted_user="" local quoted_pass="" local sql="" quoted_user="$(sql_quote "$ALT_ADMIN_USER")" quoted_pass="$(sql_quote "$ALT_ADMIN_PASS")" sql=" CREATE USER IF NOT EXISTS ${quoted_user}@'localhost' IDENTIFIED BY ${quoted_pass}; ALTER USER ${quoted_user}@'localhost' IDENTIFIED BY ${quoted_pass}; GRANT ALL PRIVILEGES ON *.* TO ${quoted_user}@'localhost' WITH GRANT OPTION; CREATE USER IF NOT EXISTS ${quoted_user}@'127.0.0.1' IDENTIFIED BY ${quoted_pass}; ALTER USER ${quoted_user}@'127.0.0.1' IDENTIFIED BY ${quoted_pass}; GRANT ALL PRIVILEGES ON *.* TO ${quoted_user}@'127.0.0.1' WITH GRANT OPTION; FLUSH PRIVILEGES; " log "Tworzę lub aktualizuję konto administratora alternatywnej instancji bez wyświetlania hasła." run_sql_as_root_or_admin "$sql" } run_final_tests() { log "Wykonuję test połączenia przez socket." "$MARIADB_DIR/bin/mariadb" --defaults-extra-file="$ALT_MY_CNF" -e "SELECT VERSION();" log "Wykonuję test połączenia TCP do 127.0.0.1 bez wyświetlania hasła w komunikacie." "$MARIADB_DIR/bin/mariadb" \ --defaults-extra-file="$ALT_MY_CNF" \ --protocol=TCP \ --host=127.0.0.1 \ --port="$PORT" \ -e "SELECT VERSION();" } rerun_plugin_install_if_available() { local script_dir="" local plugin_install="" if [[ "${SKIP_PLUGIN_INSTALL_REFRESH:-0}" == "1" ]]; then warn "Pominięto odświeżenie install.sh pluginu, bo SKIP_PLUGIN_INSTALL_REFRESH=1." return 0 fi script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" plugin_install="$script_dir/install.sh" if [[ ! -f "$plugin_install" ]]; then warn "Nie znaleziono install.sh pluginu przy $plugin_install; pomijam odświeżenie pluginu." return 0 fi log "Odświeżam instalację pluginu alt-mysql przez $plugin_install." if [[ -x "$plugin_install" ]]; then "$plugin_install" else bash "$plugin_install" fi } print_summary() { cat <