Browse Source

fix(docker): make x-ui CLI menu work inside containers

check_status() only recognized a systemd service or Alpine's
/etc/init.d/x-ui, neither of which exists in a container where the panel
runs as the foreground main process (PID 1 via "exec /app/x-ui"). Every
CLI command therefore failed with "Please install the panel first", and
restart/restart-xray relied on rc-service/systemctl that aren't present.

Detect the container (/.dockerenv or XUI_IN_DOCKER) and, when inside one:
- resolve the panel binary under /app instead of /usr/local/x-ui
- derive status from the running process instead of a service file
- restart via SIGHUP and restart-xray via SIGUSR1 to the panel process
- show Docker-appropriate guidance for start/stop/enable/disable

The Dockerfile sets XUI_IN_DOCKER/XUI_MAIN_FOLDER so detection is
explicit even though /.dockerenv alone suffices.

Closes #4817
MHSanaei 9 hours ago
parent
commit
f901cd42a5
2 changed files with 113 additions and 3 deletions
  1. 2 2
      Dockerfile
  2. 111 1
      x-ui.sh

+ 2 - 2
Dockerfile

@@ -63,9 +63,9 @@ RUN chmod +x \
   /app/x-ui \
   /app/x-ui \
   /usr/bin/x-ui
   /usr/bin/x-ui
 
 
+ENV XUI_IN_DOCKER="true"
+ENV XUI_MAIN_FOLDER="/app"
 ENV XUI_ENABLE_FAIL2BAN="true"
 ENV XUI_ENABLE_FAIL2BAN="true"
-# Database backend: set XUI_DB_TYPE=postgres and XUI_DB_DSN=postgres://... to use PostgreSQL.
-# Default (unset) is SQLite stored under /etc/x-ui.
 ENV XUI_DB_TYPE=""
 ENV XUI_DB_TYPE=""
 ENV XUI_DB_DSN=""
 ENV XUI_DB_DSN=""
 EXPOSE 2053
 EXPOSE 2053

+ 111 - 1
x-ui.sh

@@ -69,8 +69,17 @@ echo "The OS release is: $release"
 os_version=""
 os_version=""
 os_version=$(grep "^VERSION_ID" /etc/os-release | cut -d '=' -f2 | tr -d '"' | tr -d '.')
 os_version=$(grep "^VERSION_ID" /etc/os-release | cut -d '=' -f2 | tr -d '"' | tr -d '.')
 
 
+running_in_docker="false"
+if [[ -f /.dockerenv ]] || [[ "${XUI_IN_DOCKER}" == "true" ]]; then
+    running_in_docker="true"
+fi
+
 # Declare Variables
 # Declare Variables
-xui_folder="${XUI_MAIN_FOLDER:=/usr/local/x-ui}"
+if [[ "${running_in_docker}" == "true" ]]; then
+    xui_folder="${XUI_MAIN_FOLDER:=/app}"
+else
+    xui_folder="${XUI_MAIN_FOLDER:=/usr/local/x-ui}"
+fi
 xui_service="${XUI_SERVICE:=/etc/systemd/system}"
 xui_service="${XUI_SERVICE:=/etc/systemd/system}"
 log_folder="${XUI_LOG_FOLDER:=/var/log/x-ui}"
 log_folder="${XUI_LOG_FOLDER:=/var/log/x-ui}"
 mkdir -p "${log_folder}"
 mkdir -p "${log_folder}"
