package app import ( "fmt" "strconv" "strings" "time" ) func appMarksDel(target string, cgroup string) error { target = strings.ToLower(strings.TrimSpace(target)) if target != "vpn" && target != "direct" { return fmt.Errorf("invalid target") } cgroup = strings.TrimSpace(cgroup) if cgroup == "" { return fmt.Errorf("empty cgroup") } appMarksMu.Lock() defer appMarksMu.Unlock() st := loadAppMarksState() changed := pruneExpiredAppMarksLocked(&st, time.Now().UTC()) var id uint64 var cgAbs string if isAllDigits(cgroup) { v, err := strconv.ParseUint(cgroup, 10, 64) if err == nil { id = v } } else { rel := normalizeCgroupRelOnly(cgroup) if rel != "" { cgAbs = "/" + rel // Try to resolve inode id if directory still exists. if inode, err := cgroupDirInode(rel); err == nil { id = inode } } } // Fallback to state lookup by cgroup string. idx := -1 for i, it := range st.Items { if strings.ToLower(strings.TrimSpace(it.Target)) != target { continue } if id != 0 && it.ID == id { idx = i break } if id == 0 && cgAbs != "" && strings.TrimSpace(it.Cgroup) == cgAbs { id = it.ID idx = i break } } if id != 0 { _ = nftDeleteAppMarkRule(target, id) } if idx >= 0 { st.Items = append(st.Items[:idx], st.Items[idx+1:]...) changed = true } if changed { return saveAppMarksState(st) } return nil }