Import alt-mysql plugin
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=./mysql_common.sh
|
||||
source "$SCRIPT_DIR/mysql_common.sh"
|
||||
|
||||
fail() {
|
||||
echo "alt-mysql health: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
mysql_load_alt_runtime
|
||||
mysql_load_alt_credentials
|
||||
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl is-active --quiet "$MYSQL_ALT_SERVICE_NAME" || fail "service is not active: $MYSQL_ALT_SERVICE_NAME"
|
||||
fi
|
||||
|
||||
[ -r "$MYSQL_ALT_MY_CNF" ] || fail "client defaults file is not readable: $MYSQL_ALT_MY_CNF"
|
||||
[ -r "$MYSQL_ALT_MYSQL_CONF" ] || fail "credentials file is not readable: $MYSQL_ALT_MYSQL_CONF"
|
||||
[ -n "$MYSQL_ALT_PORT" ] || fail "alt port was not discovered"
|
||||
[ -n "$MYSQL_ALT_SOCKET" ] || fail "alt socket was not discovered"
|
||||
|
||||
"$(mysql_alt_binary mysql)" --defaults-extra-file="$MYSQL_ALT_MY_CNF" -e "SELECT 1" >/dev/null \
|
||||
|| fail "cannot connect through $MYSQL_ALT_MY_CNF"
|
||||
|
||||
mysql_ensure_metadata_schema
|
||||
mysql_exec mysql -e "SELECT COUNT(*) FROM da_plugin_database_owners" >/dev/null \
|
||||
|| fail "metadata schema is not usable"
|
||||
|
||||
echo "alt-mysql health: OK"
|
||||
@@ -0,0 +1,126 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
PLUGIN_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
CUSTOMBUILD_ROOT="${CUSTOMBUILD_ROOT:-/usr/local/directadmin/custombuild}"
|
||||
ACTION="${1:-install}"
|
||||
SETTINGS_FILE="${MYSQL_PLUGIN_SETTINGS_FILE:-$PLUGIN_DIR/plugin-settings.conf}"
|
||||
MARKER="managed by DirectAdmin MySQL plugin CustomBuild hook"
|
||||
|
||||
log() {
|
||||
printf 'mysql custombuild-hooks: %s\n' "$*"
|
||||
}
|
||||
|
||||
die() {
|
||||
printf 'mysql custombuild-hooks: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
read_setting() {
|
||||
local key="$1"
|
||||
local default="$2"
|
||||
local line value
|
||||
|
||||
[ -r "$SETTINGS_FILE" ] || {
|
||||
printf '%s\n' "$default"
|
||||
return 0
|
||||
}
|
||||
|
||||
line="$(grep -E "^${key}=" "$SETTINGS_FILE" | tail -n 1 || true)"
|
||||
[ -n "$line" ] || {
|
||||
printf '%s\n' "$default"
|
||||
return 0
|
||||
}
|
||||
|
||||
value="${line#*=}"
|
||||
value="${value%%#*}"
|
||||
value="${value%\"}"
|
||||
value="${value#\"}"
|
||||
value="${value%\'}"
|
||||
value="${value#\'}"
|
||||
value="$(printf '%s' "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
|
||||
printf '%s\n' "${value:-$default}"
|
||||
}
|
||||
|
||||
custombuild_hooks_enabled() {
|
||||
case "$(read_setting CUSTOMBUILD_HOOKS_ENABLED true)" in
|
||||
true|yes|on|1) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
write_hook() {
|
||||
local target="$1"
|
||||
local app="$2"
|
||||
local repair_script="$3"
|
||||
local health_script="$4"
|
||||
local repair_name health_name tmp
|
||||
|
||||
[ -f "$repair_script" ] || die "missing repair script: $repair_script"
|
||||
[ -f "$health_script" ] || die "missing health check script: $health_script"
|
||||
repair_name="$(basename "$repair_script")"
|
||||
health_name="$(basename "$health_script")"
|
||||
|
||||
mkdir -p "$(dirname "$target")"
|
||||
tmp="$(mktemp)"
|
||||
cat > "$tmp" <<EOF
|
||||
#!/bin/bash
|
||||
# $MARKER
|
||||
set -euo pipefail
|
||||
|
||||
PLUGIN_DIR='$PLUGIN_DIR'
|
||||
LOG_DIR="\$PLUGIN_DIR/data/logs"
|
||||
LOG_FILE="\$LOG_DIR/custombuild-${app}-repair.log"
|
||||
|
||||
mkdir -p "\$LOG_DIR" 2>/dev/null || true
|
||||
|
||||
{
|
||||
printf '%s\\n' "=== \$(date '+%Y-%m-%d %H:%M:%S') ${app} CustomBuild post hook ==="
|
||||
"\$PLUGIN_DIR/scripts/setup/${repair_name}"
|
||||
if ! "\$PLUGIN_DIR/scripts/setup/${health_name}"; then
|
||||
printf '%s\\n' "${app}: warning: health check failed after repair"
|
||||
fi
|
||||
} >> "\$LOG_FILE" 2>&1
|
||||
EOF
|
||||
install -m 0755 "$tmp" "$target"
|
||||
rm -f "$tmp"
|
||||
log "installed CustomBuild post hook: $target"
|
||||
}
|
||||
|
||||
install_all() {
|
||||
custombuild_hooks_enabled || {
|
||||
log "disabled by CUSTOMBUILD_HOOKS_ENABLED"
|
||||
return 0
|
||||
}
|
||||
|
||||
write_hook \
|
||||
"$CUSTOMBUILD_ROOT/custom/hooks/roundcube/post/90-alt-mysql-repair.sh" \
|
||||
"roundcube" \
|
||||
"$PLUGIN_DIR/scripts/setup/roundcube_repair_config.sh" \
|
||||
"$PLUGIN_DIR/scripts/setup/roundcube_health_check.sh"
|
||||
|
||||
write_hook \
|
||||
"$CUSTOMBUILD_ROOT/custom/hooks/phpmyadmin/post/90-alt-mysql-repair.sh" \
|
||||
"phpmyadmin" \
|
||||
"$PLUGIN_DIR/scripts/setup/phpmyadmin_repair.sh" \
|
||||
"$PLUGIN_DIR/scripts/setup/phpmyadmin_health_check.sh"
|
||||
}
|
||||
|
||||
uninstall_all() {
|
||||
local hook
|
||||
for hook in \
|
||||
"$CUSTOMBUILD_ROOT/custom/hooks/roundcube/post/90-alt-mysql-repair.sh" \
|
||||
"$CUSTOMBUILD_ROOT/custom/hooks/phpmyadmin/post/90-alt-mysql-repair.sh"
|
||||
do
|
||||
if [ -f "$hook" ] && grep -Fq "$MARKER" "$hook"; then
|
||||
rm -f "$hook"
|
||||
log "removed CustomBuild post hook: $hook"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
case "$ACTION" in
|
||||
install) install_all ;;
|
||||
uninstall) uninstall_all ;;
|
||||
*) die "unknown action: $ACTION" ;;
|
||||
esac
|
||||
@@ -0,0 +1,124 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
PLUGIN_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
HOOK_ROOT="${DA_CUSTOM_HOOK_DIR:-/usr/local/directadmin/scripts/custom}"
|
||||
ACTION="${1:-install}"
|
||||
MARKER="managed by DirectAdmin MySQL plugin hook dispatcher"
|
||||
|
||||
log() {
|
||||
printf 'mysql da-integration: %s\n' "$*"
|
||||
}
|
||||
|
||||
die() {
|
||||
printf 'mysql da-integration: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
write_dispatcher() {
|
||||
local target="$1"
|
||||
local tmp
|
||||
tmp="$(mktemp)"
|
||||
cat > "$tmp" <<'EOF'
|
||||
#!/bin/bash
|
||||
# managed by DirectAdmin MySQL plugin hook dispatcher
|
||||
set -u
|
||||
|
||||
hook_name="$(basename "$0")"
|
||||
hook_dir="${0}.d"
|
||||
status=0
|
||||
|
||||
if [ ! -d "$hook_dir" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for hook in "$hook_dir"/*.sh; do
|
||||
[ -e "$hook" ] || continue
|
||||
[ -x "$hook" ] || continue
|
||||
DA_HOOK_NAME="$hook_name" "$hook" "$@"
|
||||
status=$?
|
||||
if [ "$status" -ne 0 ]; then
|
||||
exit "$status"
|
||||
fi
|
||||
done
|
||||
|
||||
exit 0
|
||||
EOF
|
||||
install -m 0755 "$tmp" "$target"
|
||||
rm -f "$tmp"
|
||||
}
|
||||
|
||||
install_hook() {
|
||||
local hook_name="$1"
|
||||
local source_script="$2"
|
||||
local link_name="${3:-10-alt-mysql.sh}"
|
||||
local target="$HOOK_ROOT/$hook_name"
|
||||
local hook_dir="$HOOK_ROOT/${hook_name}.d"
|
||||
local preserved=""
|
||||
|
||||
[ -f "$source_script" ] || die "missing hook source: $source_script"
|
||||
chmod 0755 "$source_script" 2>/dev/null || true
|
||||
mkdir -p "$hook_dir"
|
||||
|
||||
if [ -e "$target" ] && ! grep -Fq "$MARKER" "$target" 2>/dev/null; then
|
||||
if [ -d "$target" ]; then
|
||||
die "cannot preserve hook because target is a directory: $target"
|
||||
fi
|
||||
preserved="$hook_dir/00-existing.sh"
|
||||
if [ -e "$preserved" ]; then
|
||||
preserved="$hook_dir/00-existing.$(date +%Y%m%d%H%M%S).sh"
|
||||
fi
|
||||
mv "$target" "$preserved"
|
||||
chmod 0755 "$preserved" 2>/dev/null || true
|
||||
log "preserved existing hook $hook_name as $preserved"
|
||||
fi
|
||||
|
||||
write_dispatcher "$target"
|
||||
ln -sfn "$source_script" "$hook_dir/$link_name"
|
||||
chmod 0755 "$target" "$source_script" 2>/dev/null || true
|
||||
log "installed hook $hook_name -> $source_script"
|
||||
}
|
||||
|
||||
remove_hook_link() {
|
||||
local hook_name="$1"
|
||||
local link_name="${2:-10-alt-mysql.sh}"
|
||||
local target="$HOOK_ROOT/$hook_name"
|
||||
local hook_dir="$HOOK_ROOT/${hook_name}.d"
|
||||
|
||||
rm -f "$hook_dir/$link_name"
|
||||
|
||||
if [ -d "$hook_dir" ] && ! find "$hook_dir" -mindepth 1 -maxdepth 1 -print -quit | grep -q .; then
|
||||
rmdir "$hook_dir" 2>/dev/null || true
|
||||
if [ -f "$target" ] && grep -Fq "$MARKER" "$target" 2>/dev/null; then
|
||||
rm -f "$target"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
install_all() {
|
||||
mkdir -p "$HOOK_ROOT"
|
||||
install_hook "user_backup_compress_pre.sh" "$PLUGIN_DIR/scripts/da-integration/alt_mysql_user_backup_compress_pre.sh"
|
||||
install_hook "user_restore_post_pre_cleanup.sh" "$PLUGIN_DIR/scripts/da-integration/alt_mysql_user_restore_post_pre_cleanup.sh"
|
||||
|
||||
install_hook "database_create_pre.sh" "$PLUGIN_DIR/scripts/da-integration/block_native_database_action.sh" "20-alt-mysql-block-native-db.sh"
|
||||
install_hook "database_delete_pre.sh" "$PLUGIN_DIR/scripts/da-integration/block_native_database_action.sh" "20-alt-mysql-block-native-db.sh"
|
||||
install_hook "database_user_password_change_pre.sh" "$PLUGIN_DIR/scripts/da-integration/block_native_database_action.sh" "20-alt-mysql-block-native-db.sh"
|
||||
install_hook "database_user_create_post.sh" "$PLUGIN_DIR/scripts/da-integration/block_native_database_action.sh" "20-alt-mysql-observe-native-db.sh"
|
||||
install_hook "database_destroy_user_post.sh" "$PLUGIN_DIR/scripts/da-integration/block_native_database_action.sh" "20-alt-mysql-observe-native-db.sh"
|
||||
}
|
||||
|
||||
uninstall_all() {
|
||||
remove_hook_link "user_backup_compress_pre.sh"
|
||||
remove_hook_link "user_restore_post_pre_cleanup.sh"
|
||||
remove_hook_link "database_create_pre.sh" "20-alt-mysql-block-native-db.sh"
|
||||
remove_hook_link "database_delete_pre.sh" "20-alt-mysql-block-native-db.sh"
|
||||
remove_hook_link "database_user_password_change_pre.sh" "20-alt-mysql-block-native-db.sh"
|
||||
remove_hook_link "database_user_create_post.sh" "20-alt-mysql-observe-native-db.sh"
|
||||
remove_hook_link "database_destroy_user_post.sh" "20-alt-mysql-observe-native-db.sh"
|
||||
}
|
||||
|
||||
case "$ACTION" in
|
||||
install) install_all ;;
|
||||
uninstall) uninstall_all ;;
|
||||
*) die "unknown action: $ACTION" ;;
|
||||
esac
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=./mysql_common.sh
|
||||
source "$SCRIPT_DIR/mysql_common.sh"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Użycie:
|
||||
dbrestore.sh <plik.sql|plik.sql.gz> [nazwa_bazy]
|
||||
|
||||
Opis:
|
||||
Przywraca pojedynczą bazę MySQL z pliku dumpa. Gdy nazwa bazy nie zostanie
|
||||
podana, skrypt wyprowadza ją z nazwy pliku.
|
||||
EOF
|
||||
}
|
||||
|
||||
[ "${1:-}" = "--help" ] && {
|
||||
usage
|
||||
exit 0
|
||||
}
|
||||
|
||||
[ $# -ge 1 ] || {
|
||||
usage
|
||||
exit 1
|
||||
}
|
||||
|
||||
FILE="$1"
|
||||
[ -f "$FILE" ] || {
|
||||
echo "Plik nie istnieje: $FILE" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
case "$FILE" in
|
||||
*.sql|*.sql.gz|*.gz) ;;
|
||||
*)
|
||||
echo "Dozwolone są pliki .sql, .sql.gz lub .gz." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
TARGET_DB="${2:-$(basename "$FILE" | sed -E 's/\.sql(\.gz)?$//; s/\.gz$//')}"
|
||||
TARGET_DB="$(mysql_safe_identifier "$TARGET_DB")"
|
||||
|
||||
mysql_load_alt_credentials
|
||||
|
||||
mysql_exec mysql -e "DROP DATABASE IF EXISTS $(mysql_quote_identifier "$TARGET_DB");"
|
||||
mysql_exec mysql -e "CREATE DATABASE $(mysql_quote_identifier "$TARGET_DB") CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
|
||||
mysql_restore_stream "$TARGET_DB" "$FILE"
|
||||
|
||||
echo "Przywracanie bazy ${TARGET_DB} zakończone."
|
||||
@@ -0,0 +1,99 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=./mysql_common.sh
|
||||
source "$SCRIPT_DIR/mysql_common.sh"
|
||||
|
||||
BASE_DIR="${BASE_DIR:-/home/admin/mysql_backup}"
|
||||
DATE_FORMAT="${DATE_FORMAT:-%d-%m-%Y}"
|
||||
ADD_TIME="${ADD_TIME:-0}"
|
||||
RETENTION="${RETENTION:-7}"
|
||||
ENABLE_COMPRESSION="${ENABLE_COMPRESSION:-1}"
|
||||
LOG_FILE="${LOG_FILE:-/var/log/mysql_backup.log}"
|
||||
|
||||
timestamp() {
|
||||
date '+%Y-%m-%d %H:%M:%S'
|
||||
}
|
||||
|
||||
log() {
|
||||
printf '[%s] %s\n' "$(timestamp)" "$*" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
dump_globals_file() {
|
||||
local output_file="$1"
|
||||
local tmp_file
|
||||
tmp_file="$(mktemp)"
|
||||
{
|
||||
echo "-- Global users and grants for MySQL"
|
||||
echo "-- Generated at $(timestamp)"
|
||||
} > "$tmp_file"
|
||||
|
||||
while IFS=$'\t' read -r db_user db_host; do
|
||||
[ -n "$db_user" ] || continue
|
||||
[ -n "$db_host" ] || continue
|
||||
printf "DROP USER IF EXISTS '%s'@'%s';\n" "$db_user" "$db_host" >> "$tmp_file"
|
||||
mysql_exec mysql -Nse "SHOW CREATE USER '${db_user}'@'${db_host}'" >> "$tmp_file" || true
|
||||
echo ";" >> "$tmp_file"
|
||||
while IFS= read -r grant_line; do
|
||||
[ -n "$grant_line" ] || continue
|
||||
printf "%s;\n" "$grant_line" >> "$tmp_file"
|
||||
done < <(mysql_exec mysql -Nse "SHOW GRANTS FOR '${db_user}'@'${db_host}'" || true)
|
||||
echo >> "$tmp_file"
|
||||
done < <(
|
||||
mysql_exec mysql -Nse "
|
||||
SELECT user, host
|
||||
FROM mysql.user
|
||||
WHERE user <> ''
|
||||
AND user NOT IN ('mysql.sys', 'mysql.session', 'mysql.infoschema', 'mariadb.sys')
|
||||
ORDER BY user, host
|
||||
" || true
|
||||
)
|
||||
|
||||
if [ "$ENABLE_COMPRESSION" = "1" ]; then
|
||||
gzip -9 < "$tmp_file" > "$output_file"
|
||||
else
|
||||
mv "$tmp_file" "$output_file"
|
||||
return 0
|
||||
fi
|
||||
rm -f "$tmp_file"
|
||||
}
|
||||
|
||||
mysql_load_alt_credentials
|
||||
|
||||
mkdir -p "$BASE_DIR"
|
||||
|
||||
if [ "$ADD_TIME" = "1" ]; then
|
||||
DATE_STR="$(date +"${DATE_FORMAT}_%H-%M")"
|
||||
else
|
||||
DATE_STR="$(date +"${DATE_FORMAT}")"
|
||||
fi
|
||||
BACKUP_DIR="${BASE_DIR}/${DATE_STR}"
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
log "Start backupu MySQL do ${BACKUP_DIR}"
|
||||
|
||||
GLOBALS_FILE="${BACKUP_DIR}/_globals.sql"
|
||||
if [ "$ENABLE_COMPRESSION" = "1" ]; then
|
||||
GLOBALS_FILE="${GLOBALS_FILE}.gz"
|
||||
fi
|
||||
dump_globals_file "$GLOBALS_FILE"
|
||||
log "Zapisano ${GLOBALS_FILE##*/}"
|
||||
|
||||
while IFS= read -r db_name; do
|
||||
[ -n "$db_name" ] || continue
|
||||
target_file="${BACKUP_DIR}/${db_name}.sql"
|
||||
if [ "$ENABLE_COMPRESSION" = "1" ]; then
|
||||
mysqldump_exec --single-transaction --skip-lock-tables --routines --triggers --events --databases "$db_name" | gzip -9 > "${target_file}.gz"
|
||||
log "Backup ${db_name} -> ${target_file##*/}.gz"
|
||||
else
|
||||
mysqldump_exec --single-transaction --skip-lock-tables --routines --triggers --events --databases "$db_name" > "$target_file"
|
||||
log "Backup ${db_name} -> ${target_file##*/}"
|
||||
fi
|
||||
done < <(mysql_non_system_databases)
|
||||
|
||||
if [ "$RETENTION" -gt 0 ]; then
|
||||
find "$BASE_DIR" -mindepth 1 -maxdepth 1 -type d -mtime +"$RETENTION" -exec rm -rf {} +
|
||||
fi
|
||||
|
||||
log "Backup MySQL zakończony."
|
||||
Executable
+489
@@ -0,0 +1,489 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
MYSQL_PLUGIN_CREDENTIALS_FILE="${MYSQL_PLUGIN_CREDENTIALS_FILE:-/usr/local/directadmin/conf/alt-mysql.conf}"
|
||||
MYSQL_PLUGIN_METADATA_FILE="${MYSQL_PLUGIN_METADATA_FILE:-/usr/local/directadmin/conf/alt-mariadb.env}"
|
||||
MYSQL_PLUGIN_ALT_MY_CNF="${MYSQL_PLUGIN_ALT_MY_CNF:-/usr/local/directadmin/conf/alt-my.cnf}"
|
||||
MYSQL_PLUGIN_ALT_INSTANCE_CNF="${MYSQL_PLUGIN_ALT_INSTANCE_CNF:-/etc/alt-mariadb.cnf}"
|
||||
|
||||
trim_value() {
|
||||
local value="${1:-}"
|
||||
value="${value#"${value%%[![:space:]]*}"}"
|
||||
value="${value%"${value##*[![:space:]]}"}"
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
strip_inline_comment() {
|
||||
local value
|
||||
value="$(trim_value "${1:-}")"
|
||||
value="${value%%#*}"
|
||||
printf '%s' "$(trim_value "$value")"
|
||||
}
|
||||
|
||||
unquote_value() {
|
||||
local value
|
||||
value="$(trim_value "${1:-}")"
|
||||
if [ "${#value}" -ge 2 ] && [ "${value:0:1}" = "'" ] && [ "${value: -1}" = "'" ]; then
|
||||
value="${value:1:${#value}-2}"
|
||||
elif [ "${#value}" -ge 2 ] && [ "${value:0:1}" = '"' ] && [ "${value: -1}" = '"' ]; then
|
||||
value="${value:1:${#value}-2}"
|
||||
fi
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
mysql_require_root() {
|
||||
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||||
echo "Ten skrypt musi być uruchomiony jako root." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
mysql_detect_binary() {
|
||||
local bin_name="$1"
|
||||
local candidate=""
|
||||
local alt_name=""
|
||||
if [ -n "${MYSQL_PLUGIN_CLIENT_BIN_DIR:-}" ]; then
|
||||
for alt_name in "$bin_name" "$(mysql_binary_alias "$bin_name")"; do
|
||||
[ -n "$alt_name" ] || continue
|
||||
candidate="${MYSQL_PLUGIN_CLIENT_BIN_DIR%/}/${alt_name}"
|
||||
if [ -x "$candidate" ]; then
|
||||
printf '%s\n' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
for alt_name in "$bin_name" "$(mysql_binary_alias "$bin_name")"; do
|
||||
[ -n "$alt_name" ] || continue
|
||||
candidate="$(command -v "$alt_name" 2>/dev/null || true)"
|
||||
if [ -n "$candidate" ]; then
|
||||
printf '%s\n' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
for alt_name in "$bin_name" "$(mysql_binary_alias "$bin_name")"; do
|
||||
[ -n "$alt_name" ] || continue
|
||||
while IFS= read -r candidate; do
|
||||
if [ -x "$candidate" ]; then
|
||||
printf '%s\n' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done < <(find /usr/local -maxdepth 3 -type f -path '/usr/local/mariadb-*/bin/'"$alt_name" 2>/dev/null | sort -V -r)
|
||||
for candidate in /usr/bin/"$alt_name" /usr/local/bin/"$alt_name"; do
|
||||
if [ -x "$candidate" ]; then
|
||||
printf '%s\n' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
mysql_binary_alias() {
|
||||
case "$1" in
|
||||
mysql) printf 'mariadb\n' ;;
|
||||
mysqldump) printf 'mariadb-dump\n' ;;
|
||||
mysqladmin) printf 'mariadb-admin\n' ;;
|
||||
mysql_upgrade) printf 'mariadb-upgrade\n' ;;
|
||||
*) printf '\n' ;;
|
||||
esac
|
||||
}
|
||||
|
||||
mysql_load_plugin_settings_file() {
|
||||
local file="${1:-}"
|
||||
local raw_line key value
|
||||
[ -n "$file" ] || return 0
|
||||
[ -r "$file" ] || return 0
|
||||
|
||||
while IFS= read -r raw_line || [ -n "$raw_line" ]; do
|
||||
raw_line="$(trim_value "$raw_line")"
|
||||
[ -n "$raw_line" ] || continue
|
||||
case "$raw_line" in
|
||||
\#*) continue ;;
|
||||
esac
|
||||
[[ "$raw_line" == *=* ]] || continue
|
||||
|
||||
key="$(trim_value "${raw_line%%=*}")"
|
||||
value="$(unquote_value "$(strip_inline_comment "${raw_line#*=}")")"
|
||||
case "$key" in
|
||||
MYSQL_PLUGIN_CREDENTIALS_FILE) MYSQL_PLUGIN_CREDENTIALS_FILE="$value" ;;
|
||||
MYSQL_PLUGIN_METADATA_FILE) MYSQL_PLUGIN_METADATA_FILE="$value" ;;
|
||||
MYSQL_PLUGIN_ALT_MY_CNF) MYSQL_PLUGIN_ALT_MY_CNF="$value" ;;
|
||||
MYSQL_PLUGIN_ALT_MARIADB_CNF) MYSQL_PLUGIN_ALT_INSTANCE_CNF="$value" ;;
|
||||
MYSQL_PLUGIN_CLIENT_BIN_DIR) MYSQL_PLUGIN_CLIENT_BIN_DIR="$value" ;;
|
||||
esac
|
||||
done < "$file"
|
||||
}
|
||||
|
||||
mysql_read_key_value() {
|
||||
local file="$1"
|
||||
local key="$2"
|
||||
[ -r "$file" ] || return 1
|
||||
|
||||
awk -F= -v key="$key" '
|
||||
function trim(s) {
|
||||
sub(/^[[:space:]]+/, "", s)
|
||||
sub(/[[:space:]]+$/, "", s)
|
||||
return s
|
||||
}
|
||||
$0 ~ /^[[:space:]]*#/ || $0 !~ /=/ { next }
|
||||
{
|
||||
k = trim($1)
|
||||
if (k != key) { next }
|
||||
sub(/^[^=]*=/, "")
|
||||
v = trim($0)
|
||||
if (v ~ /^'\''.*'\''$/ || v ~ /^".*"$/) {
|
||||
v = substr(v, 2, length(v) - 2)
|
||||
}
|
||||
print v
|
||||
exit
|
||||
}
|
||||
' "$file"
|
||||
}
|
||||
|
||||
mysql_read_cnf_value() {
|
||||
local file="$1"
|
||||
local section="$2"
|
||||
local key="$3"
|
||||
[ -r "$file" ] || return 1
|
||||
|
||||
awk -F= -v section="$section" -v key="$key" '
|
||||
function trim(s) {
|
||||
sub(/^[[:space:]]+/, "", s)
|
||||
sub(/[[:space:]]+$/, "", s)
|
||||
return s
|
||||
}
|
||||
/^[[:space:]]*#/ || /^[[:space:]]*;/ { next }
|
||||
/^\[[^]]+\]/ {
|
||||
current=$0
|
||||
gsub(/^\[[[:space:]]*/, "", current)
|
||||
gsub(/[[:space:]]*\]$/, "", current)
|
||||
current=trim(current)
|
||||
next
|
||||
}
|
||||
current == section && $0 ~ /=/ {
|
||||
k=trim($1)
|
||||
if (k != key) { next }
|
||||
sub(/^[^=]*=/, "")
|
||||
v=trim($0)
|
||||
if (v ~ /^'\''.*'\''$/ || v ~ /^".*"$/) {
|
||||
v=substr(v, 2, length(v)-2)
|
||||
}
|
||||
print v
|
||||
exit
|
||||
}
|
||||
' "$file"
|
||||
}
|
||||
|
||||
mysql_load_alt_runtime() {
|
||||
MYSQL_ALT_METADATA_FILE="${MYSQL_ALT_METADATA_FILE:-$MYSQL_PLUGIN_METADATA_FILE}"
|
||||
MYSQL_ALT_MYSQL_CONF="${MYSQL_ALT_MYSQL_CONF:-$MYSQL_PLUGIN_CREDENTIALS_FILE}"
|
||||
MYSQL_ALT_MY_CNF="${MYSQL_ALT_MY_CNF:-$MYSQL_PLUGIN_ALT_MY_CNF}"
|
||||
MYSQL_ALT_INSTANCE_CNF="${MYSQL_ALT_INSTANCE_CNF:-$MYSQL_PLUGIN_ALT_INSTANCE_CNF}"
|
||||
MYSQL_ALT_SERVICE_NAME="${MYSQL_ALT_SERVICE_NAME:-alt-mariadb}"
|
||||
MYSQL_ALT_BASEDIR="${MYSQL_ALT_BASEDIR:-}"
|
||||
MYSQL_ALT_DATADIR="${MYSQL_ALT_DATADIR:-}"
|
||||
MYSQL_ALT_PORT="${MYSQL_ALT_PORT:-}"
|
||||
MYSQL_ALT_SOCKET="${MYSQL_ALT_SOCKET:-}"
|
||||
|
||||
if [ -r "$MYSQL_ALT_METADATA_FILE" ]; then
|
||||
MYSQL_ALT_SERVICE_NAME="$(mysql_read_key_value "$MYSQL_ALT_METADATA_FILE" SERVICE_NAME || printf '%s' "$MYSQL_ALT_SERVICE_NAME")"
|
||||
MYSQL_ALT_BASEDIR="$(mysql_read_key_value "$MYSQL_ALT_METADATA_FILE" MARIADB_DIR || printf '%s' "$MYSQL_ALT_BASEDIR")"
|
||||
MYSQL_ALT_DATADIR="$(mysql_read_key_value "$MYSQL_ALT_METADATA_FILE" DATADIR || printf '%s' "$MYSQL_ALT_DATADIR")"
|
||||
MYSQL_ALT_PORT="$(mysql_read_key_value "$MYSQL_ALT_METADATA_FILE" PORT || printf '%s' "$MYSQL_ALT_PORT")"
|
||||
MYSQL_ALT_SOCKET="$(mysql_read_key_value "$MYSQL_ALT_METADATA_FILE" SOCKET || printf '%s' "$MYSQL_ALT_SOCKET")"
|
||||
MYSQL_ALT_MYSQL_CONF="$(mysql_read_key_value "$MYSQL_ALT_METADATA_FILE" ALT_MYSQL_CONF || printf '%s' "$MYSQL_ALT_MYSQL_CONF")"
|
||||
MYSQL_ALT_MY_CNF="$(mysql_read_key_value "$MYSQL_ALT_METADATA_FILE" ALT_MY_CNF || printf '%s' "$MYSQL_ALT_MY_CNF")"
|
||||
MYSQL_ALT_INSTANCE_CNF="$(mysql_read_key_value "$MYSQL_ALT_METADATA_FILE" INSTANCE_CNF || printf '%s' "$MYSQL_ALT_INSTANCE_CNF")"
|
||||
fi
|
||||
|
||||
if [ -r "$MYSQL_ALT_MYSQL_CONF" ]; then
|
||||
[ -n "$MYSQL_ALT_PORT" ] || MYSQL_ALT_PORT="$(mysql_read_key_value "$MYSQL_ALT_MYSQL_CONF" port || true)"
|
||||
[ -n "$MYSQL_ALT_SOCKET" ] || MYSQL_ALT_SOCKET="$(mysql_read_key_value "$MYSQL_ALT_MYSQL_CONF" socket || true)"
|
||||
fi
|
||||
|
||||
if [ -r "$MYSQL_ALT_INSTANCE_CNF" ]; then
|
||||
[ -n "$MYSQL_ALT_BASEDIR" ] || MYSQL_ALT_BASEDIR="$(mysql_read_cnf_value "$MYSQL_ALT_INSTANCE_CNF" mariadb basedir || true)"
|
||||
[ -n "$MYSQL_ALT_DATADIR" ] || MYSQL_ALT_DATADIR="$(mysql_read_cnf_value "$MYSQL_ALT_INSTANCE_CNF" mariadb datadir || true)"
|
||||
[ -n "$MYSQL_ALT_PORT" ] || MYSQL_ALT_PORT="$(mysql_read_cnf_value "$MYSQL_ALT_INSTANCE_CNF" mariadb port || true)"
|
||||
[ -n "$MYSQL_ALT_SOCKET" ] || MYSQL_ALT_SOCKET="$(mysql_read_cnf_value "$MYSQL_ALT_INSTANCE_CNF" mariadb socket || true)"
|
||||
fi
|
||||
|
||||
if [ -n "$MYSQL_ALT_BASEDIR" ] && [ -d "$MYSQL_ALT_BASEDIR/bin" ]; then
|
||||
MYSQL_PLUGIN_CLIENT_BIN_DIR="${MYSQL_PLUGIN_CLIENT_BIN_DIR:-$MYSQL_ALT_BASEDIR/bin}"
|
||||
fi
|
||||
|
||||
[ -n "$MYSQL_ALT_PORT" ] || MYSQL_ALT_PORT="3306"
|
||||
[ -n "$MYSQL_ALT_MYSQL_CONF" ] || MYSQL_ALT_MYSQL_CONF="/usr/local/directadmin/conf/alt-mysql.conf"
|
||||
[ -n "$MYSQL_ALT_MY_CNF" ] || MYSQL_ALT_MY_CNF="/usr/local/directadmin/conf/alt-my.cnf"
|
||||
[ -n "$MYSQL_ALT_INSTANCE_CNF" ] || MYSQL_ALT_INSTANCE_CNF="/etc/alt-mariadb.cnf"
|
||||
[ -n "$MYSQL_ALT_SERVICE_NAME" ] || MYSQL_ALT_SERVICE_NAME="alt-mariadb"
|
||||
}
|
||||
|
||||
mysql_alt_service_name() {
|
||||
mysql_load_alt_runtime
|
||||
printf '%s\n' "$MYSQL_ALT_SERVICE_NAME"
|
||||
}
|
||||
|
||||
mysql_alt_basedir() {
|
||||
mysql_load_alt_runtime
|
||||
printf '%s\n' "$MYSQL_ALT_BASEDIR"
|
||||
}
|
||||
|
||||
mysql_alt_datadir() {
|
||||
mysql_load_alt_runtime
|
||||
printf '%s\n' "$MYSQL_ALT_DATADIR"
|
||||
}
|
||||
|
||||
mysql_alt_port() {
|
||||
mysql_load_alt_runtime
|
||||
printf '%s\n' "$MYSQL_ALT_PORT"
|
||||
}
|
||||
|
||||
mysql_alt_socket() {
|
||||
mysql_load_alt_runtime
|
||||
printf '%s\n' "$MYSQL_ALT_SOCKET"
|
||||
}
|
||||
|
||||
mysql_alt_client_defaults() {
|
||||
mysql_load_alt_runtime
|
||||
printf '%s\n' "$MYSQL_ALT_MY_CNF"
|
||||
}
|
||||
|
||||
mysql_alt_binary() {
|
||||
local bin_name="$1"
|
||||
mysql_load_alt_runtime
|
||||
mysql_detect_binary "$bin_name"
|
||||
}
|
||||
|
||||
mysql_native_binary() {
|
||||
local bin_name="$1"
|
||||
local alt_name candidate
|
||||
for alt_name in "$bin_name" "$(mysql_binary_alias "$bin_name")"; do
|
||||
[ -n "$alt_name" ] || continue
|
||||
for candidate in /usr/bin/"$alt_name" /usr/local/bin/"$alt_name"; do
|
||||
if [ -x "$candidate" ]; then
|
||||
printf '%s\n' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
candidate="$(command -v "$alt_name" 2>/dev/null || true)"
|
||||
if [ -n "$candidate" ]; then
|
||||
printf '%s\n' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
mysql_load_credentials() {
|
||||
local file="${1:-$MYSQL_PLUGIN_CREDENTIALS_FILE}"
|
||||
[ -r "$file" ] || {
|
||||
echo "Brak pliku poświadczeń MySQL: $file" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
MYSQL_HOST=""
|
||||
MYSQL_PORT=""
|
||||
MYSQL_USER=""
|
||||
MYSQL_PASSWORD=""
|
||||
MYSQL_DATABASE=""
|
||||
MYSQL_SOCKET=""
|
||||
|
||||
while IFS= read -r raw_line || [ -n "$raw_line" ]; do
|
||||
raw_line="$(trim_value "$raw_line")"
|
||||
[ -n "$raw_line" ] || continue
|
||||
case "$raw_line" in
|
||||
\#*) continue ;;
|
||||
esac
|
||||
|
||||
if [[ "$raw_line" == *=* ]]; then
|
||||
local key="${raw_line%%=*}"
|
||||
local value="${raw_line#*=}"
|
||||
key="$(printf '%s' "$key" | tr '[:upper:]' '[:lower:]')"
|
||||
key="$(trim_value "$key")"
|
||||
value="$(unquote_value "$(strip_inline_comment "$value")")"
|
||||
case "$key" in
|
||||
host|mysql_host) MYSQL_HOST="$value" ;;
|
||||
port|mysql_port) MYSQL_PORT="$value" ;;
|
||||
user|username|mysql_user) MYSQL_USER="$value" ;;
|
||||
password|passwd|pass|mysql_password) MYSQL_PASSWORD="$value" ;;
|
||||
database|dbname|mysql_database) MYSQL_DATABASE="$value" ;;
|
||||
socket|mysql_socket) MYSQL_SOCKET="$value" ;;
|
||||
esac
|
||||
continue
|
||||
fi
|
||||
|
||||
local host_part port_part db_part user_part pass_part rest
|
||||
host_part="${raw_line%%:*}"
|
||||
rest="${raw_line#*:}"
|
||||
port_part="${rest%%:*}"
|
||||
rest="${rest#*:}"
|
||||
db_part="${rest%%:*}"
|
||||
rest="${rest#*:}"
|
||||
user_part="${rest%%:*}"
|
||||
pass_part="${rest#*:}"
|
||||
MYSQL_HOST="$host_part"
|
||||
MYSQL_PORT="$port_part"
|
||||
MYSQL_DATABASE="$db_part"
|
||||
MYSQL_USER="$user_part"
|
||||
MYSQL_PASSWORD="$pass_part"
|
||||
break
|
||||
done < "$file"
|
||||
|
||||
MYSQL_HOST="$(trim_value "${MYSQL_HOST:-}")"
|
||||
MYSQL_PORT="$(trim_value "${MYSQL_PORT:-}")"
|
||||
MYSQL_USER="$(trim_value "${MYSQL_USER:-}")"
|
||||
MYSQL_PASSWORD="${MYSQL_PASSWORD:-}"
|
||||
MYSQL_DATABASE="$(trim_value "${MYSQL_DATABASE:-}")"
|
||||
MYSQL_SOCKET="$(trim_value "${MYSQL_SOCKET:-}")"
|
||||
|
||||
[ -n "$MYSQL_HOST" ] || MYSQL_HOST="localhost"
|
||||
[ -n "$MYSQL_PORT" ] || MYSQL_PORT="3306"
|
||||
[ -n "$MYSQL_USER" ] || MYSQL_USER="root"
|
||||
[ -n "$MYSQL_DATABASE" ] || MYSQL_DATABASE="mysql"
|
||||
}
|
||||
|
||||
mysql_load_alt_credentials() {
|
||||
mysql_load_alt_runtime
|
||||
mysql_load_credentials "$MYSQL_ALT_MYSQL_CONF"
|
||||
}
|
||||
|
||||
mysql_base_args() {
|
||||
local args=("--user=$MYSQL_USER")
|
||||
if [ "$MYSQL_HOST" = "localhost" ]; then
|
||||
if [ -n "$MYSQL_SOCKET" ]; then
|
||||
args+=("--socket=$MYSQL_SOCKET")
|
||||
else
|
||||
args+=("--host=localhost")
|
||||
fi
|
||||
else
|
||||
args+=("--protocol=TCP" "--host=$MYSQL_HOST" "--port=$MYSQL_PORT")
|
||||
fi
|
||||
printf '%s\n' "${args[@]}"
|
||||
}
|
||||
|
||||
mysql_exec() {
|
||||
local database="$1"
|
||||
shift
|
||||
local -a args=()
|
||||
while IFS= read -r arg; do
|
||||
args+=("$arg")
|
||||
done < <(mysql_base_args)
|
||||
MYSQL_PWD="$MYSQL_PASSWORD" "$(mysql_detect_binary mysql)" "${args[@]}" --database="$database" "$@"
|
||||
}
|
||||
|
||||
mysql_exec_sql() {
|
||||
local database="$1"
|
||||
local sql="$2"
|
||||
mysql_exec "$database" -e "$sql"
|
||||
}
|
||||
|
||||
mysqldump_exec() {
|
||||
local -a args=()
|
||||
while IFS= read -r arg; do
|
||||
args+=("$arg")
|
||||
done < <(mysql_base_args)
|
||||
MYSQL_PWD="$MYSQL_PASSWORD" "$(mysql_detect_binary mysqldump)" "${args[@]}" "$@"
|
||||
}
|
||||
|
||||
mysql_alt_version() {
|
||||
mysql_load_alt_credentials
|
||||
mysql_exec mysql -Nse "SELECT VERSION()" 2>/dev/null || true
|
||||
}
|
||||
|
||||
mysql_ensure_metadata_schema() {
|
||||
mysql_exec mysql -e "
|
||||
CREATE TABLE IF NOT EXISTS da_plugin_access_hosts (
|
||||
da_user VARCHAR(255) NOT NULL,
|
||||
role_name VARCHAR(255) NOT NULL,
|
||||
host_pattern VARCHAR(255) NOT NULL,
|
||||
note VARCHAR(180) NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (role_name, host_pattern),
|
||||
KEY access_hosts_da_user_idx (da_user)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
CREATE TABLE IF NOT EXISTS da_plugin_grant_profiles (
|
||||
db_name VARCHAR(255) NOT NULL,
|
||||
role_name VARCHAR(255) NOT NULL,
|
||||
privileges TEXT NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (db_name, role_name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
CREATE TABLE IF NOT EXISTS da_plugin_database_owners (
|
||||
db_name VARCHAR(255) NOT NULL,
|
||||
da_user VARCHAR(255) NOT NULL,
|
||||
role_name VARCHAR(255) NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (db_name),
|
||||
KEY database_owners_da_user_idx (da_user),
|
||||
KEY database_owners_role_idx (role_name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
CREATE TABLE IF NOT EXISTS da_plugin_sso_accounts (
|
||||
role_name VARCHAR(255) NOT NULL,
|
||||
host_pattern VARCHAR(255) NOT NULL,
|
||||
expires_at INT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (role_name, host_pattern),
|
||||
KEY sso_accounts_expires_idx (expires_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
CREATE TABLE IF NOT EXISTS da_plugin_system_databases (
|
||||
service_name VARCHAR(64) NOT NULL,
|
||||
db_name VARCHAR(255) NOT NULL,
|
||||
db_user VARCHAR(255) NOT NULL,
|
||||
endpoint_mode VARCHAR(32) NOT NULL DEFAULT 'tcp',
|
||||
config_path VARCHAR(512) NOT NULL DEFAULT '',
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (service_name),
|
||||
KEY system_databases_db_idx (db_name),
|
||||
KEY system_databases_user_idx (db_user)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
"
|
||||
}
|
||||
|
||||
mysql_service_name() {
|
||||
local service
|
||||
for service in mysqld mariadb mysql; do
|
||||
if systemctl list-unit-files "${service}.service" >/dev/null 2>&1; then
|
||||
printf '%s\n' "$service"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
printf '%s\n' "mysqld"
|
||||
}
|
||||
|
||||
mysql_safe_identifier() {
|
||||
local value="${1:-}"
|
||||
if [[ ! "$value" =~ ^[A-Za-z0-9_]+$ ]]; then
|
||||
echo "Nieprawidłowy identyfikator MySQL: $value" >&2
|
||||
exit 1
|
||||
fi
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
mysql_quote_identifier() {
|
||||
local value
|
||||
value="$(mysql_safe_identifier "$1")"
|
||||
printf '`%s`' "$value"
|
||||
}
|
||||
|
||||
mysql_escape_literal() {
|
||||
printf "%s" "${1:-}" | sed "s/'/''/g"
|
||||
}
|
||||
|
||||
mysql_non_system_databases() {
|
||||
mysql_exec mysql -Nse "SHOW DATABASES" | grep -Ev '^(information_schema|performance_schema|mysql|sys)$' || true
|
||||
}
|
||||
|
||||
mysql_restore_stream() {
|
||||
local database="$1"
|
||||
local file="$2"
|
||||
if [[ "$file" == *.gz ]]; then
|
||||
gunzip -c "$file" | mysql_exec "$database"
|
||||
return ${PIPESTATUS[1]}
|
||||
fi
|
||||
mysql_exec "$database" < "$file"
|
||||
}
|
||||
Executable
+376
@@ -0,0 +1,376 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PLUGIN_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
PLUGIN_SETTINGS_FILE="${MYSQL_HOST_SYNC_PLUGIN_SETTINGS:-$PLUGIN_DIR/plugin-settings.conf}"
|
||||
|
||||
# shellcheck source=./mysql_common.sh
|
||||
source "$SCRIPT_DIR/mysql_common.sh"
|
||||
|
||||
load_plugin_settings() {
|
||||
local file="$1"
|
||||
local raw_line key value
|
||||
|
||||
[ -r "$file" ] || return 0
|
||||
|
||||
while IFS= read -r raw_line || [ -n "$raw_line" ]; do
|
||||
raw_line="$(trim_value "$raw_line")"
|
||||
[ -n "$raw_line" ] || continue
|
||||
case "$raw_line" in
|
||||
\#*) continue ;;
|
||||
esac
|
||||
[[ "$raw_line" == *=* ]] || continue
|
||||
|
||||
key="$(trim_value "${raw_line%%=*}")"
|
||||
value="$(unquote_value "$(strip_inline_comment "${raw_line#*=}")")"
|
||||
|
||||
case "$key" in
|
||||
MYSQL_PLUGIN_CREDENTIALS_FILE) MYSQL_PLUGIN_CREDENTIALS_FILE="$value" ;;
|
||||
MYSQL_PLUGIN_METADATA_FILE) MYSQL_PLUGIN_METADATA_FILE="$value" ;;
|
||||
MYSQL_PLUGIN_ALT_MY_CNF) MYSQL_PLUGIN_ALT_MY_CNF="$value" ;;
|
||||
MYSQL_PLUGIN_CLIENT_BIN_DIR) MYSQL_PLUGIN_CLIENT_BIN_DIR="$value" ;;
|
||||
MYSQL_PLUGIN_REMOTE_BIND_ADDRESS) MYSQL_PLUGIN_REMOTE_BIND_ADDRESS="$value" ;;
|
||||
MYSQL_PLUGIN_CSF_ALLOW_FILE) MYSQL_HOST_SYNC_CSF_ALLOW="$value" ;;
|
||||
MYSQL_PLUGIN_CSF_CONF_FILE) MYSQL_HOST_SYNC_CSF_CONF="$value" ;;
|
||||
MYSQL_PLUGIN_CSF_MANAGED_ALLOW_FILE) MYSQL_HOST_SYNC_MANAGED_ALLOW="$value" ;;
|
||||
MYSQL_PLUGIN_ALT_MARIADB_CNF) MYSQL_HOST_SYNC_INSTANCE_CNF="$value" ;;
|
||||
MYSQL_PLUGIN_ALT_MARIADB_SERVICE) MYSQL_HOST_SYNC_SERVICE="$value" ;;
|
||||
esac
|
||||
done < "$file"
|
||||
}
|
||||
|
||||
require_root_or_sudo() {
|
||||
if [ "${MYSQL_HOST_SYNC_ASSUME_ROOT:-0}" = "1" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "${EUID:-$(id -u)}" -eq 0 ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if command -v sudo >/dev/null 2>&1; then
|
||||
exec sudo -n "$0" "$@"
|
||||
fi
|
||||
|
||||
echo "mysql: synchronizacja hostów wymaga uprawnień root." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
is_ipv4() {
|
||||
local ip="${1:-}"
|
||||
local IFS=.
|
||||
local -a octets
|
||||
[[ "$ip" =~ ^[0-9]+(\.[0-9]+){3}$ ]] || return 1
|
||||
read -r -a octets <<< "$ip"
|
||||
[ "${#octets[@]}" -eq 4 ] || return 1
|
||||
local octet
|
||||
for octet in "${octets[@]}"; do
|
||||
[[ "$octet" =~ ^[0-9]+$ ]] || return 1
|
||||
[ "$octet" -ge 0 ] && [ "$octet" -le 255 ] || return 1
|
||||
done
|
||||
}
|
||||
|
||||
is_local_host() {
|
||||
case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in
|
||||
""|localhost|127.0.0.1|::1) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
collect_remote_hosts() {
|
||||
local sql output host
|
||||
sql="SELECT DISTINCT host_pattern FROM da_plugin_access_hosts WHERE host_pattern <> '' ORDER BY host_pattern"
|
||||
|
||||
if ! output="$(mysql_exec mysql --batch --skip-column-names --raw -e "$sql")"; then
|
||||
echo "mysql: nie można odczytać listy hostów zdalnych z da_plugin_access_hosts." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
while IFS= read -r host || [ -n "$host" ]; do
|
||||
host="$(trim_value "$host")"
|
||||
[ -n "$host" ] || continue
|
||||
is_local_host "$host" && continue
|
||||
is_ipv4 "$host" || continue
|
||||
printf '%s\n' "$host"
|
||||
done <<< "$output" | sort -u
|
||||
}
|
||||
|
||||
write_managed_allow_file() {
|
||||
local target="$1"
|
||||
local port="$2"
|
||||
local tmp
|
||||
shift 2
|
||||
local -a hosts=("$@")
|
||||
|
||||
install -d -m 0755 "$(dirname "$target")"
|
||||
tmp="$(mktemp)"
|
||||
|
||||
{
|
||||
echo "# Managed by DirectAdmin MySQL plugin. Do not edit manually."
|
||||
echo "# Remote MariaDB access for port $port."
|
||||
echo "# Source: da_plugin_access_hosts in the plugin metadata schema."
|
||||
local host
|
||||
for host in "${hosts[@]}"; do
|
||||
echo "tcp|in|d=$port|s=$host"
|
||||
done
|
||||
} > "$tmp"
|
||||
|
||||
if [ ! -f "$target" ] || ! cmp -s "$tmp" "$target"; then
|
||||
if [ -f "$target" ]; then
|
||||
cat "$tmp" > "$target"
|
||||
chmod 0644 "$target"
|
||||
else
|
||||
install -m 0644 "$tmp" "$target"
|
||||
fi
|
||||
changed_csf=1
|
||||
fi
|
||||
|
||||
rm -f "$tmp"
|
||||
}
|
||||
|
||||
ensure_csf_include() {
|
||||
local csf_allow="$1"
|
||||
local managed_allow="$2"
|
||||
local include_line="Include $managed_allow"
|
||||
|
||||
[ -f "$csf_allow" ] || {
|
||||
echo "mysql: brak pliku CSF allow: $csf_allow" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
if grep -Fxq "$include_line" "$csf_allow"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf '\n%s\n' "$include_line" >> "$csf_allow"
|
||||
changed_csf=1
|
||||
}
|
||||
|
||||
remove_port_from_csv() {
|
||||
local csv="$1"
|
||||
local port="$2"
|
||||
local old_ifs="$IFS"
|
||||
local item out="" start end left_start left_end right_start right_end
|
||||
IFS=','
|
||||
for item in $csv; do
|
||||
item="$(trim_value "$item")"
|
||||
[ -n "$item" ] || continue
|
||||
|
||||
if [ "$item" = "$port" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "$item" =~ ^([0-9]+):([0-9]+)$ ]]; then
|
||||
start="${BASH_REMATCH[1]}"
|
||||
end="${BASH_REMATCH[2]}"
|
||||
if [ "$start" -le "$port" ] && [ "$port" -le "$end" ]; then
|
||||
left_start="$start"
|
||||
left_end=$((port - 1))
|
||||
right_start=$((port + 1))
|
||||
right_end="$end"
|
||||
|
||||
if [ "$left_start" -le "$left_end" ]; then
|
||||
if [ -z "$out" ]; then
|
||||
out="$left_start"
|
||||
else
|
||||
out="$out,$left_start"
|
||||
fi
|
||||
if [ "$left_start" -ne "$left_end" ]; then
|
||||
out="$out:$left_end"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$right_start" -le "$right_end" ]; then
|
||||
if [ -z "$out" ]; then
|
||||
out="$right_start"
|
||||
else
|
||||
out="$out,$right_start"
|
||||
fi
|
||||
if [ "$right_start" -ne "$right_end" ]; then
|
||||
out="$out:$right_end"
|
||||
fi
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$out" ]; then
|
||||
out="$item"
|
||||
else
|
||||
out="$out,$item"
|
||||
fi
|
||||
done
|
||||
IFS="$old_ifs"
|
||||
printf '%s' "$out"
|
||||
}
|
||||
|
||||
remove_port_from_global_csf_lists() {
|
||||
local csf_conf="$1"
|
||||
local port="$2"
|
||||
local tmp changed=0 line new_list new_line
|
||||
|
||||
[ -f "$csf_conf" ] || {
|
||||
echo "mysql: brak pliku CSF config: $csf_conf" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
tmp="$(mktemp)"
|
||||
while IFS= read -r line || [ -n "$line" ]; do
|
||||
if [[ "$line" =~ ^([[:space:]]*)(TCP6?_IN)([[:space:]]*=[[:space:]]*)\"([^\"]*)\"(.*)$ ]]; then
|
||||
new_list="$(remove_port_from_csv "${BASH_REMATCH[4]}" "$port")"
|
||||
new_line="${BASH_REMATCH[1]}${BASH_REMATCH[2]}${BASH_REMATCH[3]}\"$new_list\"${BASH_REMATCH[5]}"
|
||||
if [ "$new_line" != "$line" ]; then
|
||||
changed=1
|
||||
fi
|
||||
printf '%s\n' "$new_line" >> "$tmp"
|
||||
else
|
||||
printf '%s\n' "$line" >> "$tmp"
|
||||
fi
|
||||
done < "$csf_conf"
|
||||
|
||||
if [ "$changed" -eq 1 ]; then
|
||||
cat "$tmp" > "$csf_conf"
|
||||
chmod 0600 "$csf_conf"
|
||||
changed_csf=1
|
||||
fi
|
||||
rm -f "$tmp"
|
||||
}
|
||||
|
||||
ensure_remote_bind_address() {
|
||||
local cnf="$1"
|
||||
local desired="$2"
|
||||
local tmp changed=0 inserted=0 in_mariadb=0 line new_line
|
||||
|
||||
[ -f "$cnf" ] || {
|
||||
echo "mysql: brak pliku konfiguracji MariaDB: $cnf" >&2
|
||||
return 1
|
||||
}
|
||||
is_ipv4 "$desired" || {
|
||||
echo "mysql: nieprawidłowy bind-address dla zdalnego MySQL: $desired" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
tmp="$(mktemp)"
|
||||
while IFS= read -r line || [ -n "$line" ]; do
|
||||
if [[ "$line" =~ ^[[:space:]]*\[mariadb\][[:space:]]*$ ]]; then
|
||||
in_mariadb=1
|
||||
printf '%s\n' "$line" >> "$tmp"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "$in_mariadb" -eq 1 ] && [[ "$line" =~ ^[[:space:]]*\[.*\][[:space:]]*$ ]]; then
|
||||
if [ "$inserted" -eq 0 ]; then
|
||||
printf 'bind-address=%s\n' "$desired" >> "$tmp"
|
||||
inserted=1
|
||||
changed=1
|
||||
fi
|
||||
in_mariadb=0
|
||||
fi
|
||||
|
||||
if [ "$in_mariadb" -eq 1 ] && [[ "$line" =~ ^[[:space:]]*bind-address[[:space:]]*= ]]; then
|
||||
new_line="bind-address=$desired"
|
||||
if [ "$line" != "$new_line" ]; then
|
||||
changed=1
|
||||
fi
|
||||
printf '%s\n' "$new_line" >> "$tmp"
|
||||
inserted=1
|
||||
continue
|
||||
fi
|
||||
|
||||
printf '%s\n' "$line" >> "$tmp"
|
||||
done < "$cnf"
|
||||
|
||||
if [ "$inserted" -eq 0 ]; then
|
||||
printf '\n[mariadb]\nbind-address=%s\n' "$desired" >> "$tmp"
|
||||
changed=1
|
||||
fi
|
||||
|
||||
if [ "$changed" -eq 1 ]; then
|
||||
cat "$tmp" > "$cnf"
|
||||
chmod 0640 "$cnf"
|
||||
changed_bind=1
|
||||
fi
|
||||
rm -f "$tmp"
|
||||
}
|
||||
|
||||
reload_csf() {
|
||||
if [ "${MYSQL_HOST_SYNC_SKIP_CSF_RELOAD:-0}" = "1" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if command -v csf >/dev/null 2>&1; then
|
||||
csf -r
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "mysql: nie znaleziono polecenia csf, nie można przeładować firewalla." >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
restart_mariadb_service() {
|
||||
local service="$1"
|
||||
|
||||
if [ "${MYSQL_HOST_SYNC_SKIP_SERVICE_RESTART:-0}" = "1" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl restart "$service"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if command -v service >/dev/null 2>&1; then
|
||||
service "$service" restart
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "mysql: nie znaleziono systemctl/service, nie można zrestartować $service." >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
main() {
|
||||
require_root_or_sudo "$@"
|
||||
load_plugin_settings "$PLUGIN_SETTINGS_FILE"
|
||||
mysql_load_alt_credentials
|
||||
mysql_ensure_metadata_schema
|
||||
|
||||
local csf_dir="${MYSQL_HOST_SYNC_CSF_DIR:-/etc/csf}"
|
||||
local csf_allow="${MYSQL_HOST_SYNC_CSF_ALLOW:-$csf_dir/csf.allow}"
|
||||
local csf_conf="${MYSQL_HOST_SYNC_CSF_CONF:-$csf_dir/csf.conf}"
|
||||
local managed_allow="${MYSQL_HOST_SYNC_MANAGED_ALLOW:-$csf_dir/alt-mysql-plugin.allow}"
|
||||
local instance_cnf="${MYSQL_HOST_SYNC_INSTANCE_CNF:-$MYSQL_ALT_INSTANCE_CNF}"
|
||||
local bind_address="${MYSQL_REMOTE_BIND_ADDRESS:-${MYSQL_PLUGIN_REMOTE_BIND_ADDRESS:-0.0.0.0}}"
|
||||
local service="${MYSQL_HOST_SYNC_SERVICE:-$MYSQL_ALT_SERVICE_NAME}"
|
||||
local changed_csf=0 changed_bind=0 host_output
|
||||
local -a hosts=()
|
||||
|
||||
host_output="$(collect_remote_hosts)"
|
||||
while IFS= read -r host || [ -n "$host" ]; do
|
||||
[ -n "$host" ] || continue
|
||||
hosts+=("$host")
|
||||
done <<< "$host_output"
|
||||
|
||||
write_managed_allow_file "$managed_allow" "$MYSQL_PORT" "${hosts[@]}"
|
||||
ensure_csf_include "$csf_allow" "$managed_allow"
|
||||
remove_port_from_global_csf_lists "$csf_conf" "$MYSQL_PORT"
|
||||
ensure_remote_bind_address "$instance_cnf" "$bind_address"
|
||||
|
||||
if [ "$changed_bind" -eq 1 ]; then
|
||||
restart_mariadb_service "$service"
|
||||
fi
|
||||
|
||||
if [ "$changed_csf" -eq 1 ]; then
|
||||
reload_csf
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
mysql: zsynchronizowano zdalne hosty MariaDB.
|
||||
Port: $MYSQL_PORT
|
||||
Bind address: $bind_address
|
||||
Plik CSF include: $managed_allow
|
||||
Liczba hostów: ${#hosts[@]}
|
||||
EOF
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -0,0 +1,365 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Upgrade alternatywnej instancji MariaDB instalowanej z oficjalnego tarballa.
|
||||
|
||||
TARGET_MARIADB_VERSION="${TARGET_MARIADB_VERSION:-11.8}"
|
||||
MARIADB_DIR="${MARIADB_DIR:-/usr/local/mariadb-$TARGET_MARIADB_VERSION}"
|
||||
DATADIR="${DATADIR:-/var/lib/mariadb_data}"
|
||||
SERVICE_NAME="${SERVICE_NAME:-alt-mariadb}"
|
||||
INSTANCE_CNF="${INSTANCE_CNF:-/etc/alt-mariadb.cnf}"
|
||||
ALT_MY_CNF="${ALT_MY_CNF:-/usr/local/directadmin/conf/alt-my.cnf}"
|
||||
ALT_MYSQL_CONF="${ALT_MYSQL_CONF:-/usr/local/directadmin/conf/alt-mysql.conf}"
|
||||
ALT_METADATA_ENV="${ALT_METADATA_ENV:-/usr/local/directadmin/conf/alt-mariadb.env}"
|
||||
DOWNLOAD_DIR="${DOWNLOAD_DIR:-/usr/local/src}"
|
||||
MARIADB_TARBALL_URL="${MARIADB_TARBALL_URL:-}"
|
||||
FORCE_UPGRADE="${FORCE_UPGRADE:-no}"
|
||||
PORT="${PORT:-}"
|
||||
SOCKET="${SOCKET:-}"
|
||||
|
||||
CURRENT_MARIADB_DIR=""
|
||||
RESOLVED_MARIADB_RELEASE=""
|
||||
DOWNLOAD_URL=""
|
||||
TARBALL_PATH=""
|
||||
TMP_DIR=""
|
||||
TARGET_BACKUP_DIR=""
|
||||
INSTANCE_CNF_BACKUP=""
|
||||
UPGRADE_EXTRACTED=0
|
||||
UPGRADE_COMMITTED=0
|
||||
SERVICE_WAS_ACTIVE=0
|
||||
|
||||
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
|
||||
|
||||
rollback_upgrade() {
|
||||
local code="$1"
|
||||
if [ "$code" -eq 0 ] || [ "$UPGRADE_COMMITTED" -eq 1 ]; then
|
||||
cleanup
|
||||
return 0
|
||||
fi
|
||||
|
||||
warn "Upgrade nie zakończył się poprawnie, uruchamiam rollback."
|
||||
|
||||
if [ -n "$INSTANCE_CNF_BACKUP" ] && [ -r "$INSTANCE_CNF_BACKUP" ]; then
|
||||
cp -a "$INSTANCE_CNF_BACKUP" "$INSTANCE_CNF" || true
|
||||
warn "RESTORED_INSTANCE_CNF: $INSTANCE_CNF"
|
||||
fi
|
||||
|
||||
if [ "$UPGRADE_EXTRACTED" -eq 1 ] && [ -d "$MARIADB_DIR" ]; then
|
||||
if [ -n "$TARGET_BACKUP_DIR" ] && [ -d "$TARGET_BACKUP_DIR" ]; then
|
||||
rm -rf "$MARIADB_DIR" || true
|
||||
elif [ "$MARIADB_DIR" != "$CURRENT_MARIADB_DIR" ]; then
|
||||
rm -rf "$MARIADB_DIR" || true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$TARGET_BACKUP_DIR" ] && [ -d "$TARGET_BACKUP_DIR" ] && [ ! -e "$MARIADB_DIR" ]; then
|
||||
mv "$TARGET_BACKUP_DIR" "$MARIADB_DIR" || true
|
||||
warn "RESTORED_PREVIOUS_DIR: $MARIADB_DIR"
|
||||
fi
|
||||
|
||||
if [ "$SERVICE_WAS_ACTIVE" -eq 1 ]; then
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl daemon-reload || true
|
||||
systemctl restart "$SERVICE_NAME" || true
|
||||
fi
|
||||
fi
|
||||
|
||||
cleanup
|
||||
}
|
||||
trap 'code=$?; rollback_upgrade "$code"' EXIT
|
||||
|
||||
require_root() {
|
||||
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||||
die "Ten skrypt musi być uruchomiony jako root."
|
||||
fi
|
||||
}
|
||||
|
||||
validate_version() {
|
||||
case "$TARGET_MARIADB_VERSION" in
|
||||
10.11|11.4|11.8)
|
||||
log "Wybrano docelową wersję MariaDB: $TARGET_MARIADB_VERSION."
|
||||
;;
|
||||
*)
|
||||
die "Nieobsługiwana wersja MariaDB: $TARGET_MARIADB_VERSION. Dozwolone wartości: 10.11, 11.4, 11.8."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
detect_architecture() {
|
||||
case "$(uname -m)" in
|
||||
x86_64|amd64) printf 'x86_64\n' ;;
|
||||
*) die "Nieobsługiwana architektura: $(uname -m). Skrypt obsługuje automatycznie tylko x86_64/amd64." ;;
|
||||
esac
|
||||
}
|
||||
|
||||
detect_current_mariadb_dir() {
|
||||
if [ -r "$INSTANCE_CNF" ]; then
|
||||
CURRENT_MARIADB_DIR="$(awk -F= '
|
||||
/^[[:space:]]*basedir[[:space:]]*=/ {
|
||||
value=$2
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
|
||||
print value
|
||||
exit
|
||||
}
|
||||
' "$INSTANCE_CNF")"
|
||||
DATADIR="$(awk -F= '
|
||||
/^[[:space:]]*datadir[[:space:]]*=/ {
|
||||
value=$2
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
|
||||
print value
|
||||
exit
|
||||
}
|
||||
' "$INSTANCE_CNF")"
|
||||
PORT="$(awk -F= '
|
||||
/^[[:space:]]*port[[:space:]]*=/ {
|
||||
value=$2
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
|
||||
print value
|
||||
exit
|
||||
}
|
||||
' "$INSTANCE_CNF")"
|
||||
SOCKET="$(awk -F= '
|
||||
/^[[:space:]]*socket[[:space:]]*=/ {
|
||||
value=$2
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
|
||||
print value
|
||||
exit
|
||||
}
|
||||
' "$INSTANCE_CNF")"
|
||||
fi
|
||||
|
||||
if [ -z "$CURRENT_MARIADB_DIR" ] && systemctl cat "$SERVICE_NAME" >/dev/null 2>&1; then
|
||||
CURRENT_MARIADB_DIR="$(systemctl cat "$SERVICE_NAME" \
|
||||
| awk '/ExecStart=/ {
|
||||
sub(/^.*ExecStart=/, "")
|
||||
sub(/\/bin\/mariadbd.*$/, "")
|
||||
print
|
||||
exit
|
||||
}')"
|
||||
fi
|
||||
|
||||
[ -n "$CURRENT_MARIADB_DIR" ] || die "Nie udało się wykryć bieżącego katalogu MariaDB."
|
||||
[ -x "$CURRENT_MARIADB_DIR/bin/mariadbd" ] || die "Bieżący katalog MariaDB nie zawiera bin/mariadbd: $CURRENT_MARIADB_DIR."
|
||||
[ -n "$DATADIR" ] || die "Nie udało się wykryć katalogu danych z $INSTANCE_CNF."
|
||||
log "Wykryto bieżący katalog MariaDB: $CURRENT_MARIADB_DIR."
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
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=""
|
||||
|
||||
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
|
||||
|
||||
RESOLVED_MARIADB_RELEASE="$(resolve_latest_release_from_archive "$TARGET_MARIADB_VERSION" || true)"
|
||||
[ -n "$RESOLVED_MARIADB_RELEASE" ] || die "Nie udało się wykryć najnowszego wydania dla gałęzi $TARGET_MARIADB_VERSION."
|
||||
DOWNLOAD_URL="$(resolve_tarball_from_directory "$RESOLVED_MARIADB_RELEASE" "$arch" || true)"
|
||||
[ -n "$DOWNLOAD_URL" ] || die "Nie udało się automatycznie ustalić URL tarballa. Ustaw MARIADB_TARBALL_URL."
|
||||
log "Wykryto tarball MariaDB $RESOLVED_MARIADB_RELEASE."
|
||||
}
|
||||
|
||||
download_tarball() {
|
||||
local filename=""
|
||||
|
||||
install -d -m 0755 "$DOWNLOAD_DIR"
|
||||
filename="$(basename "${DOWNLOAD_URL%%\?*}")"
|
||||
TARBALL_PATH="$DOWNLOAD_DIR/$filename"
|
||||
log "Pobieram 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 extracted_dir=""
|
||||
local backup_dir=""
|
||||
|
||||
if [ -d "$MARIADB_DIR" ] && [ -n "$(find "$MARIADB_DIR" -mindepth 1 -maxdepth 1 -print -quit)" ]; then
|
||||
if [ "$FORCE_UPGRADE" != "yes" ]; then
|
||||
die "Katalog docelowy $MARIADB_DIR już istnieje. Ustaw FORCE_UPGRADE=yes, aby zastąpić go po kopii."
|
||||
fi
|
||||
TARGET_BACKUP_DIR="${MARIADB_DIR}.pre-upgrade.$(date +%Y%m%d%H%M%S)"
|
||||
log "Tworzę kopię istniejącego katalogu docelowego: $TARGET_BACKUP_DIR."
|
||||
mv "$MARIADB_DIR" "$TARGET_BACKUP_DIR"
|
||||
fi
|
||||
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
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 MariaDB."
|
||||
mv "$extracted_dir" "$MARIADB_DIR"
|
||||
chown -R root:root "$MARIADB_DIR"
|
||||
UPGRADE_EXTRACTED=1
|
||||
log "Nowe binaria MariaDB są w $MARIADB_DIR."
|
||||
}
|
||||
|
||||
rewrite_instance_basedir() {
|
||||
local backup=""
|
||||
|
||||
[ -r "$INSTANCE_CNF" ] || die "Brak konfiguracji instancji: $INSTANCE_CNF."
|
||||
backup="${INSTANCE_CNF}.pre-upgrade.$(date +%Y%m%d%H%M%S)"
|
||||
cp -a "$INSTANCE_CNF" "$backup"
|
||||
INSTANCE_CNF_BACKUP="$backup"
|
||||
sed -i "s#^[[:space:]]*basedir[[:space:]]*=.*#basedir=$MARIADB_DIR#" "$INSTANCE_CNF"
|
||||
sed -i "s#^[[:space:]]*plugin-dir[[:space:]]*=.*#plugin-dir=$MARIADB_DIR/lib/plugin#" "$INSTANCE_CNF"
|
||||
log "Zaktualizowano basedir i plugin-dir w $INSTANCE_CNF. Kopia: $backup."
|
||||
}
|
||||
|
||||
restart_service() {
|
||||
log "Restartuję usługę $SERVICE_NAME."
|
||||
systemctl daemon-reload
|
||||
systemctl restart "$SERVICE_NAME"
|
||||
sleep 5
|
||||
systemctl is-active --quiet "$SERVICE_NAME" || {
|
||||
systemctl status "$SERVICE_NAME" --no-pager || true
|
||||
die "Usługa $SERVICE_NAME nie uruchomiła się po upgrade."
|
||||
}
|
||||
}
|
||||
|
||||
run_mariadb_upgrade() {
|
||||
local upgrade_bin=""
|
||||
|
||||
if [ -x "$MARIADB_DIR/bin/mariadb-upgrade" ]; then
|
||||
upgrade_bin="$MARIADB_DIR/bin/mariadb-upgrade"
|
||||
elif [ -x "$MARIADB_DIR/bin/mysql_upgrade" ]; then
|
||||
upgrade_bin="$MARIADB_DIR/bin/mysql_upgrade"
|
||||
else
|
||||
warn "Nie znaleziono mariadb-upgrade ani mysql_upgrade, pomijam etap aktualizacji tabel systemowych."
|
||||
return 0
|
||||
fi
|
||||
|
||||
[ -r "$ALT_MY_CNF" ] || die "Brak pliku klienta alternatywnej instancji: $ALT_MY_CNF."
|
||||
log "Uruchamiam aktualizację tabel systemowych MariaDB."
|
||||
"$upgrade_bin" --defaults-extra-file="$ALT_MY_CNF"
|
||||
}
|
||||
|
||||
credential_group() {
|
||||
if getent group diradmin >/dev/null 2>&1; then
|
||||
printf 'diradmin\n'
|
||||
else
|
||||
printf 'root\n'
|
||||
fi
|
||||
}
|
||||
|
||||
sh_single_quote() {
|
||||
local value="$1"
|
||||
value="${value//\'/\'\\\'\'}"
|
||||
printf "'%s'" "$value"
|
||||
}
|
||||
|
||||
write_alt_metadata_env() {
|
||||
local group_name metadata_mode tmp_file
|
||||
group_name="$(credential_group)"
|
||||
metadata_mode="0600"
|
||||
if [ "$group_name" = "diradmin" ]; then
|
||||
metadata_mode="0640"
|
||||
fi
|
||||
|
||||
tmp_file="$(mktemp)"
|
||||
cat > "$tmp_file" <<EOF
|
||||
# Metadata alternatywnej instancji MariaDB zarządzanej przez mysql_upgrade.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 "$TARGET_MARIADB_VERSION")
|
||||
MARIADB_RELEASE=$(sh_single_quote "${RESOLVED_MARIADB_RELEASE:-$TARGET_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")
|
||||
EOF
|
||||
install -m "$metadata_mode" -o root -g "$group_name" "$tmp_file" "$ALT_METADATA_ENV"
|
||||
rm -f "$tmp_file"
|
||||
log "Zaktualizowano metadane alternatywnej instancji: $ALT_METADATA_ENV."
|
||||
}
|
||||
|
||||
print_summary() {
|
||||
cat <<EOF
|
||||
|
||||
Upgrade alternatywnej instancji MariaDB zakończony.
|
||||
|
||||
Poprzedni katalog programu: $CURRENT_MARIADB_DIR
|
||||
Nowy katalog programu: $MARIADB_DIR
|
||||
Katalog danych: $DATADIR
|
||||
Konfiguracja instancji: $INSTANCE_CNF
|
||||
Usługa systemd: $SERVICE_NAME
|
||||
|
||||
Polecenie testowe:
|
||||
$MARIADB_DIR/bin/mariadb --defaults-extra-file=$ALT_MY_CNF -e "SELECT VERSION();"
|
||||
EOF
|
||||
}
|
||||
|
||||
main() {
|
||||
require_root
|
||||
validate_version
|
||||
detect_current_mariadb_dir
|
||||
resolve_download_url
|
||||
download_tarball
|
||||
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
||||
SERVICE_WAS_ACTIVE=1
|
||||
fi
|
||||
systemctl stop "$SERVICE_NAME"
|
||||
extract_tarball
|
||||
rewrite_instance_basedir
|
||||
restart_service
|
||||
run_mariadb_upgrade
|
||||
write_alt_metadata_env
|
||||
UPGRADE_COMMITTED=1
|
||||
print_summary
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
PHPMYADMIN_DIR="${PHPMYADMIN_PRIVATE_DIR:-${PHPMYADMIN_DIR:-/var/www/html/alt-mysql-phpmyadmin}}"
|
||||
CONFIG_INC="${PHPMYADMIN_DIR}/config.inc.php"
|
||||
SIGNON_SCRIPT="${PHPMYADMIN_DIR}/da_signon.php"
|
||||
SIGNON_URL_PAGE="${PHPMYADMIN_DIR}/da_login.php"
|
||||
RUNTIME_CONFIG="${PHPMYADMIN_DIR}/runtime/config.json"
|
||||
|
||||
fail() {
|
||||
printf 'phpMyAdmin alt-mysql health: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ -d "$PHPMYADMIN_DIR" ] || fail "phpMyAdmin directory not found: $PHPMYADMIN_DIR"
|
||||
[ -r "$PHPMYADMIN_DIR/index.php" ] || fail "phpMyAdmin index.php is not readable: $PHPMYADMIN_DIR/index.php"
|
||||
[ -r "$CONFIG_INC" ] || fail "config.inc.php is not readable: $CONFIG_INC"
|
||||
[ -r "$SIGNON_SCRIPT" ] || fail "signon script is not readable: $SIGNON_SCRIPT"
|
||||
[ -r "$SIGNON_URL_PAGE" ] || fail "SignonURL page is not readable: $SIGNON_URL_PAGE"
|
||||
grep -Fq "DA_MYSQL_PMA_DEFAULT_CONF" "$CONFIG_INC" || fail "config.inc.php does not configure alt-mysql endpoint"
|
||||
grep -Fq "auth_type'] = 'signon" "$CONFIG_INC" || fail "config.inc.php does not configure signon auth"
|
||||
grep -Fq "SignonURL'] = da_mysql_phpmyadmin_signon_url" "$CONFIG_INC" || fail "config.inc.php does not configure dynamic SignonURL"
|
||||
grep -Fq "da_mysql_login_consume_ticket" "$SIGNON_URL_PAGE" || fail "da_login.php does not consume SSO tickets"
|
||||
grep -Fq "DA_MYSQL_DEFAULT_CONF = '/usr/local/directadmin/conf/alt-mysql.conf'" "$SIGNON_SCRIPT" \
|
||||
|| fail "signon script does not default to alt-mysql.conf"
|
||||
|
||||
if [ -r "$RUNTIME_CONFIG" ] && command -v php >/dev/null 2>&1; then
|
||||
php -r '
|
||||
$path = $argv[1];
|
||||
$data = json_decode(file_get_contents($path), true);
|
||||
if (!is_array($data)) {
|
||||
fwrite(STDERR, "runtime config is not valid JSON\n");
|
||||
exit(2);
|
||||
}
|
||||
if (($data["credentials_file"] ?? "") === "/usr/local/directadmin/conf/mysql.conf") {
|
||||
fwrite(STDERR, "runtime config points to native DirectAdmin mysql.conf\n");
|
||||
exit(3);
|
||||
}
|
||||
' "$RUNTIME_CONFIG" || fail "runtime config validation failed"
|
||||
fi
|
||||
|
||||
echo "phpMyAdmin alt-mysql health: OK"
|
||||
@@ -0,0 +1,486 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
PHPMYADMIN_PRIVATE_DIR="${PHPMYADMIN_PRIVATE_DIR:-${PHPMYADMIN_DIR:-/var/www/html/alt-mysql-phpmyadmin}}"
|
||||
PHPMYADMIN_VERSION="${PHPMYADMIN_VERSION:-5.2.3}"
|
||||
PHPMYADMIN_TARBALL_URL="${PHPMYADMIN_TARBALL_URL:-https://files.phpmyadmin.net/phpMyAdmin/5.2.3/phpMyAdmin-5.2.3-all-languages.tar.gz}"
|
||||
PHPMYADMIN_DOWNLOAD_DIR="${PHPMYADMIN_DOWNLOAD_DIR:-/usr/local/src}"
|
||||
PHPMYADMIN_SOURCE_DIR="${PHPMYADMIN_SOURCE_DIR:-}"
|
||||
RUNTIME_DIR="${PHPMYADMIN_PRIVATE_DIR}/runtime"
|
||||
TICKETS_DIR="${RUNTIME_DIR}/tickets"
|
||||
CONFIG_INC="${PHPMYADMIN_PRIVATE_DIR}/config.inc.php"
|
||||
SIGNON_SCRIPT="${PHPMYADMIN_PRIVATE_DIR}/da_signon.php"
|
||||
SIGNON_URL_PAGE="${PHPMYADMIN_PRIVATE_DIR}/da_login.php"
|
||||
BLOWFISH_SECRET_FILE="${RUNTIME_DIR}/blowfish_secret"
|
||||
|
||||
if [ "${PHPMYADMIN_INSTALL_ASSUME_ROOT:-0}" != "1" ] && [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||||
echo "Skrypt musi być uruchomiony jako root." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log() {
|
||||
printf 'phpMyAdmin alt-mysql: %s\n' "$*"
|
||||
}
|
||||
|
||||
die() {
|
||||
printf 'phpMyAdmin alt-mysql: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
install_private_copy_from_source() {
|
||||
local source_dir="$1"
|
||||
[ -d "$source_dir" ] || die "Katalog źródłowy phpMyAdmin nie istnieje: $source_dir"
|
||||
[ -f "$source_dir/index.php" ] || die "Katalog źródłowy nie wygląda jak phpMyAdmin: $source_dir"
|
||||
|
||||
mkdir -p "$PHPMYADMIN_PRIVATE_DIR"
|
||||
cp -a "$source_dir"/. "$PHPMYADMIN_PRIVATE_DIR"/
|
||||
}
|
||||
|
||||
download_private_copy() {
|
||||
local tarball extracted tmp_dir
|
||||
|
||||
command -v curl >/dev/null 2>&1 || die "Brak curl; ustaw PHPMYADMIN_SOURCE_DIR albo doinstaluj curl."
|
||||
command -v tar >/dev/null 2>&1 || die "Brak tar."
|
||||
|
||||
mkdir -p "$PHPMYADMIN_DOWNLOAD_DIR"
|
||||
tarball="$PHPMYADMIN_DOWNLOAD_DIR/phpMyAdmin-${PHPMYADMIN_VERSION}-all-languages.tar.gz"
|
||||
if [ ! -s "$tarball" ]; then
|
||||
log "Pobieram prywatny phpMyAdmin ${PHPMYADMIN_VERSION}."
|
||||
curl -fL --retry 3 --connect-timeout 20 -o "$tarball" "$PHPMYADMIN_TARBALL_URL"
|
||||
fi
|
||||
|
||||
tmp_dir="$(mktemp -d)"
|
||||
tar -xzf "$tarball" -C "$tmp_dir"
|
||||
extracted="$(find "$tmp_dir" -mindepth 1 -maxdepth 1 -type d -name 'phpMyAdmin-*' -print -quit)"
|
||||
[ -n "$extracted" ] || {
|
||||
rm -rf "$tmp_dir"
|
||||
die "Tarball phpMyAdmin nie zawiera oczekiwanego katalogu."
|
||||
}
|
||||
|
||||
install_private_copy_from_source "$extracted"
|
||||
rm -rf "$tmp_dir"
|
||||
}
|
||||
|
||||
ensure_private_copy() {
|
||||
if [ -f "$PHPMYADMIN_PRIVATE_DIR/index.php" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
rm -rf "$PHPMYADMIN_PRIVATE_DIR"
|
||||
if [ -n "$PHPMYADMIN_SOURCE_DIR" ]; then
|
||||
install_private_copy_from_source "$PHPMYADMIN_SOURCE_DIR"
|
||||
else
|
||||
download_private_copy
|
||||
fi
|
||||
}
|
||||
|
||||
generate_secret() {
|
||||
if [ -r "$BLOWFISH_SECRET_FILE" ]; then
|
||||
local existing
|
||||
existing="$(tr -d '\r\n' < "$BLOWFISH_SECRET_FILE")"
|
||||
if [ -n "$existing" ]; then
|
||||
printf '%s\n' "$existing"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
openssl rand -hex 32
|
||||
return 0
|
||||
fi
|
||||
|
||||
tr -dc 'a-f0-9' </dev/urandom | head -c 64
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
write_private_config() {
|
||||
local secret
|
||||
|
||||
mkdir -p "$RUNTIME_DIR" "$TICKETS_DIR"
|
||||
secret="$(generate_secret)"
|
||||
printf '%s\n' "$secret" > "$BLOWFISH_SECRET_FILE"
|
||||
chmod 600 "$BLOWFISH_SECRET_FILE" 2>/dev/null || true
|
||||
cat > "$RUNTIME_DIR/.htaccess" <<'EOF'
|
||||
Require all denied
|
||||
Deny from all
|
||||
EOF
|
||||
cat > "$TICKETS_DIR/.htaccess" <<'EOF'
|
||||
Require all denied
|
||||
Deny from all
|
||||
EOF
|
||||
printf '%s\n' '<!doctype html><title>Forbidden</title>' > "$RUNTIME_DIR/index.html"
|
||||
printf '%s\n' '<!doctype html><title>Forbidden</title>' > "$TICKETS_DIR/index.html"
|
||||
|
||||
cat > "$CONFIG_INC" <<'PHP'
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
const DA_MYSQL_PMA_DEFAULT_CONF = '/usr/local/directadmin/conf/alt-mysql.conf';
|
||||
const DA_MYSQL_PMA_RUNTIME_CONFIG = __DIR__ . '/runtime/config.json';
|
||||
const DA_MYSQL_PMA_BLOWFISH_SECRET_FILE = __DIR__ . '/runtime/blowfish_secret';
|
||||
|
||||
function da_mysql_phpmyadmin_runtime_config(): array
|
||||
{
|
||||
if (!is_readable(DA_MYSQL_PMA_RUNTIME_CONFIG)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$raw = file_get_contents(DA_MYSQL_PMA_RUNTIME_CONFIG);
|
||||
if (!is_string($raw) || $raw === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$decoded = json_decode($raw, true);
|
||||
return is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
|
||||
function da_mysql_phpmyadmin_parse_conf(string $path): array
|
||||
{
|
||||
$data = [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => '33033',
|
||||
'socket' => '',
|
||||
];
|
||||
|
||||
if (!is_readable($path)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$lines = file($path, FILE_IGNORE_NEW_LINES);
|
||||
if ($lines === false) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
if ($line === '' || $line[0] === '#' || strpos($line, '=') === false) {
|
||||
continue;
|
||||
}
|
||||
[$key, $value] = explode('=', $line, 2);
|
||||
$key = strtolower(trim($key));
|
||||
$value = trim(trim($value), "\"'");
|
||||
if ($key === 'host' && $value !== '') {
|
||||
$data['host'] = $value;
|
||||
} elseif (($key === 'port' || $key === 'mysql_port') && ctype_digit($value)) {
|
||||
$data['port'] = $value;
|
||||
} elseif (($key === 'socket' || $key === 'mysql_socket') && $value !== '') {
|
||||
$data['socket'] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if ($data['host'] === '' || $data['host'] === 'localhost') {
|
||||
$data['host'] = '127.0.0.1';
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
function da_mysql_phpmyadmin_blowfish_secret(): string
|
||||
{
|
||||
if (is_readable(DA_MYSQL_PMA_BLOWFISH_SECRET_FILE)) {
|
||||
$secret = trim((string)file_get_contents(DA_MYSQL_PMA_BLOWFISH_SECRET_FILE));
|
||||
if ($secret !== '') {
|
||||
return $secret;
|
||||
}
|
||||
}
|
||||
|
||||
return 'alt-mysql-phpmyadmin-secret-change-me';
|
||||
}
|
||||
|
||||
function da_mysql_phpmyadmin_url_path(array $runtimeConfig): string
|
||||
{
|
||||
$path = trim((string)($runtimeConfig['phpmyadmin_url_path'] ?? '/alt-mysql-phpmyadmin'));
|
||||
if ($path === '') {
|
||||
return '/alt-mysql-phpmyadmin';
|
||||
}
|
||||
if ($path[0] !== '/') {
|
||||
$path = '/' . $path;
|
||||
}
|
||||
|
||||
$path = rtrim($path, '/');
|
||||
return $path !== '' ? $path : '/alt-mysql-phpmyadmin';
|
||||
}
|
||||
|
||||
function da_mysql_phpmyadmin_public_base_url(array $runtimeConfig): string
|
||||
{
|
||||
$configured = trim((string)($runtimeConfig['phpmyadmin_public_base_url'] ?? ''));
|
||||
if (preg_match('#^https?://#i', $configured) === 1) {
|
||||
return rtrim($configured, '/');
|
||||
}
|
||||
|
||||
$scheme = 'http';
|
||||
$https = strtolower((string)($_SERVER['HTTPS'] ?? ''));
|
||||
if ($https !== '' && $https !== 'off' && $https !== '0') {
|
||||
$scheme = 'https';
|
||||
} elseif (strtolower((string)($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '')) === 'https') {
|
||||
$scheme = 'https';
|
||||
} elseif (strtolower((string)($_SERVER['REQUEST_SCHEME'] ?? '')) === 'https') {
|
||||
$scheme = 'https';
|
||||
}
|
||||
|
||||
$host = trim((string)($_SERVER['HTTP_HOST'] ?? ''));
|
||||
if ($host === '') {
|
||||
$host = trim((string)($_SERVER['SERVER_NAME'] ?? 'localhost'));
|
||||
}
|
||||
if ($host === '') {
|
||||
$host = 'localhost';
|
||||
}
|
||||
|
||||
return $scheme . '://' . $host;
|
||||
}
|
||||
|
||||
function da_mysql_phpmyadmin_signon_url(array $runtimeConfig): string
|
||||
{
|
||||
return da_mysql_phpmyadmin_public_base_url($runtimeConfig)
|
||||
. da_mysql_phpmyadmin_url_path($runtimeConfig)
|
||||
. '/da_login.php';
|
||||
}
|
||||
|
||||
$runtimeConfig = da_mysql_phpmyadmin_runtime_config();
|
||||
$credentialsFile = (string)($runtimeConfig['credentials_file'] ?? DA_MYSQL_PMA_DEFAULT_CONF);
|
||||
if ($credentialsFile === '') {
|
||||
$credentialsFile = DA_MYSQL_PMA_DEFAULT_CONF;
|
||||
}
|
||||
$daMysqlServer = da_mysql_phpmyadmin_parse_conf($credentialsFile);
|
||||
|
||||
$cfg = [];
|
||||
$cfg['blowfish_secret'] = da_mysql_phpmyadmin_blowfish_secret();
|
||||
$cfg['TempDir'] = __DIR__ . '/runtime/tmp';
|
||||
$cfg['LoginCookieValidity'] = 900;
|
||||
|
||||
$i = 0;
|
||||
$i++;
|
||||
$cfg['Servers'][$i]['host'] = $daMysqlServer['host'];
|
||||
$cfg['Servers'][$i]['port'] = $daMysqlServer['port'];
|
||||
$cfg['Servers'][$i]['socket'] = $daMysqlServer['host'] === 'localhost' ? $daMysqlServer['socket'] : '';
|
||||
$cfg['Servers'][$i]['auth_type'] = 'signon';
|
||||
$cfg['Servers'][$i]['SignonScript'] = __DIR__ . '/da_signon.php';
|
||||
$cfg['Servers'][$i]['SignonSession'] = 'DA_MYSQL_PHPMYADMIN';
|
||||
$cfg['Servers'][$i]['SignonURL'] = da_mysql_phpmyadmin_signon_url($runtimeConfig);
|
||||
$cfg['Servers'][$i]['LogoutURL'] = da_mysql_phpmyadmin_signon_url($runtimeConfig);
|
||||
$cfg['Servers'][$i]['AllowNoPassword'] = false;
|
||||
$cfg['Servers'][$i]['verbose'] = 'DirectAdmin alt-mysql';
|
||||
$cfg['Servers'][$i]['only_db'] = '';
|
||||
$cfg['ServerDefault'] = 1;
|
||||
$cfg['AllowArbitraryServer'] = false;
|
||||
PHP
|
||||
|
||||
cat > "$SIGNON_URL_PAGE" <<'PHP'
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
const DA_MYSQL_SESSION = 'DA_MYSQL_PHPMYADMIN';
|
||||
const DA_MYSQL_RUNTIME_CONFIG = __DIR__ . '/runtime/config.json';
|
||||
|
||||
function da_mysql_login_runtime_config(): array
|
||||
{
|
||||
if (!is_readable(DA_MYSQL_RUNTIME_CONFIG)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$raw = file_get_contents(DA_MYSQL_RUNTIME_CONFIG);
|
||||
if (!is_string($raw) || $raw === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$decoded = json_decode($raw, true);
|
||||
return is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
|
||||
function da_mysql_login_url_path(array $runtimeConfig): string
|
||||
{
|
||||
$path = trim((string)($runtimeConfig['phpmyadmin_url_path'] ?? '/alt-mysql-phpmyadmin'));
|
||||
if ($path === '') {
|
||||
return '/alt-mysql-phpmyadmin';
|
||||
}
|
||||
if ($path[0] !== '/') {
|
||||
$path = '/' . $path;
|
||||
}
|
||||
|
||||
$path = rtrim($path, '/');
|
||||
return $path !== '' ? $path : '/alt-mysql-phpmyadmin';
|
||||
}
|
||||
|
||||
function da_mysql_login_ticket_dir(array $runtimeConfig): string
|
||||
{
|
||||
$dir = trim((string)($runtimeConfig['ticket_dir'] ?? ''));
|
||||
if ($dir !== '') {
|
||||
return rtrim($dir, '/');
|
||||
}
|
||||
|
||||
return __DIR__ . '/runtime/tickets';
|
||||
}
|
||||
|
||||
function da_mysql_login_error(string $message): void
|
||||
{
|
||||
http_response_code(400);
|
||||
header('Content-Type: text/html; charset=UTF-8');
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Pragma: no-cache');
|
||||
$safe = htmlspecialchars($message, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
echo '<!doctype html><html lang="en"><head><meta charset="utf-8"><title>alt-mysql phpMyAdmin</title></head><body>';
|
||||
echo '<h1>alt-mysql phpMyAdmin</h1><p>' . $safe . '</p>';
|
||||
echo '</body></html>';
|
||||
exit;
|
||||
}
|
||||
|
||||
function da_mysql_login_consume_ticket(array $runtimeConfig): array
|
||||
{
|
||||
$token = strtolower((string)($_GET['ticket'] ?? ''));
|
||||
if (preg_match('/\A[a-f0-9]{64}\z/', $token) !== 1) {
|
||||
da_mysql_login_error('Missing or invalid phpMyAdmin login ticket.');
|
||||
}
|
||||
|
||||
$path = da_mysql_login_ticket_dir($runtimeConfig) . '/' . $token . '.json';
|
||||
if (!is_readable($path)) {
|
||||
da_mysql_login_error('The phpMyAdmin login ticket is not available or has already been used.');
|
||||
}
|
||||
|
||||
$raw = file_get_contents($path);
|
||||
@unlink($path);
|
||||
$ticket = is_string($raw) ? json_decode($raw, true) : null;
|
||||
if (!is_array($ticket)) {
|
||||
da_mysql_login_error('The phpMyAdmin login ticket is invalid.');
|
||||
}
|
||||
|
||||
if ((int)($ticket['expires_at'] ?? 0) < time()) {
|
||||
da_mysql_login_error('The phpMyAdmin login ticket has expired.');
|
||||
}
|
||||
|
||||
$auth = $ticket['auth'] ?? [];
|
||||
if (!is_array($auth) || (string)($auth['user'] ?? '') === '' || (string)($auth['password'] ?? '') === '') {
|
||||
da_mysql_login_error('The phpMyAdmin login ticket does not contain credentials.');
|
||||
}
|
||||
|
||||
return $ticket;
|
||||
}
|
||||
|
||||
function da_mysql_login_redirect_target(array $ticket): string
|
||||
{
|
||||
$route = (string)($ticket['route'] ?? '/database/structure');
|
||||
if (preg_match('#^/[A-Za-z0-9_./-]+$#', $route) !== 1 || strpos($route, '//') !== false) {
|
||||
$route = '/';
|
||||
}
|
||||
|
||||
$query = ['route' => $route];
|
||||
$db = (string)($ticket['db'] ?? ($ticket['auth']['db'] ?? ''));
|
||||
if ($db !== '') {
|
||||
$query['db'] = $db;
|
||||
}
|
||||
|
||||
return 'index.php?' . http_build_query($query);
|
||||
}
|
||||
|
||||
$runtimeConfig = da_mysql_login_runtime_config();
|
||||
$ticket = da_mysql_login_consume_ticket($runtimeConfig);
|
||||
$secure = false;
|
||||
$https = strtolower((string)($_SERVER['HTTPS'] ?? ''));
|
||||
if ($https !== '' && $https !== 'off' && $https !== '0') {
|
||||
$secure = true;
|
||||
} elseif (strtolower((string)($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '')) === 'https') {
|
||||
$secure = true;
|
||||
}
|
||||
|
||||
session_name(DA_MYSQL_SESSION);
|
||||
session_set_cookie_params([
|
||||
'lifetime' => 0,
|
||||
'path' => da_mysql_login_url_path($runtimeConfig),
|
||||
'secure' => $secure,
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax',
|
||||
]);
|
||||
session_start();
|
||||
$_SESSION['DA_MYSQL_PHPMYADMIN_AUTH'] = $ticket['auth'];
|
||||
session_write_close();
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Pragma: no-cache');
|
||||
header('Location: ' . da_mysql_login_redirect_target($ticket), true, 302);
|
||||
exit;
|
||||
PHP
|
||||
|
||||
cat > "$SIGNON_SCRIPT" <<'PHP'
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
const DA_MYSQL_SESSION = 'DA_MYSQL_PHPMYADMIN';
|
||||
const DA_MYSQL_DEFAULT_CONF = '/usr/local/directadmin/conf/alt-mysql.conf';
|
||||
const DA_MYSQL_RUNTIME_CONFIG = __DIR__ . '/runtime/config.json';
|
||||
|
||||
function da_mysql_runtime_config(): array
|
||||
{
|
||||
if (!is_readable(DA_MYSQL_RUNTIME_CONFIG)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$raw = file_get_contents(DA_MYSQL_RUNTIME_CONFIG);
|
||||
if (!is_string($raw) || $raw === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$decoded = json_decode($raw, true);
|
||||
return is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
|
||||
function da_mysql_signon_session_auth(): array
|
||||
{
|
||||
$payload = $_SESSION['DA_MYSQL_PHPMYADMIN_AUTH'] ?? [];
|
||||
return is_array($payload) ? $payload : [];
|
||||
}
|
||||
|
||||
function da_mysql_signon_read_auth(): array
|
||||
{
|
||||
$previousActive = session_status() === PHP_SESSION_ACTIVE;
|
||||
$previousName = session_name();
|
||||
$previousId = $previousActive ? session_id() : '';
|
||||
|
||||
if ($previousActive && $previousName === DA_MYSQL_SESSION) {
|
||||
return da_mysql_signon_session_auth();
|
||||
}
|
||||
|
||||
if ($previousActive) {
|
||||
session_write_close();
|
||||
}
|
||||
|
||||
session_name(DA_MYSQL_SESSION);
|
||||
session_id('');
|
||||
$ssoSessionId = (string)($_COOKIE[DA_MYSQL_SESSION] ?? '');
|
||||
if ($ssoSessionId !== '' && preg_match('/\A[A-Za-z0-9,-]+\z/', $ssoSessionId) === 1) {
|
||||
session_id($ssoSessionId);
|
||||
}
|
||||
session_start();
|
||||
$payload = da_mysql_signon_session_auth();
|
||||
session_write_close();
|
||||
|
||||
if ($previousActive) {
|
||||
session_name($previousName);
|
||||
if ($previousId !== '') {
|
||||
session_id($previousId);
|
||||
}
|
||||
session_start();
|
||||
} elseif ($previousName !== '') {
|
||||
session_name($previousName);
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
function get_login_credentials($user = ''): array
|
||||
{
|
||||
$payload = da_mysql_signon_read_auth();
|
||||
$username = (string)($payload['user'] ?? '');
|
||||
$password = (string)($payload['password'] ?? '');
|
||||
return [$username, $password];
|
||||
}
|
||||
PHP
|
||||
|
||||
mkdir -p "$RUNTIME_DIR/tmp"
|
||||
chown -R diradmin:diradmin "$RUNTIME_DIR" "$CONFIG_INC" "$SIGNON_SCRIPT" "$SIGNON_URL_PAGE" 2>/dev/null || true
|
||||
chmod 755 "$RUNTIME_DIR" "$TICKETS_DIR" "$RUNTIME_DIR/tmp" 2>/dev/null || true
|
||||
chmod 644 "$CONFIG_INC" "$SIGNON_SCRIPT" "$SIGNON_URL_PAGE" "$RUNTIME_DIR/.htaccess" "$TICKETS_DIR/.htaccess" "$RUNTIME_DIR/index.html" "$TICKETS_DIR/index.html" 2>/dev/null || true
|
||||
}
|
||||
|
||||
ensure_private_copy
|
||||
write_private_config
|
||||
|
||||
log "Prywatna kopia phpMyAdmin dla alt-mysql jest gotowa w ${PHPMYADMIN_PRIVATE_DIR}."
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
exec "$SCRIPT_DIR/phpmyadmin_install.sh" "$@"
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
exec "$SCRIPT_DIR/phpmyadmin_install.sh"
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
CB_DIR="/usr/local/directadmin/custombuild"
|
||||
|
||||
[ "${EUID:-$(id -u)}" -eq 0 ] || {
|
||||
echo "Skrypt musi być uruchomiony jako root." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ -x "${CB_DIR}/build" ] || {
|
||||
echo "Nie znaleziono DirectAdmin CustomBuild w ${CB_DIR}." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
php_slots=()
|
||||
for slot in 1 2 3 4 5 6 7 8 9; do
|
||||
version="$(awk -F= -v key="php${slot}_release" '$1 == key {print $2}' "${CB_DIR}/options.conf" 2>/dev/null | tr -d '[:space:]')"
|
||||
case "${version,,}" in
|
||||
""|no|off) continue ;;
|
||||
esac
|
||||
php_slots+=("$version")
|
||||
done
|
||||
|
||||
[ ${#php_slots[@]} -gt 0 ] || {
|
||||
echo "Nie wykryto aktywnych wersji PHP w CustomBuild." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
for version in "${php_slots[@]}"; do
|
||||
echo "Rebuild PHP ${version}"
|
||||
"${CB_DIR}/build" php "$version"
|
||||
done
|
||||
|
||||
"${CB_DIR}/build" rewrite_confs || true
|
||||
echo "Rekompilacja PHP zakończona. Rozszerzenia mysqli i pdo_mysql są dostarczane przez standardowy build DirectAdmin."
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=./mysql_common.sh
|
||||
source "$SCRIPT_DIR/mysql_common.sh"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Użycie:
|
||||
restore_all.sh <katalog_backupu> [--restore-users]
|
||||
|
||||
Opis:
|
||||
Przywraca wszystkie bazy MySQL z katalogu backupu. Jeśli obecny jest plik
|
||||
`_globals.sql` lub `_globals.sql.gz`, można go odtworzyć flagą
|
||||
`--restore-users`.
|
||||
EOF
|
||||
}
|
||||
|
||||
BACKUP_DIR=""
|
||||
RESTORE_USERS="0"
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--help|-h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--restore-users)
|
||||
RESTORE_USERS="1"
|
||||
;;
|
||||
*)
|
||||
if [ -n "$BACKUP_DIR" ]; then
|
||||
echo "Podano więcej niż jeden katalog backupu." >&2
|
||||
exit 1
|
||||
fi
|
||||
BACKUP_DIR="$arg"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ -n "$BACKUP_DIR" ] || {
|
||||
usage
|
||||
exit 1
|
||||
}
|
||||
[ -d "$BACKUP_DIR" ] || {
|
||||
echo "Katalog nie istnieje: $BACKUP_DIR" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
mysql_load_alt_credentials
|
||||
|
||||
if [ "$RESTORE_USERS" = "1" ]; then
|
||||
for globals_file in "$BACKUP_DIR/_globals.sql.gz" "$BACKUP_DIR/_globals.sql"; do
|
||||
[ -f "$globals_file" ] || continue
|
||||
mysql_restore_stream mysql "$globals_file"
|
||||
break
|
||||
done
|
||||
fi
|
||||
|
||||
find "$BACKUP_DIR" -maxdepth 1 -type f \( -name '*.sql' -o -name '*.sql.gz' \) ! -name '_globals.*' | sort | while IFS= read -r file; do
|
||||
[ -n "$file" ] || continue
|
||||
db_name="$(basename "$file" | sed -E 's/\.sql(\.gz)?$//')"
|
||||
db_name="$(mysql_safe_identifier "$db_name")"
|
||||
mysql_exec mysql -e "DROP DATABASE IF EXISTS $(mysql_quote_identifier "$db_name");"
|
||||
mysql_exec mysql -e "CREATE DATABASE $(mysql_quote_identifier "$db_name") CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
|
||||
mysql_restore_stream "$db_name" "$file"
|
||||
echo "Przywrócono ${db_name}"
|
||||
done
|
||||
@@ -0,0 +1,186 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
ROUNDCUBE_PLUGIN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
ROUNDCUBE_SETTINGS_FILE="${ROUNDCUBE_SETTINGS_FILE:-$ROUNDCUBE_PLUGIN_DIR/plugin-settings.conf}"
|
||||
ROUNDCUBE_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# shellcheck source=./mysql_common.sh
|
||||
source "$ROUNDCUBE_SCRIPT_DIR/mysql_common.sh"
|
||||
mysql_load_plugin_settings_file "$ROUNDCUBE_SETTINGS_FILE"
|
||||
|
||||
roundcube_read_setting() {
|
||||
local key="$1"
|
||||
local default="$2"
|
||||
local line value
|
||||
[ -r "$ROUNDCUBE_SETTINGS_FILE" ] || {
|
||||
printf '%s\n' "$default"
|
||||
return 0
|
||||
}
|
||||
line="$(grep -E "^${key}=" "$ROUNDCUBE_SETTINGS_FILE" | tail -n 1 || true)"
|
||||
[ -n "$line" ] || {
|
||||
printf '%s\n' "$default"
|
||||
return 0
|
||||
}
|
||||
value="${line#*=}"
|
||||
value="${value%%#*}"
|
||||
value="${value%\"}"
|
||||
value="${value#\"}"
|
||||
value="${value%\'}"
|
||||
value="${value#\'}"
|
||||
value="$(printf '%s' "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
|
||||
printf '%s\n' "${value:-$default}"
|
||||
}
|
||||
|
||||
roundcube_enabled() {
|
||||
case "$(roundcube_read_setting ROUNDCUBE_ENABLED true)" in
|
||||
true|yes|on|1) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
roundcube_config_file() {
|
||||
roundcube_read_setting ROUNDCUBE_CONFIG_FILE /var/www/html/roundcube/config/config.inc.php
|
||||
}
|
||||
|
||||
roundcube_custom_config_file() {
|
||||
roundcube_read_setting ROUNDCUBE_CUSTOM_CONFIG_FILE /usr/local/directadmin/custombuild/custom/roundcube/config.inc.php
|
||||
}
|
||||
|
||||
roundcube_write_custombuild_config() {
|
||||
case "$(roundcube_read_setting ROUNDCUBE_WRITE_CUSTOMBUILD_CONFIG true)" in
|
||||
true|yes|on|1) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
roundcube_db_name() {
|
||||
roundcube_read_setting ROUNDCUBE_DB_NAME roundcube
|
||||
}
|
||||
|
||||
roundcube_db_user() {
|
||||
roundcube_read_setting ROUNDCUBE_DB_USER roundcube
|
||||
}
|
||||
|
||||
roundcube_password_file() {
|
||||
roundcube_read_setting ROUNDCUBE_DB_PASSWORD_FILE /usr/local/directadmin/conf/alt-roundcube.conf
|
||||
}
|
||||
|
||||
roundcube_native_my_cnf() {
|
||||
roundcube_read_setting ROUNDCUBE_NATIVE_MY_CNF /usr/local/directadmin/conf/my.cnf
|
||||
}
|
||||
|
||||
roundcube_endpoint_mode() {
|
||||
roundcube_read_setting ROUNDCUBE_ALT_DSN_MODE tcp
|
||||
}
|
||||
|
||||
roundcube_generate_secret() {
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
openssl rand -base64 36 | tr -d '\n'
|
||||
return 0
|
||||
fi
|
||||
tr -dc 'A-Za-z0-9_@%+=' </dev/urandom | head -c 48
|
||||
}
|
||||
|
||||
roundcube_load_or_create_password() {
|
||||
local file user pass group_name tmp
|
||||
file="$(roundcube_password_file)"
|
||||
user="$(roundcube_db_user)"
|
||||
|
||||
if [ -r "$file" ]; then
|
||||
pass="$(awk -F= '$1 == "password" || $1 == "passwd" { sub(/^[^=]*=/, ""); print; exit }' "$file")"
|
||||
if [ -n "$pass" ]; then
|
||||
printf '%s\n' "$pass"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
pass="$(roundcube_generate_secret)"
|
||||
group_name=""
|
||||
if command -v getent >/dev/null 2>&1 && getent group diradmin >/dev/null 2>&1; then
|
||||
group_name="diradmin"
|
||||
elif command -v getent >/dev/null 2>&1 && getent group root >/dev/null 2>&1; then
|
||||
group_name="root"
|
||||
fi
|
||||
tmp="$(mktemp)"
|
||||
{
|
||||
printf 'user=%s\n' "$user"
|
||||
printf 'password=%s\n' "$pass"
|
||||
} > "$tmp"
|
||||
if [ "${EUID:-$(id -u)}" -eq 0 ] && [ -n "$group_name" ]; then
|
||||
install -m 0640 -o root -g "$group_name" "$tmp" "$file"
|
||||
else
|
||||
install -m 0600 "$tmp" "$file"
|
||||
fi
|
||||
rm -f "$tmp"
|
||||
printf '%s\n' "$pass"
|
||||
}
|
||||
|
||||
roundcube_load_password() {
|
||||
local file pass
|
||||
file="$(roundcube_password_file)"
|
||||
[ -r "$file" ] || return 1
|
||||
pass="$(awk -F= '$1 == "password" || $1 == "passwd" { sub(/^[^=]*=/, ""); print; exit }' "$file")"
|
||||
[ -n "$pass" ] || return 1
|
||||
printf '%s\n' "$pass"
|
||||
}
|
||||
|
||||
roundcube_urlencode() {
|
||||
php -r 'echo rawurlencode($argv[1]);' "$1"
|
||||
}
|
||||
|
||||
roundcube_alt_dsn() {
|
||||
local db user pass host port mode encoded_user encoded_pass
|
||||
mysql_load_alt_runtime
|
||||
db="$(roundcube_db_name)"
|
||||
user="$(roundcube_db_user)"
|
||||
pass="$(roundcube_load_or_create_password)"
|
||||
mode="$(roundcube_endpoint_mode)"
|
||||
encoded_user="$(roundcube_urlencode "$user")"
|
||||
encoded_pass="$(roundcube_urlencode "$pass")"
|
||||
|
||||
case "$mode" in
|
||||
socket)
|
||||
printf 'mysql://%s:%s@unix(%s)/%s\n' "$encoded_user" "$encoded_pass" "$MYSQL_ALT_SOCKET" "$db"
|
||||
;;
|
||||
*)
|
||||
host="127.0.0.1"
|
||||
port="$MYSQL_ALT_PORT"
|
||||
printf 'mysql://%s:%s@%s:%s/%s\n' "$encoded_user" "$encoded_pass" "$host" "$port" "$db"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
roundcube_sql_quote() {
|
||||
local value="$1"
|
||||
value="${value//\\/\\\\}"
|
||||
value="${value//\'/\'\'}"
|
||||
printf "'%s'" "$value"
|
||||
}
|
||||
|
||||
roundcube_create_alt_database_and_user() {
|
||||
local db user pass q_user q_pass
|
||||
mysql_load_alt_credentials
|
||||
mysql_ensure_metadata_schema
|
||||
db="$(roundcube_db_name)"
|
||||
user="$(roundcube_db_user)"
|
||||
pass="$(roundcube_load_or_create_password)"
|
||||
q_user="$(roundcube_sql_quote "$user")"
|
||||
q_pass="$(roundcube_sql_quote "$pass")"
|
||||
|
||||
mysql_safe_identifier "$db" >/dev/null
|
||||
mysql_safe_identifier "$user" >/dev/null
|
||||
|
||||
mysql_exec mysql -e "CREATE DATABASE IF NOT EXISTS $(mysql_quote_identifier "$db") CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
|
||||
mysql_exec mysql -e "CREATE USER IF NOT EXISTS ${q_user}@'localhost' IDENTIFIED BY ${q_pass};"
|
||||
mysql_exec mysql -e "ALTER USER ${q_user}@'localhost' IDENTIFIED BY ${q_pass};"
|
||||
mysql_exec mysql -e "CREATE USER IF NOT EXISTS ${q_user}@'127.0.0.1' IDENTIFIED BY ${q_pass};"
|
||||
mysql_exec mysql -e "ALTER USER ${q_user}@'127.0.0.1' IDENTIFIED BY ${q_pass};"
|
||||
mysql_exec mysql -e "GRANT ALL PRIVILEGES ON $(mysql_quote_identifier "$db").* TO ${q_user}@'localhost';"
|
||||
mysql_exec mysql -e "GRANT ALL PRIVILEGES ON $(mysql_quote_identifier "$db").* TO ${q_user}@'127.0.0.1';"
|
||||
mysql_exec mysql -e "
|
||||
INSERT INTO da_plugin_system_databases (service_name, db_name, db_user, endpoint_mode, config_path, enabled)
|
||||
VALUES ('roundcube', '$(mysql_escape_literal "$db")', '$(mysql_escape_literal "$user")', '$(mysql_escape_literal "$(roundcube_endpoint_mode)")', '$(mysql_escape_literal "$(roundcube_config_file)")', 1)
|
||||
ON DUPLICATE KEY UPDATE db_name=VALUES(db_name), db_user=VALUES(db_user), endpoint_mode=VALUES(endpoint_mode), config_path=VALUES(config_path), enabled=VALUES(enabled), updated_at=CURRENT_TIMESTAMP;
|
||||
"
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=./roundcube_common.sh
|
||||
source "$SCRIPT_DIR/roundcube_common.sh"
|
||||
|
||||
fail() {
|
||||
echo "roundcube alt-mysql health: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
roundcube_enabled || {
|
||||
echo "roundcube alt-mysql health: disabled"
|
||||
exit 0
|
||||
}
|
||||
|
||||
CONFIG_FILE="$(roundcube_config_file)"
|
||||
DB_NAME="$(roundcube_db_name)"
|
||||
DB_USER="$(roundcube_db_user)"
|
||||
PASS="$(roundcube_load_password)" || fail "Roundcube password file is missing or invalid: $(roundcube_password_file)"
|
||||
mysql_load_alt_credentials
|
||||
mysql_load_alt_runtime
|
||||
|
||||
[ -r "$CONFIG_FILE" ] || fail "config file is not readable: $CONFIG_FILE"
|
||||
grep -Fq "BEGIN DIRECTADMIN MYSQL PLUGIN ROUNDCUBE" "$CONFIG_FILE" || fail "managed Roundcube DB block is missing"
|
||||
grep -Fq "$DB_NAME" "$CONFIG_FILE" || fail "Roundcube config does not reference expected database"
|
||||
if grep -Fq "/var/lib/mysql/mysql.sock" "$CONFIG_FILE"; then
|
||||
fail "Roundcube config references native DirectAdmin socket"
|
||||
fi
|
||||
if grep -Fq "/usr/local/directadmin/conf/mysql.conf" "$CONFIG_FILE"; then
|
||||
fail "Roundcube config references native DirectAdmin mysql.conf"
|
||||
fi
|
||||
|
||||
MYSQL_PWD="$PASS" "$(mysql_alt_binary mysql)" --protocol=TCP --host=127.0.0.1 --port="$MYSQL_ALT_PORT" --user="$DB_USER" --database="$DB_NAME" -e "SELECT 1" >/dev/null \
|
||||
|| fail "cannot connect to Roundcube database on alt-mariadb as $DB_USER"
|
||||
|
||||
echo "roundcube alt-mysql health: OK"
|
||||
@@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=./roundcube_common.sh
|
||||
source "$SCRIPT_DIR/roundcube_common.sh"
|
||||
|
||||
FORCE="${FORCE:-0}"
|
||||
SKIP_NATIVE_DUMP="${SKIP_NATIVE_DUMP:-0}"
|
||||
LOG_FILE="${LOG_FILE:-$ROUNDCUBE_PLUGIN_DIR/data/logs/roundcube-migrate.log}"
|
||||
mkdir -p "$(dirname "$LOG_FILE")"
|
||||
|
||||
log() {
|
||||
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
die() {
|
||||
log "ERROR: $*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
roundcube_enabled || die "Roundcube integration is disabled by ROUNDCUBE_ENABLED."
|
||||
|
||||
DB_NAME="$(roundcube_db_name)"
|
||||
NATIVE_MY_CNF="$(roundcube_native_my_cnf)"
|
||||
TMP_DUMP="$(mktemp)"
|
||||
trap 'rm -f "$TMP_DUMP"' EXIT
|
||||
|
||||
roundcube_create_alt_database_and_user
|
||||
|
||||
if [ "$SKIP_NATIVE_DUMP" != "1" ]; then
|
||||
[ -r "$NATIVE_MY_CNF" ] || die "Native DirectAdmin MySQL config not readable: $NATIVE_MY_CNF"
|
||||
NATIVE_MYSQL_BIN="$(mysql_native_binary mysql)" || die "native mysql/mariadb client not found"
|
||||
NATIVE_MYSQLDUMP_BIN="$(mysql_native_binary mysqldump)" || die "native mysqldump/mariadb-dump client not found"
|
||||
|
||||
if ! "$NATIVE_MYSQL_BIN" --defaults-extra-file="$NATIVE_MY_CNF" --batch --skip-column-names information_schema \
|
||||
-e "SELECT 1 FROM SCHEMATA WHERE SCHEMA_NAME='$(mysql_escape_literal "$DB_NAME")' LIMIT 1" | grep -q 1; then
|
||||
if [ "$FORCE" != "1" ]; then
|
||||
die "Native Roundcube database $DB_NAME not found. Use SKIP_NATIVE_DUMP=1 if this is a new Roundcube install."
|
||||
fi
|
||||
log "Native Roundcube database $DB_NAME not found; continuing because FORCE=1."
|
||||
else
|
||||
log "Dumping native Roundcube database: $DB_NAME"
|
||||
"$NATIVE_MYSQLDUMP_BIN" --defaults-extra-file="$NATIVE_MY_CNF" \
|
||||
--single-transaction \
|
||||
--quick \
|
||||
--skip-lock-tables \
|
||||
--routines \
|
||||
--triggers \
|
||||
--events \
|
||||
--default-character-set=utf8mb4 \
|
||||
"$DB_NAME" > "$TMP_DUMP" 2>>"$LOG_FILE" || die "native Roundcube dump failed"
|
||||
|
||||
log "Importing Roundcube database into alt-mariadb: $DB_NAME"
|
||||
mysql_exec mysql -e "DROP DATABASE IF EXISTS $(mysql_quote_identifier "$DB_NAME");"
|
||||
mysql_exec mysql -e "CREATE DATABASE $(mysql_quote_identifier "$DB_NAME") CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
|
||||
mysql_exec "$DB_NAME" < "$TMP_DUMP" >> "$LOG_FILE" 2>&1 || die "Roundcube import into alt-mariadb failed"
|
||||
roundcube_create_alt_database_and_user
|
||||
fi
|
||||
fi
|
||||
|
||||
"$SCRIPT_DIR/roundcube_repair_config.sh"
|
||||
"$SCRIPT_DIR/roundcube_health_check.sh"
|
||||
|
||||
log "Roundcube migration to alt-mariadb completed."
|
||||
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=./roundcube_common.sh
|
||||
source "$SCRIPT_DIR/roundcube_common.sh"
|
||||
|
||||
rewrite_roundcube_config() {
|
||||
local source_file="$1"
|
||||
local target_file="$2"
|
||||
local dsn="$3"
|
||||
local tmp parent
|
||||
|
||||
[ -f "$source_file" ] || {
|
||||
echo "roundcube: source config file not found: $source_file" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
if [ -e "$target_file" ]; then
|
||||
[ -w "$target_file" ] || {
|
||||
echo "roundcube: config file is not writable: $target_file" >&2
|
||||
return 1
|
||||
}
|
||||
else
|
||||
parent="$(dirname "$target_file")"
|
||||
mkdir -p "$parent"
|
||||
fi
|
||||
|
||||
tmp="$(mktemp)"
|
||||
awk '
|
||||
/BEGIN DIRECTADMIN MYSQL PLUGIN ROUNDCUBE/ { skip=1; next }
|
||||
/END DIRECTADMIN MYSQL PLUGIN ROUNDCUBE/ { skip=0; next }
|
||||
skip != 1 { print }
|
||||
' "$source_file" > "$tmp"
|
||||
|
||||
cat >> "$tmp" <<PHP
|
||||
|
||||
// BEGIN DIRECTADMIN MYSQL PLUGIN ROUNDCUBE
|
||||
\$config['db_dsnw'] = '$dsn';
|
||||
// END DIRECTADMIN MYSQL PLUGIN ROUNDCUBE
|
||||
PHP
|
||||
|
||||
if [ -e "$target_file" ]; then
|
||||
cat "$tmp" > "$target_file"
|
||||
else
|
||||
install -m 0644 "$tmp" "$target_file"
|
||||
fi
|
||||
rm -f "$tmp"
|
||||
}
|
||||
|
||||
roundcube_enabled || {
|
||||
echo "roundcube: integration disabled by ROUNDCUBE_ENABLED."
|
||||
exit 0
|
||||
}
|
||||
|
||||
CONFIG_FILE="$(roundcube_config_file)"
|
||||
[ -f "$CONFIG_FILE" ] || {
|
||||
echo "roundcube: config file not found: $CONFIG_FILE" >&2
|
||||
exit 1
|
||||
}
|
||||
[ -w "$CONFIG_FILE" ] || {
|
||||
echo "roundcube: config file is not writable: $CONFIG_FILE" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
DSN="$(roundcube_alt_dsn)"
|
||||
rewrite_roundcube_config "$CONFIG_FILE" "$CONFIG_FILE" "$DSN"
|
||||
|
||||
echo "roundcube: config repaired: $CONFIG_FILE"
|
||||
|
||||
if roundcube_write_custombuild_config; then
|
||||
CUSTOM_CONFIG_FILE="$(roundcube_custom_config_file)"
|
||||
if [ -f "$CUSTOM_CONFIG_FILE" ]; then
|
||||
rewrite_roundcube_config "$CUSTOM_CONFIG_FILE" "$CUSTOM_CONFIG_FILE" "$DSN"
|
||||
else
|
||||
rewrite_roundcube_config "$CONFIG_FILE" "$CUSTOM_CONFIG_FILE" "$DSN"
|
||||
fi
|
||||
echo "roundcube: CustomBuild config repaired: $CUSTOM_CONFIG_FILE"
|
||||
fi
|
||||
Reference in New Issue
Block a user