mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2026-05-31 16:39:51 +08:00
- Added regex pattern for parsing the pid directive in nginx configurations. - Introduced `getPIDPathFromNginxT` function to extract the pid file path, handling both absolute and relative paths. - Enhanced `GetPIDPath` function to prioritize user settings, compile-time defaults, and runtime overrides, ensuring robust path resolution. - Added unit tests for PID regex parsing to validate various scenarios, including standard, indented, and commented directives. This update improves the handling of pid paths, particularly for nginx-unprivileged setups, and ensures accurate logging and configuration management.
186 lines
4.7 KiB
Go
186 lines
4.7 KiB
Go
package nginx
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/uozi-tech/cosy/logger"
|
|
)
|
|
|
|
// Regular expressions for parsing directives from nginx -T output
|
|
const (
|
|
// AccessLogRegexPattern matches access_log directive with unquoted path
|
|
// Matches: access_log /path/to/file
|
|
AccessLogRegexPattern = `(?m)^\s*access_log\s+([^\s;]+)`
|
|
|
|
// ErrorLogRegexPattern matches error_log directive with unquoted path
|
|
// Matches: error_log /path/to/file
|
|
ErrorLogRegexPattern = `(?m)^\s*error_log\s+([^\s;]+)`
|
|
|
|
// PIDRegexPattern matches pid directive with unquoted path
|
|
// Matches: pid /path/to/file;
|
|
PIDRegexPattern = `(?m)^\s*pid\s+([^\s;]+)`
|
|
)
|
|
|
|
var (
|
|
accessLogRegex *regexp.Regexp
|
|
errorLogRegex *regexp.Regexp
|
|
pidRegex *regexp.Regexp
|
|
)
|
|
|
|
func init() {
|
|
accessLogRegex = regexp.MustCompile(AccessLogRegexPattern)
|
|
errorLogRegex = regexp.MustCompile(ErrorLogRegexPattern)
|
|
pidRegex = regexp.MustCompile(PIDRegexPattern)
|
|
}
|
|
|
|
// isValidRegularFile checks if the given path is a valid regular file
|
|
// Returns true if the path exists and is a regular file (not a directory or special file)
|
|
func isValidRegularFile(path string) bool {
|
|
if path == "" {
|
|
return false
|
|
}
|
|
|
|
fileInfo, err := os.Stat(path)
|
|
if err != nil {
|
|
logger.Debug("nginx.isValidRegularFile: failed to stat file", "path", path, "error", err)
|
|
return false
|
|
}
|
|
|
|
// Check if it's a regular file (not a directory or special file)
|
|
if !fileInfo.Mode().IsRegular() {
|
|
logger.Debug("nginx.isValidRegularFile: path is not a regular file", "path", path, "mode", fileInfo.Mode())
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// isCommentedLine checks if a line is commented (starts with #)
|
|
func isCommentedLine(line string) bool {
|
|
trimmed := strings.TrimSpace(line)
|
|
return strings.HasPrefix(trimmed, "#")
|
|
}
|
|
|
|
// getAccessLogPathFromNginxT extracts the first access_log path from nginx -T output
|
|
func getAccessLogPathFromNginxT() string {
|
|
output := getNginxT()
|
|
if output == "" {
|
|
logger.Error("nginx.getAccessLogPathFromNginxT: nginx -T output is empty")
|
|
return ""
|
|
}
|
|
|
|
lines := strings.Split(output, "\n")
|
|
|
|
for _, line := range lines {
|
|
// Skip commented lines
|
|
if isCommentedLine(line) {
|
|
continue
|
|
}
|
|
|
|
matches := accessLogRegex.FindStringSubmatch(line)
|
|
if len(matches) >= 2 {
|
|
logPath := matches[1]
|
|
|
|
// Skip 'off' directive
|
|
if logPath == "off" {
|
|
continue
|
|
}
|
|
// Handle relative paths
|
|
if !filepath.IsAbs(logPath) {
|
|
logPath = filepath.Join(GetPrefix(), logPath)
|
|
}
|
|
resolvedPath := resolvePath(logPath)
|
|
|
|
// Validate that the path is a regular file
|
|
if !isValidRegularFile(resolvedPath) {
|
|
logger.Warn("nginx.getAccessLogPathFromNginxT: path is not a valid regular file", "path", resolvedPath)
|
|
continue
|
|
}
|
|
|
|
return resolvedPath
|
|
}
|
|
}
|
|
|
|
logger.Error("nginx.getAccessLogPathFromNginxT: no valid access_log file found")
|
|
return ""
|
|
}
|
|
|
|
// getErrorLogPathFromNginxT extracts the first error_log path from nginx -T output
|
|
func getErrorLogPathFromNginxT() string {
|
|
output := getNginxT()
|
|
if output == "" {
|
|
logger.Error("nginx.getErrorLogPathFromNginxT: nginx -T output is empty")
|
|
return ""
|
|
}
|
|
|
|
lines := strings.Split(output, "\n")
|
|
|
|
for _, line := range lines {
|
|
// Skip commented lines
|
|
if isCommentedLine(line) {
|
|
continue
|
|
}
|
|
|
|
matches := errorLogRegex.FindStringSubmatch(line)
|
|
if len(matches) >= 2 {
|
|
logPath := matches[1]
|
|
|
|
// Handle relative paths
|
|
if !filepath.IsAbs(logPath) {
|
|
logPath = filepath.Join(GetPrefix(), logPath)
|
|
}
|
|
resolvedPath := resolvePath(logPath)
|
|
|
|
// Validate that the path is a regular file
|
|
if !isValidRegularFile(resolvedPath) {
|
|
logger.Warn("nginx.getErrorLogPathFromNginxT: path is not a valid regular file", "path", resolvedPath)
|
|
continue
|
|
}
|
|
|
|
return resolvedPath
|
|
}
|
|
}
|
|
|
|
logger.Error("nginx.getErrorLogPathFromNginxT: no valid error_log file found")
|
|
return ""
|
|
}
|
|
|
|
// getPIDPathFromNginxT extracts the pid file path from nginx -T output.
|
|
// This is needed because nginx -V returns the compile-time default --pid-path,
|
|
// but Docker images like nginx-unprivileged override this at runtime via the
|
|
// "pid" directive in nginx.conf (e.g., pid /tmp/nginx.pid;).
|
|
func getPIDPathFromNginxT() string {
|
|
output := getNginxT()
|
|
if output == "" {
|
|
logger.Error("nginx.getPIDPathFromNginxT: nginx -T output is empty")
|
|
return ""
|
|
}
|
|
|
|
lines := strings.Split(output, "\n")
|
|
|
|
for _, line := range lines {
|
|
// Skip commented lines
|
|
if isCommentedLine(line) {
|
|
continue
|
|
}
|
|
|
|
matches := pidRegex.FindStringSubmatch(line)
|
|
if len(matches) >= 2 {
|
|
pidPath := matches[1]
|
|
|
|
// Handle relative paths
|
|
if !filepath.IsAbs(pidPath) {
|
|
pidPath = filepath.Join(GetPrefix(), pidPath)
|
|
}
|
|
|
|
return resolvePath(pidPath)
|
|
}
|
|
}
|
|
|
|
logger.Debug("nginx.getPIDPathFromNginxT: no pid directive found in nginx -T output")
|
|
return ""
|
|
}
|