Skip to content

Commit b4b4a8a

Browse files
committed
fix: module loading regex #1108
1 parent b31365f commit b4b4a8a

File tree

2 files changed

+422
-11
lines changed

2 files changed

+422
-11
lines changed

internal/nginx/modules.go

Lines changed: 103 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package nginx
22

33
import (
4+
"fmt"
45
"os"
56
"regexp"
67
"strings"
@@ -97,26 +98,114 @@ func updateDynamicModulesStatus() {
9798
return
9899
}
99100

100-
// Regular expression to find loaded dynamic modules in nginx -T output
101-
// Look for lines like "load_module modules/ngx_http_image_filter_module.so;"
102-
loadModuleRe := regexp.MustCompile(`load_module\s+(?:modules/|/.*/)([a-zA-Z0-9_-]+)\.so;`)
101+
// Use the shared regex function to find loaded dynamic modules
102+
loadModuleRe := GetLoadModuleRegex()
103103
matches := loadModuleRe.FindAllStringSubmatch(out, -1)
104104

105105
for _, match := range matches {
106106
if len(match) > 1 {
107-
// Extract the module name without path and suffix
108-
moduleName := match[1]
109-
// Some normalization to match format in GetModules
110-
moduleName = strings.TrimPrefix(moduleName, "ngx_")
111-
moduleName = strings.TrimSuffix(moduleName, "_module")
112-
module, ok := modulesCache.Get(moduleName)
107+
// Extract the module name from load_module statement and normalize it
108+
loadModuleName := match[1]
109+
normalizedName := normalizeModuleNameFromLoadModule(loadModuleName)
110+
111+
// Try to find the module in our cache using the normalized name
112+
module, ok := modulesCache.Get(normalizedName)
113113
if ok {
114114
module.Loaded = true
115115
}
116116
}
117117
}
118118
}
119119

120+
// GetLoadModuleRegex returns a compiled regular expression to match nginx load_module statements.
121+
// It matches both quoted and unquoted module paths:
122+
// - load_module "/usr/local/nginx/modules/ngx_stream_module.so";
123+
// - load_module modules/ngx_http_upstream_fair_module.so;
124+
//
125+
// The regex captures the module name (without path and extension).
126+
func GetLoadModuleRegex() *regexp.Regexp {
127+
// Pattern explanation:
128+
// load_module\s+ - matches "load_module" followed by whitespace
129+
// "? - optional opening quote
130+
// (?:[^"\s]+/)? - non-capturing group for optional path (any non-quote, non-space chars ending with /)
131+
// ([a-zA-Z0-9_-]+) - capturing group for module name
132+
// \.so - matches ".so" extension
133+
// "? - optional closing quote
134+
// \s*; - optional whitespace followed by semicolon
135+
return regexp.MustCompile(`load_module\s+"?(?:[^"\s]+/)?([a-zA-Z0-9_-]+)\.so"?\s*;`)
136+
}
137+
138+
// normalizeModuleNameFromLoadModule converts a module name from load_module statement
139+
// to match the format used in configure arguments.
140+
// Examples:
141+
// - "ngx_stream_module" -> "stream"
142+
// - "ngx_http_geoip_module" -> "http_geoip"
143+
// - "ngx_stream_geoip_module" -> "stream_geoip"
144+
// - "ngx_http_image_filter_module" -> "http_image_filter"
145+
func normalizeModuleNameFromLoadModule(moduleName string) string {
146+
// Remove "ngx_" prefix if present
147+
normalized := strings.TrimPrefix(moduleName, "ngx_")
148+
149+
// Remove "_module" suffix if present
150+
normalized = strings.TrimSuffix(normalized, "_module")
151+
152+
return normalized
153+
}
154+
155+
// normalizeModuleNameFromConfigure converts a module name from configure arguments
156+
// to a consistent format for internal use.
157+
// Examples:
158+
// - "stream" -> "stream"
159+
// - "http_geoip_module" -> "http_geoip"
160+
// - "http_image_filter_module" -> "http_image_filter"
161+
func normalizeModuleNameFromConfigure(moduleName string) string {
162+
// Remove "_module" suffix if present to keep consistent format
163+
normalized := strings.TrimSuffix(moduleName, "_module")
164+
165+
return normalized
166+
}
167+
168+
// getExpectedLoadModuleName converts a configure argument module name
169+
// to the expected load_module statement module name.
170+
// Examples:
171+
// - "stream" -> "ngx_stream_module"
172+
// - "http_geoip" -> "ngx_http_geoip_module"
173+
// - "stream_geoip" -> "ngx_stream_geoip_module"
174+
func getExpectedLoadModuleName(configureModuleName string) string {
175+
normalized := normalizeModuleNameFromConfigure(configureModuleName)
176+
return "ngx_" + normalized + "_module"
177+
}
178+
179+
// GetModuleMapping returns a map showing the relationship between different module name formats.
180+
// This is useful for debugging and understanding how module names are processed.
181+
// Returns a map with normalized names as keys and mapping info as values.
182+
func GetModuleMapping() map[string]map[string]string {
183+
modules := GetModules()
184+
mapping := make(map[string]map[string]string)
185+
186+
modulesCacheLock.RLock()
187+
defer modulesCacheLock.RUnlock()
188+
189+
// Use AllFromFront() to iterate through the ordered map
190+
for normalizedName, module := range modules.AllFromFront() {
191+
if module == nil {
192+
continue
193+
}
194+
195+
expectedLoadName := getExpectedLoadModuleName(normalizedName)
196+
197+
mapping[normalizedName] = map[string]string{
198+
"normalized": normalizedName,
199+
"expected_load_module": expectedLoadName,
200+
"dynamic": fmt.Sprintf("%t", module.Dynamic),
201+
"loaded": fmt.Sprintf("%t", module.Loaded),
202+
"params": module.Params,
203+
}
204+
}
205+
206+
return mapping
207+
}
208+
120209
func GetModules() *orderedmap.OrderedMap[string, *Module] {
121210
modulesCacheLock.RLock()
122211
cachedModules := modulesCache
@@ -165,6 +254,9 @@ func GetModules() *orderedmap.OrderedMap[string, *Module] {
165254
continue
166255
}
167256

257+
// Normalize the module name for consistent internal representation
258+
normalizedModuleName := normalizeModuleNameFromConfigure(module)
259+
168260
// Determine if the module is dynamic
169261
isDynamic := false
170262
if strings.Contains(out, "--with-"+module+"=dynamic") ||
@@ -176,8 +268,8 @@ func GetModules() *orderedmap.OrderedMap[string, *Module] {
176268
params = ""
177269
}
178270

179-
modulesCache.Set(module, &Module{
180-
Name: module,
271+
modulesCache.Set(normalizedModuleName, &Module{
272+
Name: normalizedModuleName,
181273
Params: params,
182274
Dynamic: isDynamic,
183275
Loaded: !isDynamic, // Static modules are always loaded

0 commit comments

Comments
 (0)