RKNnoVPN/runtime/internal/modulehelper/modulehelper.go
loop-uh 2ccfe8ce0a
Some checks failed
RKNnoVPN Linux and Android CI / policy-and-go (push) Successful in 14s
RKNnoVPN Linux and Android CI / android-debug (push) Successful in 2m5s
Full build and Forgejo release / Privacy Lint (push) Successful in 3s
Full build and Forgejo release / Local-first Runtime Guardrail (push) Successful in 2s
Full build and Forgejo release / Go Tests (push) Successful in 13s
Full build and Forgejo release / Resolve sing-box release (push) Successful in 1s
Full build and Forgejo release / Resolve Xray-core release (push) Successful in 1s
Full build and Forgejo release / Android Guardrails & Tests (push) Failing after 16s
Full build and Forgejo release / Build APK (push) Has been skipped
Full build and Forgejo release / Build Runtime CLI (arm64) (push) Successful in 11s
Full build and Forgejo release / Build Runtime CLI (armv7) (push) Successful in 11s
Full build and Forgejo release / Build sing-box (arm64) (push) Failing after 2s
Full build and Forgejo release / Build sing-box (armv7) (push) Failing after 2s
Full build and Forgejo release / Build Xray-core (arm64) (push) Failing after 3s
Full build and Forgejo release / Build Xray-core (armv7) (push) Failing after 2s
Full build and Forgejo release / Build Magisk Module (push) Has been skipped
Full build and Forgejo release / Create Release (push) Has been skipped
Перенести выпуск RKNnoVPN на Forgejo
2026-08-07 03:08:36 +03:00

123 lines
3 KiB
Go

package modulehelper
import (
"errors"
"os"
"path/filepath"
"strconv"
"strings"
"syscall"
"git.zapret.moe/zapretdiscordyoutube/RKNnoVPN/runtime/internal/modulecontract"
)
const (
StatusActive = "active"
StatusPendingUpdate = "pending_update"
StatusPendingRemove = "pending_remove"
StatusDisabled = "disabled"
StatusMissing = "missing"
StatusServiceMissing = "service_missing"
StatusRuntimeMissing = "runtime_missing"
)
type State struct {
Status string `json:"status"`
ModuleDir string `json:"moduleDir"`
Message string `json:"message,omitempty"`
}
func CurrentState(moduleDir string) State {
paths := modulecontract.NewPaths(moduleDir)
state := State{
Status: StatusActive,
ModuleDir: paths.Dir(),
}
if hasPendingUpdate(paths.Dir()) {
state.Status = StatusPendingUpdate
state.Message = "module update is staged; reboot required"
return state
}
if exists(filepath.Join(paths.Dir(), "remove")) {
state.Status = StatusPendingRemove
state.Message = "module is marked for removal; reboot required"
return state
}
if exists(filepath.Join(paths.Dir(), "disable")) {
state.Status = StatusDisabled
state.Message = "module is disabled"
return state
}
if !exists(paths.Dir()) {
state.Status = StatusMissing
state.Message = "module directory is missing"
return state
}
if !isExecutable(filepath.Join(paths.Dir(), "service.sh")) {
state.Status = StatusServiceMissing
state.Message = "service.sh is missing or not executable"
return state
}
if !isExecutable(filepath.Join(paths.BinDir(), "rknnovpn-runtime")) {
state.Status = StatusRuntimeMissing
state.Message = "rknnovpn-runtime is missing or not executable"
return state
}
return state
}
func hasPendingUpdate(moduleDir string) bool {
for _, base := range []string{"/data/adb/modules_update", "/data/adb/ksu/modules_update", "/data/adb/ap/modules_update"} {
if exists(filepath.Join(base, filepath.Base(moduleDir))) {
return true
}
}
return false
}
func exists(path string) bool {
_, err := os.Stat(path)
return err == nil
}
func isExecutable(path string) bool {
info, err := os.Stat(path)
return err == nil && !info.IsDir() && info.Mode()&0111 != 0
}
func readPID(path string) int {
data, err := os.ReadFile(path)
if err != nil {
return 0
}
pid, err := strconv.Atoi(strings.TrimSpace(string(data)))
if err != nil || pid <= 0 {
return 0
}
return pid
}
func processExists(pid int) bool {
if pid <= 0 {
return false
}
err := syscall.Kill(pid, 0)
return err == nil || errors.Is(err, syscall.EPERM)
}
func processMatchesPath(pid int, wanted string) bool {
if !processExists(pid) {
return false
}
if exe, err := os.Readlink(filepath.Join("/proc", strconv.Itoa(pid), "exe")); err == nil {
if exe == wanted || filepath.Clean(exe) == filepath.Clean(wanted) {
return true
}
}
cmdline, err := os.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "cmdline"))
if err != nil {
return false
}
return strings.Contains(strings.ReplaceAll(string(cmdline), "\x00", " "), wanted)
}