#!/usr/bin/env bash # install-service.sh # Создаёт systemd-сервис для запуска Minecraft сервера (через ./start.sh) # и сразу его поднимает. Должен быть запущен от root. # # Использование: # sudo ./install-service.sh # sudo ./install-service.sh --name minecraft --user minecraft --dir /opt/minecraft # sudo ./install-service.sh --uninstall # # Флаги: # --name <имя> Имя systemd-сервиса (по умолчанию: minecraft) # --user От какого пользователя запускать сервер # (по умолчанию: minecraft, будет создан если нет) # --dir <путь> Каталог сервера (по умолчанию: каталог этого скрипта) # --java-args <строка> Дополнительные JVM-аргументы (напр. "-Xms4G -Xmx8G") # --admin-user Доп. пользователь, который тоже должен иметь # запись в каталог (для git pull и т.п.). Будет # добавлен в группу сервиса, на каталог встанут # g+rwX и setgid, чтобы новые файлы наследовали # группу. Можно указать несколько раз. # --uninstall Остановить, выключить и удалить сервис # -h | --help Показать справку set -euo pipefail SERVICE_NAME="minecraft" SERVICE_USER="minecraft" SERVER_DIR="$(cd "$(dirname "$0")" && pwd)" JAVA_ARGS="" UNINSTALL=0 ADMIN_USERS=() print_help() { sed -n '2,27p' "$0" | sed 's/^# \{0,1\}//' } err() { echo "Ошибка: $*" >&2 exit 1 } while [[ $# -gt 0 ]]; do case "$1" in --name) SERVICE_NAME="$2"; shift 2 ;; --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)" ;; esac done # 1. Проверка прав root if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then err "скрипт должен быть запущен от root. Используйте: sudo $0 $*" fi # 2. Проверка systemd if ! command -v systemctl >/dev/null 2>&1; then err "systemctl не найден. Этот скрипт работает только на системах с systemd." fi UNIT_FILE="/etc/systemd/system/${SERVICE_NAME}.service" # Удаление сервиса if [[ "${UNINSTALL}" -eq 1 ]]; then echo ">> Останавливаю и удаляю сервис ${SERVICE_NAME}..." systemctl stop "${SERVICE_NAME}.service" 2>/dev/null || true systemctl disable "${SERVICE_NAME}.service" 2>/dev/null || true if [[ -f "${UNIT_FILE}" ]]; then rm -f "${UNIT_FILE}" echo " Удалён ${UNIT_FILE}" fi systemctl daemon-reload systemctl reset-failed "${SERVICE_NAME}.service" 2>/dev/null || true echo ">> Готово. Сервис ${SERVICE_NAME} удалён." exit 0 fi # 3. Проверка содержимого каталога сервера [[ -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/. 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 </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. ==================================================================== 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 # 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 ввода нет) EULA_FILE="${SERVER_DIR}/eula.txt" if [[ ! -f "${EULA_FILE}" ]] || ! grep -qE '^\s*eula\s*=\s*true' "${EULA_FILE}"; then cat >&2 </dev/null || true else PROTECT_HOME_LINE="ProtectHome=read-only" fi echo ">> Создаю ${UNIT_FILE}..." cat > "${UNIT_FILE}" <> Перечитываю systemd, включаю и запускаю сервис..." systemctl daemon-reload systemctl enable "${SERVICE_NAME}.service" systemctl restart "${SERVICE_NAME}.service" sleep 2 echo "" echo "====================================================================" systemctl --no-pager --full status "${SERVICE_NAME}.service" || true echo "====================================================================" echo "" echo "Готово. Полезные команды:" echo " sudo systemctl status ${SERVICE_NAME}" echo " sudo systemctl stop ${SERVICE_NAME}" echo " sudo systemctl restart ${SERVICE_NAME}" echo " sudo journalctl -u ${SERVICE_NAME} -f # живые логи" echo " sudo $0 --uninstall # удалить сервис"