Files
alt-mysql/scripts/install_db.sh
T
2026-07-04 15:48:19 +02:00

873 lines
27 KiB
Bash
Executable File

#!/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"
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 <<EOF
Użycie:
$0 <MARIADB_VERSION> <PORT>
Przykłady:
$0 10.11 33033
$0 11.4 3020
$0 11.8 3021
Dozwolone gałęzie MariaDB: 10.11, 11.4, 11.8.
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
if [[ $# -lt 2 ]]; then
usage_error "Podaj wersję MariaDB i port TCP, np. 11.4 3020."
fi
if [[ $# -ge 1 && -n "${1:-}" ]]; then
MARIADB_VERSION="$1"
fi
if [[ $# -ge 2 && -n "${2:-}" ]]; then
PORT="$2"
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
}
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" <<EOF
[client]
port=$PORT
socket=$SOCKET
[mariadb]
user=$SYSTEM_USER
basedir=$MARIADB_DIR
datadir=$DATADIR
port=$PORT
socket=$SOCKET
pid-file=$RUN_DIR/alt-mariadb.pid
bind-address=$BIND_ADDRESS
skip-name-resolve
log-error=$LOG_DIR/alt-mariadb.log
plugin-dir=$MARIADB_DIR/lib/plugin
tmpdir=/tmp
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
max_connections=100
innodb_buffer_pool_size=512M
innodb_log_file_size=256M
local-infile=0
symbolic-links=0
EOF
log "Zapisano konfigurację alternatywnej instancji w $INSTANCE_CNF z prawem odczytu dla grupy $SYSTEM_GROUP."
}
credential_group() {
if getent group diradmin >/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" <<EOF
host=
passwd=$ALT_ADMIN_PASS
port=$PORT
socket=$SOCKET
user=$ALT_ADMIN_USER
EOF
write_file_atomic "$ALT_MY_CNF" "$credential_mode" root "$group_name" <<EOF
[client]
user="$cnf_user"
password="$cnf_pass"
protocol="socket"
socket="$cnf_socket"
EOF
log "Zapisano pliki poświadczeń DirectAdmin dla alternatywnej instancji bez wyświetlania hasła."
}
sh_single_quote() {
local value="$1"
value="${value//\'/\'\\\'\'}"
printf "'%s'" "$value"
}
write_alt_metadata_env() {
local group_name=""
local metadata_mode="0600"
group_name="$(credential_group)"
if [[ "$group_name" == "diradmin" ]]; then
metadata_mode="0640"
fi
backup_existing_file "$ALT_METADATA_ENV"
write_file_atomic "$ALT_METADATA_ENV" "$metadata_mode" root "$group_name" <<EOF
# Metadata alternatywnej instancji MariaDB zarządzanej przez install_db.sh.
# Ten plik jest kontraktem dla pluginu DirectAdmin i nie jest natywną konfiguracją MySQL DirectAdmina.
SERVICE_NAME=$(sh_single_quote "$SERVICE_NAME")
MARIADB_VERSION=$(sh_single_quote "$MARIADB_VERSION")
MARIADB_RELEASE=$(sh_single_quote "${RESOLVED_MARIADB_RELEASE:-$MARIADB_VERSION}")
MARIADB_DIR=$(sh_single_quote "$MARIADB_DIR")
DATADIR=$(sh_single_quote "$DATADIR")
PORT=$(sh_single_quote "$PORT")
SOCKET=$(sh_single_quote "$SOCKET")
ALT_MYSQL_CONF=$(sh_single_quote "$ALT_MYSQL_CONF")
ALT_MY_CNF=$(sh_single_quote "$ALT_MY_CNF")
INSTANCE_CNF=$(sh_single_quote "$INSTANCE_CNF")
SERVICE_FILE=$(sh_single_quote "$SERVICE_FILE")
LOG_DIR=$(sh_single_quote "$LOG_DIR")
RUN_DIR=$(sh_single_quote "$RUN_DIR")
SYSTEM_USER=$(sh_single_quote "$SYSTEM_USER")
SYSTEM_GROUP=$(sh_single_quote "$SYSTEM_GROUP")
BIND_ADDRESS=$(sh_single_quote "$BIND_ADDRESS")
EOF
log "Zapisano metadane alternatywnej instancji w $ALT_METADATA_ENV."
}
datadir_is_initialized() {
[[ -d "$DATADIR/mysql" ]] && [[ -f "$DATADIR/mysql/user.frm" || -f "$DATADIR/mysql/global_priv.frm" || -f "$DATADIR/mysql/global_priv.ibd" ]]
}
initialize_datadir() {
local install_db=""
if datadir_is_initialized; then
if [[ "$FORCE_REINIT" != "yes" ]]; then
log "Katalog danych wygląda na zainicjalizowany, pomijam inicjalizację."
return 0
fi
die "FORCE_REINIT=\"yes\" wymaga ręcznego opróżnienia lub zabezpieczenia katalogu $DATADIR. Skrypt nie usuwa danych automatycznie."
fi
if [[ -n "$(find "$DATADIR" -mindepth 1 -maxdepth 1 -print -quit)" ]] && [[ "$FORCE_REINIT" != "yes" ]]; then
die "Katalog $DATADIR zawiera pliki, ale nie wygląda na zainicjalizowany katalog MariaDB. Przerwano dla bezpieczeństwa."
fi
if [[ -x "$MARIADB_DIR/scripts/mariadb-install-db" ]]; then
install_db="$MARIADB_DIR/scripts/mariadb-install-db"
elif [[ -x "$MARIADB_DIR/bin/mariadb-install-db" ]]; then
install_db="$MARIADB_DIR/bin/mariadb-install-db"
else
die "Nie znaleziono mariadb-install-db w rozpakowanym katalogu MariaDB."
fi
log "Inicjalizuję katalog danych alternatywnej instancji MariaDB."
if ! "$install_db" \
--defaults-file="$INSTANCE_CNF" \
--basedir="$MARIADB_DIR" \
--datadir="$DATADIR" \
--user="$SYSTEM_USER" \
--auth-root-authentication-method=normal; then
warn "Inicjalizacja z auth-root-authentication-method=normal nie powiodła się, próbuję wariantu zgodności."
"$install_db" \
--defaults-file="$INSTANCE_CNF" \
--basedir="$MARIADB_DIR" \
--datadir="$DATADIR" \
--user="$SYSTEM_USER"
fi
chown -R "$SYSTEM_USER:$SYSTEM_GROUP" "$DATADIR"
}
write_systemd_service() {
if [[ -e "$SERVICE_FILE" && "$FORCE_SERVICE_REWRITE" != "yes" ]]; then
die "Plik usługi $SERVICE_FILE już istnieje. Ustaw FORCE_SERVICE_REWRITE=\"yes\", aby go odtworzyć po wykonaniu kopii."
fi
backup_existing_file "$SERVICE_FILE"
write_file_atomic "$SERVICE_FILE" 0644 root root <<EOF
[Unit]
Description=Alternatywna instancja MariaDB
After=network.target
[Service]
Type=simple
User=$SYSTEM_USER
Group=$SYSTEM_GROUP
RuntimeDirectory=alt-mariadb
RuntimeDirectoryMode=0750
ExecStart=$MARIADB_DIR/bin/mariadbd --defaults-file=$INSTANCE_CNF
Restart=on-failure
RestartSec=5
LimitNOFILE=65535
PrivateTmp=false
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
log "Zapisano i przeładowano usługę systemd $SERVICE_NAME."
}
print_service_diagnostics() {
warn "Usługa $SERVICE_NAME nie uruchomiła się poprawnie. Poniżej ostatnie wpisy diagnostyczne."
if [[ -f "$LOG_DIR/alt-mariadb.log" ]]; then
warn "Ostatnie linie z $LOG_DIR/alt-mariadb.log:"
tail -n 80 "$LOG_DIR/alt-mariadb.log" >&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 <<EOF
Instalacja alternatywnej instancji MariaDB zakończona.
Wersja MariaDB: ${RESOLVED_MARIADB_RELEASE:-$MARIADB_VERSION}
Katalog programu: $MARIADB_DIR
Katalog danych: $DATADIR
Port TCP: $PORT
Socket: $SOCKET
Konfiguracja instancji: $INSTANCE_CNF
Usługa systemd: $SERVICE_NAME
Plik usługi: $SERVICE_FILE
Logi: $LOG_DIR/alt-mariadb.log
Plik DirectAdmin alt-mysql.conf: $ALT_MYSQL_CONF
Plik klienta alt-my.cnf: $ALT_MY_CNF
Plik metadanych pluginu: $ALT_METADATA_ENV
Przykładowe polecenia testowe:
systemctl status $SERVICE_NAME --no-pager
$MARIADB_DIR/bin/mariadb --defaults-extra-file=$ALT_MY_CNF -e "SELECT VERSION();"
$MARIADB_DIR/bin/mariadb --defaults-extra-file=$ALT_MY_CNF --protocol=TCP --host=127.0.0.1 --port=$PORT -e "SELECT VERSION();"
Hasło administratora nie zostało wypisane. Znajduje się wyłącznie w chronionych plikach poświadczeń.
EOF
}
main() {
apply_cli_args "$@"
validate_version
validate_port
require_root
detect_os
load_da_credentials
validate_paths
install_dependencies
resolve_download_url
download_tarball
extract_tarball
prepare_user_and_dirs
write_instance_config
write_da_style_credential_files
write_alt_metadata_env
initialize_datadir
write_systemd_service
start_service
create_admin_user
run_final_tests
rerun_plugin_install_if_available
print_summary
}
main "$@"