Enhance install-service.sh to determine service user from SUDO_USER, improving usability; update Java memory arguments in variables.txt for better performance; and expand Russian translations in resource packs for improved localization and user experience.

This commit is contained in:
Leonid Pershin
2026-05-08 12:08:02 +03:00
parent 5fe7b72a62
commit 2f81d73f8e
5 changed files with 565 additions and 417 deletions
+57 -118
View File
@@ -1,38 +1,38 @@
#!/usr/bin/env bash
# install-service.sh
# Создаёт systemd-сервис для запуска Minecraft сервера (через ./start.sh)
# и сразу его поднимает. Должен быть запущен от root.
# и сразу его поднимает. Должен быть запущен от root через sudo.
#
# Сервис будет работать от того же пользователя, который вызвал sudo
# (т.е. от $SUDO_USER). Это значит:
# - не нужно создавать отдельного юзера и делать chown;
# - git pull / редактирование конфигов работают как обычно, без sudo;
# - домашний каталог пользователя доступен сервису.
#
# Использование:
# sudo ./install-service.sh
# sudo ./install-service.sh --name minecraft --user minecraft --dir /opt/minecraft
# sudo ./install-service.sh --name minecraft --java-args "-Xms4G -Xmx8G"
# sudo ./install-service.sh --uninstall
#
# Флаги:
# --name <имя> Имя systemd-сервиса (по умолчанию: minecraft)
# --user <user> От какого пользователя запускать сервер
# (по умолчанию: minecraft, будет создан если нет)
# --dir <путь> Каталог сервера (по умолчанию: каталог этого скрипта)
# --java-args <строка> Дополнительные JVM-аргументы (напр. "-Xms4G -Xmx8G")
# --admin-user <user> Доп. пользователь, который тоже должен иметь
# запись в каталог (для git pull и т.п.). Будет
# добавлен в группу сервиса, на каталог встанут
# g+rwX и setgid, чтобы новые файлы наследовали
# группу. Можно указать несколько раз.
# --uninstall Остановить, выключить и удалить сервис
# -h | --help Показать справку
# --name <имя> Имя systemd-сервиса (по умолчанию: minecraft)
# --user <user> Переопределить пользователя для сервиса
# (по умолчанию: $SUDO_USER)
# --dir <путь> Каталог сервера (по умолчанию: каталог скрипта)
# --java-args <строка> Дополнительные JVM-аргументы (напр. "-Xms4G -Xmx8G")
# --uninstall Остановить, выключить и удалить сервис
# -h | --help Показать справку
set -euo pipefail
SERVICE_NAME="minecraft"
SERVICE_USER="minecraft"
SERVICE_USER=""
SERVER_DIR="$(cd "$(dirname "$0")" && pwd)"
JAVA_ARGS=""
UNINSTALL=0
ADMIN_USERS=()
print_help() {
sed -n '2,27p' "$0" | sed 's/^# \{0,1\}//'
sed -n '2,25p' "$0" | sed 's/^# \{0,1\}//'
}
err() {
@@ -46,7 +46,6 @@ while [[ $# -gt 0 ]]; do
--user) SERVICE_USER="$2"; shift 2 ;;
--dir) SERVER_DIR="$2"; shift 2 ;;
--java-args) JAVA_ARGS="$2"; shift 2 ;;
--admin-user) ADMIN_USERS+=("$2"); shift 2 ;;
--uninstall) UNINSTALL=1; shift ;;
-h|--help) print_help; exit 0 ;;
*) err "Неизвестный аргумент: $1 (используйте --help)" ;;
@@ -80,52 +79,46 @@ if [[ "${UNINSTALL}" -eq 1 ]]; then
exit 0
fi
# 3. Проверка содержимого каталога сервера
# 3. Определение пользователя сервиса
if [[ -z "${SERVICE_USER}" ]]; then
if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then
SERVICE_USER="${SUDO_USER}"
else
err "не удалось определить пользователя. Запустите через sudo от обычного юзера, либо укажите --user <имя>."
fi
fi
if ! id -u "${SERVICE_USER}" >/dev/null 2>&1; then
err "пользователь '${SERVICE_USER}' не существует."
fi
SERVICE_GROUP="$(id -gn "${SERVICE_USER}")"
echo ">> Сервис будет работать от: ${SERVICE_USER}:${SERVICE_GROUP}"
# 4. Проверка содержимого каталога сервера
[[ -d "${SERVER_DIR}" ]] || err "каталог сервера не найден: ${SERVER_DIR}"
[[ -f "${SERVER_DIR}/start.sh" ]] || err "не найден ${SERVER_DIR}/start.sh"
# 3b. Предупреждение про /home — типичная причина status=200/CHDIR
# ProtectHome=read-only в unit-файле блокирует доступ к /home,
# плюс домашний каталог обычно имеет права 750 и пользователь
# сервиса не может в него войти. Рекомендуем /opt/<name>.
# 4a. Предупреждение про /home — типичная причина status=200/CHDIR.
# ProtectHome=read-only блокирует доступ к /home. Если сервер
# в /home — ослабим защиту в unit-файле (см. ниже).
if [[ "${SERVER_DIR}" == /home/* ]]; then
PARENT_USER="$(stat -c '%U' "$(dirname "${SERVER_DIR}")" 2>/dev/null || echo "$USER")"
SUGGESTED_DIR="/opt/$(basename "$(dirname "${SERVER_DIR}")")/$(basename "${SERVER_DIR}")"
cat >&2 <<EOF
============================ ВНИМАНИЕ ============================
Каталог сервера находится в /home/:
${SERVER_DIR}
Каталог сервера находится в /home/. Для systemd рекомендуется
переносить такие сервисы в /opt (или /srv): это удобнее с точки
зрения прав и sandbox-настроек.
Это плохое место для systemd-сервиса:
- ProtectHome=read-only в unit-файле блокирует /home;
- дефолтные права на ~ (обычно 750) не дают зайти юзеру minecraft;
- типовая ошибка systemd: status=200/CHDIR.
Рекомендуется перенести сервер в /opt (или /srv) и работать оттуда.
Пример переноса (если вы уже клонировали репозиторий в /home):
sudo systemctl stop ${SERVICE_NAME} 2>/dev/null || true
sudo mkdir -p $(dirname "${SUGGESTED_DIR}")
sudo mv "$(dirname "${SERVER_DIR}")" "$(dirname "${SUGGESTED_DIR}")/"
cd "${SUGGESTED_DIR}"
sudo ./install-service.sh --admin-user ${PARENT_USER}
После этого git pull/push продолжит работать из нового пути,
а сервис уже не будет упираться в ProtectHome.
Скрипт продолжит работу — ProtectHome будет ослаблен в unit-файле.
====================================================================
EOF
read -r -p "Продолжить установку с текущим путём в /home? [y/N] " ans
case "${ans,,}" in
y|yes) echo "Продолжаю..." ;;
*) err "установка отменена. Перенесите каталог в /opt и запустите скрипт снова." ;;
esac
fi
chmod +x "${SERVER_DIR}/start.sh" "${SERVER_DIR}/run.sh" 2>/dev/null || true
# 3c. Проверка Java. start.sh иначе попытается интерактивно поставить
# 4b. Проверка Java. start.sh иначе попытается интерактивно поставить
# Jabba и упадёт в systemd с "User did not agree to Jabba installation".
REQUIRED_JAVA_VERSION="$(grep -E '^RECOMMENDED_JAVA_VERSION=' "${SERVER_DIR}/variables.txt" 2>/dev/null \
| cut -d= -f2 | tr -d '"' | head -n1)"
@@ -135,24 +128,17 @@ if ! command -v java >/dev/null 2>&1; then
cat >&2 <<EOF
============================ ВНИМАНИЕ ============================
В системе не найдена команда 'java'. start.sh попытается поставить
её через Jabba, но Jabba требует интерактивного 'I agree', которого
в systemd нет. Сервис будет бесконечно перезапускаться.
Java не найдена в системе. Сначала установите Java ${REQUIRED_JAVA_VERSION},
затем запустите этот скрипт повторно.
Установите Java ${REQUIRED_JAVA_VERSION} системно перед запуском:
Рекомендуемый способ (системный пакет, виден всем пользователям):
# Debian/Ubuntu (родной пакет, если доступен):
sudo apt update && sudo apt install -y openjdk-${REQUIRED_JAVA_VERSION}-jre-headless
sudo apt update
sudo apt install -y openjdk-${REQUIRED_JAVA_VERSION}-jre-headless
# Альтернатива — Temurin от Adoptium:
sudo apt install -y wget gnupg ca-certificates
wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public \\
| sudo gpg --dearmor -o /usr/share/keyrings/adoptium.gpg
echo "deb [signed-by=/usr/share/keyrings/adoptium.gpg] https://packages.adoptium.net/artifactory/deb \$(. /etc/os-release && echo \$VERSION_CODENAME) main" \\
| sudo tee /etc/apt/sources.list.d/adoptium.list
sudo apt update && sudo apt install -y temurin-${REQUIRED_JAVA_VERSION}-jre
После установки запустите этот скрипт повторно.
Альтернатива — скрипт ./install_java.sh (использует Jabba, ставит
Java в \$HOME пользователя). Запускайте его от того же юзера, под
которым будет работать сервис (${SERVICE_USER}), без sudo.
====================================================================
EOF
@@ -165,71 +151,24 @@ if [[ "${JAVA_MAJOR}" == "1" ]]; then
fi
if [[ "${JAVA_MAJOR}" != "${REQUIRED_JAVA_VERSION}" ]]; then
echo ">> ВНИМАНИЕ: установлена Java ${JAVA_MAJOR}, ожидалась ${REQUIRED_JAVA_VERSION}." >&2
echo " Сервер может не стартовать. Рекомендуется поставить openjdk-${REQUIRED_JAVA_VERSION}-jre-headless." >&2
echo " Сервер может не стартовать. Рекомендуется openjdk-${REQUIRED_JAVA_VERSION}-jre-headless." >&2
fi
# 4. Создание пользователя при необходимости
if ! id -u "${SERVICE_USER}" >/dev/null 2>&1; then
echo ">> Создаю системного пользователя ${SERVICE_USER}..."
useradd --system \
--home-dir "${SERVER_DIR}" \
--shell /usr/sbin/nologin \
"${SERVICE_USER}"
else
echo ">> Пользователь ${SERVICE_USER} уже существует — пропускаю создание."
fi
# 5. Передача прав на каталог сервера
echo ">> Назначаю владельца каталога ${SERVER_DIR} -> ${SERVICE_USER}..."
chown -R "${SERVICE_USER}:${SERVICE_USER}" "${SERVER_DIR}"
# 5b. Доп. пользователи (для git pull / редактирования конфигов без sudo).
# Включаем их в группу сервиса, на каталог ставим g+rwX и setgid,
# чтобы новые файлы автоматически создавались с этой группой.
if [[ ${#ADMIN_USERS[@]} -gt 0 ]]; then
echo ">> Настраиваю групповой доступ для: ${ADMIN_USERS[*]}"
for au in "${ADMIN_USERS[@]}"; do
if ! id -u "${au}" >/dev/null 2>&1; then
echo " ! пользователь ${au} не существует — пропускаю"
continue
fi
usermod -aG "${SERVICE_USER}" "${au}"
echo " + ${au} добавлен в группу ${SERVICE_USER}"
# Чтобы git не ругался на 'dubious ownership' под этим юзером
if command -v sudo >/dev/null 2>&1; then
sudo -u "${au}" git config --global --add safe.directory "${SERVER_DIR}" \
2>/dev/null || true
fi
done
# g+rwX на всё содержимое + setgid на каталоги
chmod -R g+rwX "${SERVER_DIR}"
find "${SERVER_DIR}" -type d -exec chmod g+s {} +
echo " Установлены g+rwX и setgid на ${SERVER_DIR}"
echo " ВАЖНО: после добавления в группу нужно перелогиниться,"
echo " либо выполнить 'newgrp ${SERVICE_USER}' в текущей сессии."
fi
# 6. Проверка EULA — без неё сервер встанет ожидая ввод (а в systemd ввода нет)
# 5. Проверка EULA
EULA_FILE="${SERVER_DIR}/eula.txt"
if [[ ! -f "${EULA_FILE}" ]] || ! grep -qE '^\s*eula\s*=\s*true' "${EULA_FILE}"; then
cat >&2 <<EOF
Внимание: ${EULA_FILE} отсутствует или eula=false.
В systemd нет интерактивного stdin, поэтому сервер не сможет спросить согласие.
Подтвердите EULA вручную перед стартом сервиса:
echo 'eula=true' | sudo tee ${EULA_FILE}
echo 'eula=true' > ${EULA_FILE}
Скрипт продолжит и создаст unit, но сервис стартанёт только после принятия EULA.
EOF
fi
# 7. Генерация systemd unit-файла
# Если каталог в /home, ProtectHome нужно ослабить, иначе systemd
# не сможет даже сделать chdir в WorkingDirectory.
# 6. Генерация systemd unit-файла
if [[ "${SERVER_DIR}" == /home/* ]]; then
PROTECT_HOME_LINE="ProtectHome=false # ослаблено, т.к. сервер в /home"
# Дополнительно открываем доступ юзеру minecraft на проход через ~
chmod o+x "$(dirname "${SERVER_DIR}")" 2>/dev/null || true
PROTECT_HOME_LINE="ProtectHome=false"
else
PROTECT_HOME_LINE="ProtectHome=read-only"
fi
@@ -244,7 +183,7 @@ Wants=network-online.target
[Service]
Type=simple
User=${SERVICE_USER}
Group=${SERVICE_USER}
Group=${SERVICE_GROUP}
WorkingDirectory=${SERVER_DIR}
Environment=WAIT_FOR_USER_INPUT=false
$( [[ -n "${JAVA_ARGS}" ]] && echo "Environment=JAVA_ARGS=${JAVA_ARGS}" )
@@ -269,7 +208,7 @@ EOF
chmod 644 "${UNIT_FILE}"
# 8. Активация сервиса
# 7. Активация сервиса
echo ">> Перечитываю systemd, включаю и запускаю сервис..."
systemctl daemon-reload
systemctl enable "${SERVICE_NAME}.service"
+1 -1
View File
@@ -76,7 +76,7 @@ QUILT_INSTALLER_VERSION=0.12.1
RECOMMENDED_JAVA_VERSION=21
WAIT_FOR_USER_INPUT=true
JAVA="java"
JAVA_ARGS="-Xmx4G -Xms4G"
JAVA_ARGS="-Xmx13G -Xms8G"
ADDITIONAL_ARGS="-Dlog4j2.formatMsgNoLookups=true"
SSJ_FORGE_ARGS="-Djava.security.manager=allow"
RESTART=true