@@ -400,6 +409,15 @@ start() {
         echo ""
         echo ""
         LOGI "Panel is running, No need to start again, If you need to restart, please select restart"
         LOGI "Panel is running, No need to start again, If you need to restart, please select restart"
     else
     else
+        if [[ "${running_in_docker}" == "true" ]]; then
+            LOGE "Panel process is not running inside this container."
+            LOGI "In Docker the panel is the container's main process. Restart the container to bring it back up:"
+            LOGI "  docker restart <container_name>"
+            if [[ $# == 0 ]]; then
+                before_show_menu
+            fi
+            return 0
+        fi
         if [[ $release == "alpine" ]]; then
         if [[ $release == "alpine" ]]; then
             rc-service x-ui start
             rc-service x-ui start
         else
         else
@@ -425,6 +443,15 @@ stop() {
         echo ""
         echo ""
         LOGI "Panel stopped, No need to stop again!"
         LOGI "Panel stopped, No need to stop again!"
     else
     else
+        if [[ "${running_in_docker}" == "true" ]]; then
+            LOGI "In Docker the panel runs as the container's main process."
+            LOGI "To stop it, stop the container from the host:"
+            LOGI "  docker stop <container_name>"
+            if [[ $# == 0 ]]; then
+                before_show_menu
+            fi
+            return 0
+        fi
         if [[ $release == "alpine" ]]; then
         if [[ $release == "alpine" ]]; then
             rc-service x-ui stop
             rc-service x-ui stop
         else
         else
@@ -445,6 +472,26 @@ stop() {
 }
 }
 
 
 restart() {
 restart() {
+    if [[ "${running_in_docker}" == "true" ]]; then
+        if signal_xui HUP; then
+            sleep 1
+            signal_xui USR1
+            LOGI "Restart signal sent to the panel and xray-core."
+        else
+            LOGE "Could not find the running panel process to signal."
+        fi
+        sleep 2
+        check_status
+        if [[ $? == 0 ]]; then
+            LOGI "x-ui and xray Restarted successfully"
+        else
+            LOGE "Panel restart failed, Please check the log information later"
+        fi
+        if [[ $# == 0 ]]; then
+            before_show_menu
+        fi
+        return 0
+    fi
     if [[ $release == "alpine" ]]; then
     if [[ $release == "alpine" ]]; then
         rc-service x-ui restart
         rc-service x-ui restart
     else
     else
@@ -463,6 +510,19 @@ restart() {
 }
 }
 
 
 restart_xray() {
 restart_xray() {
+    if [[ "${running_in_docker}" == "true" ]]; then
+        if signal_xui USR1; then
+            LOGI "xray-core Restart signal sent successfully, Please check the log information to confirm whether xray restarted successfully"
+        else
+            LOGE "Could not find the running panel process to signal."
+        fi
+        sleep 2
+        show_xray_status
+        if [[ $# == 0 ]]; then
+            before_show_menu
+        fi
+        return 0
+    fi
     if [[ $release == "alpine" ]]; then
     if [[ $release == "alpine" ]]; then
         rc-service x-ui reload
         rc-service x-ui reload
     else
     else
@@ -477,6 +537,13 @@ restart_xray() {
 }
 }
 
 
 status() {
 status() {
+    if [[ "${running_in_docker}" == "true" ]]; then
+        show_status
+        if [[ $# == 0 ]]; then
+            before_show_menu
+        fi
+        return 0
+    fi
     if [[ $release == "alpine" ]]; then
     if [[ $release == "alpine" ]]; then
         rc-service x-ui status
         rc-service x-ui status
     else
     else
@@ -488,6 +555,14 @@ status() {
 }
 }
 
 
 enable() {
 enable() {
+    if [[ "${running_in_docker}" == "true" ]]; then
+        LOGI "Autostart is controlled by the Docker restart policy (e.g. 'restart: unless-stopped' in docker-compose.yml)."
+        LOGI "There is no service to enable inside the container."
+        if [[ $# == 0 ]]; then
+            before_show_menu
+        fi
+        return 0
+    fi
     if [[ $release == "alpine" ]]; then
     if [[ $release == "alpine" ]]; then
         rc-update add x-ui default
         rc-update add x-ui default
     else
     else
@@ -505,6 +580,14 @@ enable() {
 }
 }
 
 
 disable() {
 disable() {
+    if [[ "${running_in_docker}" == "true" ]]; then
+        LOGI "Autostart is controlled by the Docker restart policy (e.g. 'restart: unless-stopped' in docker-compose.yml)."
+        LOGI "Set 'restart: no' for the container on the host to disable autostart."
+        if [[ $# == 0 ]]; then
+            before_show_menu
+        fi
+        return 0
+    fi
     if [[ $release == "alpine" ]]; then
     if [[ $release == "alpine" ]]; then
         rc-update del x-ui
         rc-update del x-ui
     else
     else
@@ -673,8 +756,31 @@ update_shell() {
     fi
     fi
 }
 }
 
 
+xui_pid() {
+    ps -ef 2> /dev/null | grep -F "${xui_folder}/x-ui" | grep -v grep | awk 'NR==1 {print $1}'
+}
+
+signal_xui() {
+    local sig="$1" pid
+    pid="$(xui_pid)"
+    if [[ -z "${pid}" ]]; then
+        return 1
+    fi
+    kill -"${sig}" "${pid}" 2> /dev/null
+}
+
 # 0: running, 1: not running, 2: not installed
 # 0: running, 1: not running, 2: not installed
 check_status() {
 check_status() {
+    if [[ "${running_in_docker}" == "true" ]]; then
+        if [[ ! -x "${xui_folder}/x-ui" ]]; then
+            return 2
+        fi
+        if [[ -n "$(xui_pid)" ]]; then
+            return 0
+        else
+            return 1
+        fi
+    fi
     if [[ $release == "alpine" ]]; then
     if [[ $release == "alpine" ]]; then
         if [[ ! -f /etc/init.d/x-ui ]]; then
         if [[ ! -f /etc/init.d/x-ui ]]; then
             return 2
             return 2
@@ -761,6 +867,10 @@ show_status() {
 }
 }
 
 
 show_enable_status() {
 show_enable_status() {
+    if [[ "${running_in_docker}" == "true" ]]; then
+        echo -e "Start automatically: ${green}Managed by Docker${plain}"
+        return
+    fi
     check_enabled
     check_enabled
     if [[ $? == 0 ]]; then
     if [[ $? == 0 ]]; then
         echo -e "Start automatically: ${green}Yes${plain}"
         echo -e "Start automatically: ${green}Yes${plain}"