Files
nginx-ui/internal/nginx/nginx.go
2025-10-03 14:33:07 +08:00

160 lines
3.2 KiB
Go

package nginx
import (
"os"
"strconv"
"strings"
"sync"
"time"
"math"
"github.com/0xJacky/Nginx-UI/internal/docker"
"github.com/0xJacky/Nginx-UI/settings"
"github.com/shirou/gopsutil/v4/process"
"github.com/uozi-tech/cosy/logger"
)
var (
mutex sync.Mutex
lastStdOut string
lastStdErr error
)
// TestConfig tests the nginx config
func TestConfig() (stdOut string, stdErr error) {
mutex.Lock()
defer mutex.Unlock()
if settings.NginxSettings.TestConfigCmd != "" {
return execShell(settings.NginxSettings.TestConfigCmd)
}
sbin := GetSbinPath()
if sbin == "" {
return execCommand("nginx", "-t")
}
return execCommand(sbin, "-t")
}
// Reload reloads the nginx
func Reload() (stdOut string, stdErr error) {
mutex.Lock()
defer mutex.Unlock()
// Clear the modules cache when reloading Nginx
clearModulesCache()
if !IsRunning() {
restart()
return
}
if settings.NginxSettings.ReloadCmd != "" {
return execShell(settings.NginxSettings.ReloadCmd)
}
sbin := GetSbinPath()
if sbin == "" {
return execCommand("nginx", "-s", "reload")
}
return execCommand(sbin, "-s", "reload")
}
func restart() {
// fix(docker): nginx restart always output network error
time.Sleep(500 * time.Millisecond)
if settings.NginxSettings.RestartCmd != "" {
lastStdOut, lastStdErr = execShell(settings.NginxSettings.RestartCmd)
return
}
pidPath := GetPIDPath()
daemon := GetSbinPath()
// Check if nginx is running before attempting to stop it
if IsRunning() {
lastStdOut, lastStdErr = execCommand("start-stop-daemon", "--stop", "--quiet", "--oknodo", "--retry=TERM/30/KILL/5", "--pidfile", pidPath)
if lastStdErr != nil {
return
}
}
if daemon == "" {
lastStdOut, lastStdErr = execCommand("nginx")
return
}
lastStdOut, lastStdErr = execCommand("start-stop-daemon", "--start", "--quiet", "--pidfile", pidPath, "--exec", daemon)
}
// Restart restarts the nginx
func Restart() {
mutex.Lock()
defer mutex.Unlock()
// Clear the modules cache when restarting Nginx
clearModulesCache()
restart()
}
// GetLastResult returns the last output of the nginx command
func GetLastResult() *ControlResult {
mutex.Lock()
defer mutex.Unlock()
return &ControlResult{
stdOut: lastStdOut,
stdErr: lastStdErr,
}
}
func IsRunning() bool {
pidPath := GetPIDPath()
switch settings.NginxSettings.RunningInAnotherContainer() {
case true:
return docker.StatPath(pidPath)
case false:
return isProcessRunning(pidPath)
}
return false
}
// isProcessRunning checks if the process with the PID from pidPath is actually running
func isProcessRunning(pidPath string) bool {
logger.Debugf("isProcessRunning pidPath: %s", pidPath)
// Check if PID file exists
if fileInfo, err := os.Stat(pidPath); err != nil || fileInfo.Size() == 0 {
return false
}
// Read PID from file
pidBytes, err := os.ReadFile(pidPath)
if err != nil {
return false
}
pidStr := strings.TrimSpace(string(pidBytes))
pid, err := strconv.Atoi(pidStr)
if err != nil {
return false
}
// Check that pid is within int32 and positive bounds
if pid <= 0 || pid > math.MaxInt32 {
return false
}
// Use gopsutil for cross-platform process existence check
exists, err := process.PidExists(int32(pid))
if err != nil {
return false
}
if exists {
return true
}
return false
}