mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-09 00:56:37 +08:00
96a17ae35d
* fix(agent): recover OpenResty runtime after invalid PID * fix(agent): scope OpenResty shutdown to instance processes --------- Co-authored-by: Noru Wyrms <wyrmsnoru@gmail.com>
119 lines
3.9 KiB
Go
119 lines
3.9 KiB
Go
// Copyright 2026 Arctel.net
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package nginx
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
//nolint:unused // Used by the Linux process inspector.
|
|
const procParentPIDIndex = 1
|
|
|
|
//nolint:unused // Used by the Linux process inspector.
|
|
const procStartTimeIndex = 19 // Field 22, counting from state after the process name.
|
|
|
|
//nolint:unused // Used by the Linux executor; portable for process fixture tests.
|
|
func inspectProcRuntime(root string, executable os.FileInfo, binary, configPath string, uid int) ([]runtimeProcess, error) {
|
|
entries, err := os.ReadDir(root)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var processes []runtimeProcess
|
|
for _, entry := range entries {
|
|
pid, err := strconv.Atoi(entry.Name())
|
|
if err != nil || pid <= 0 || !entry.IsDir() {
|
|
continue
|
|
}
|
|
process, found, err := inspectProcProcess(filepath.Join(root, entry.Name()), pid, executable, binary, configPath, uid)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
continue
|
|
}
|
|
return nil, err
|
|
}
|
|
if found {
|
|
processes = append(processes, process)
|
|
}
|
|
}
|
|
return processes, nil
|
|
}
|
|
|
|
//nolint:unused // Used by the Linux process inspector.
|
|
func inspectProcProcess(dir string, pid int, executable os.FileInfo, binary, configPath string, uid int) (runtimeProcess, bool, error) {
|
|
//nolint:gosec // Read a fixed proc entry under a numeric PID directory.
|
|
status, err := os.ReadFile(filepath.Join(dir, "status"))
|
|
if err != nil {
|
|
return runtimeProcess{}, false, err
|
|
}
|
|
owned := effectiveProcessUID(string(status)) == uid
|
|
//nolint:gosec // Read a fixed proc entry under a numeric PID directory.
|
|
command, err := os.ReadFile(filepath.Join(dir, "cmdline"))
|
|
if err != nil {
|
|
return runtimeProcess{}, false, err
|
|
}
|
|
title := strings.ReplaceAll(string(command), "\x00", " ")
|
|
if !strings.HasPrefix(title, "nginx: ") && !strings.HasPrefix(title, "openresty: ") {
|
|
return runtimeProcess{}, false, nil
|
|
}
|
|
master := owned && matchesRuntimeMaster(title, configPath)
|
|
exe, err := os.Stat(filepath.Join(dir, "exe"))
|
|
if err != nil {
|
|
if !os.IsPermission(err) {
|
|
return runtimeProcess{}, false, err
|
|
}
|
|
// Non-master candidates are only retained by runtimeDescendants when
|
|
// their parent belongs to this instance. A capability-enabled non-root
|
|
// master may be non-dumpable, making /proc/PID/exe inaccessible; verify
|
|
// its invocation and UID instead.
|
|
fields := strings.Fields(title)
|
|
if master && (len(fields) < 4 || fields[3] != binary) {
|
|
return runtimeProcess{}, false, fmt.Errorf("cannot verify executable of openresty master %d", pid)
|
|
}
|
|
} else if !os.SameFile(executable, exe) {
|
|
if master {
|
|
return runtimeProcess{}, false, fmt.Errorf("executable changed for openresty master %d; refusing duplicate start", pid)
|
|
}
|
|
return runtimeProcess{}, false, nil
|
|
}
|
|
//nolint:gosec // Read a fixed proc entry under a numeric PID directory.
|
|
stat, err := os.ReadFile(filepath.Join(dir, "stat"))
|
|
if err != nil {
|
|
return runtimeProcess{}, false, err
|
|
}
|
|
closeParen := strings.LastIndexByte(string(stat), ')')
|
|
if closeParen < 0 {
|
|
return runtimeProcess{}, false, fmt.Errorf("invalid stat for process %d", pid)
|
|
}
|
|
fields := strings.Fields(string(stat)[closeParen+1:])
|
|
if len(fields) <= procStartTimeIndex {
|
|
return runtimeProcess{}, false, fmt.Errorf("incomplete stat for process %d", pid)
|
|
}
|
|
if fields[0] == "Z" {
|
|
return runtimeProcess{}, false, nil
|
|
}
|
|
parentPID, err := strconv.Atoi(fields[procParentPIDIndex])
|
|
if err != nil {
|
|
return runtimeProcess{}, false, fmt.Errorf("invalid parent pid for process %d: %w", pid, err)
|
|
}
|
|
return runtimeProcess{PID: pid, ParentPID: parentPID, Master: master, StartTime: fields[procStartTimeIndex]}, true, nil
|
|
}
|
|
|
|
//nolint:unused // Used by the Linux process inspector.
|
|
func effectiveProcessUID(status string) int {
|
|
for _, line := range strings.Split(status, "\n") {
|
|
fields := strings.Fields(line)
|
|
if len(fields) >= 3 && fields[0] == "Uid:" {
|
|
uid, err := strconv.Atoi(fields[2])
|
|
if err == nil {
|
|
return uid
|
|
}
|
|
}
|
|
}
|
|
return -1
|
|
}
|