<commit_before>package bird

import (
	"os/exec"
	"strings"
	"sync"
	"time"
)

var ClientConf BirdConfig
var StatusConf StatusConfig
var RateLimitConf struct {
	sync.Mutex
	Conf RateLimitConfig
}

var Cache = struct {
	sync.RWMutex
	m map[string]Parsed
}{m: make(map[string]Parsed)}

func fromCache(key string) (Parsed, bool) {
	Cache.RLock()
	val, ok := Cache.m[key]
	Cache.RUnlock()
	return val, ok
}

func toCache(key string, val Parsed) {
	val["ttl"] = time.Now().Add(5 * time.Minute)
	Cache.Lock()
	Cache.m[key] = val
	Cache.Unlock()
}

func Run(args string) ([]byte, error) {
	args = "show " + args
	argsList := strings.Split(args, " ")
	return exec.Command(ClientConf.BirdCmd, argsList...).Output()
}

func InstallRateLimitReset() {
	go func() {
		c := time.Tick(time.Second)

		for _ = range c {
			RateLimitConf.Lock()
			RateLimitConf.Conf.Reqs = 0
			RateLimitConf.Unlock()
		}
	}()
}

func checkRateLimit() bool {
	if !RateLimitConf.Conf.Enabled {
		return true
	}

	if RateLimitConf.Conf.Reqs > RateLimitConf.Conf.Max {
		return false
	}

	RateLimitConf.Lock()
	RateLimitConf.Conf.Reqs += 1
	RateLimitConf.Unlock()

	return true
}

func RunAndParse(cmd string, parser func([]byte) Parsed) (Parsed, bool) {
	if val, ok := fromCache(cmd); ok {
		return val, true
	}

	if !checkRateLimit() {
		return nil, false
	}

	out, err := Run(cmd)

	if err != nil {
		// ignore errors for now
		return Parsed{}, false
	}

	parsed := parser(out)
	toCache(cmd, parsed)
	return parsed, false
}

func Status() (Parsed, bool) {
	birdStatus, ok := RunAndParse("status", parseStatus)
	status := birdStatus["status"].(Parsed)

	// Last Reconfig Timestamp source:
	var lastReconfig string
	switch StatusConf.ReconfigTimestampSource {
	case "bird":
		lastReconfig = status["last_reconfig"].(string)
		break
	case "config_modified":
		lastReconfig = lastReconfigTimestampFromFileStat(
			ClientConf.ConfigFilename,
		)
	case "config_regex":
		lastReconfig = lastReconfigTimestampFromFileContent(
			ClientConf.ConfigFilename,
			StatusConf.ReconfigTimestampMatch,
		)
	}

	status["last_reconfig"] = lastReconfig

	// Filter fields
	for _, field := range StatusConf.FilterFields {
		status[field] = nil
	}

	birdStatus["status"] = status

	return birdStatus, ok
}

func Protocols() (Parsed, bool) {
	return RunAndParse("protocols all", parseProtocols)
}

func ProtocolsBgp() (Parsed, bool) {
	p, from_cache := Protocols()
	protocols := p["protocols"].([]string)

	bgpProto := Parsed{}

	for _, v := range protocols {
		if strings.Contains(v, " BGP ") {
			key := strings.Split(v, " ")[0]
			bgpProto[key] = parseBgp(v)
		}
	}

	return Parsed{"protocols": bgpProto}, from_cache
}

func Symbols() (Parsed, bool) {
	return RunAndParse("symbols", parseSymbols)
}

func RoutesPrefixed(prefix string) (Parsed, bool) {
	return RunAndParse("route all '"+prefix+"'", parseRoutes)
}

func RoutesProto(protocol string) (Parsed, bool) {
	return RunAndParse("route protocol '"+protocol+"' all",
		parseRoutes)
}

func RoutesProtoCount(protocol string) (Parsed, bool) {
	return RunAndParse("route protocol '"+protocol+"' count",
		parseRoutesCount)
}

func RoutesFiltered(protocol string) (Parsed, bool) {
	return RunAndParse("route protocol '"+protocol+"' filtered", parseRoutes)
}

func RoutesExport(protocol string) (Parsed, bool) {
	return RunAndParse("route export '"+protocol+"' all",
		parseRoutes)
}

func RoutesExportCount(protocol string) (Parsed, bool) {
	return RunAndParse("route export '"+protocol+"' count",
		parseRoutesCount)
}

func RoutesTable(table string) (Parsed, bool) {
	return RunAndParse("route table '"+table+"' all",
		parseRoutes)
}

func RoutesTableCount(table string) (Parsed, bool) {
	return RunAndParse("route table '"+table+"' count",
		parseRoutesCount)
}

func RoutesLookupTable(net string, table string) (Parsed, bool) {
	return RunAndParse("route for '"+net+"' table '"+table+"' all",
		parseRoutes)
}

func RoutesLookupProtocol(net string, protocol string) (Parsed, bool) {
	return RunAndParse("route for '"+net+"' protocol '"+protocol+"' all",
		parseRoutes)
}
<commit_msg>lock and unlock mutex correctly<commit_after>package bird

import (
	"os/exec"
	"strings"
	"sync"
	"time"
)

var ClientConf BirdConfig
var StatusConf StatusConfig
var RateLimitConf struct {
	sync.RWMutex
	Conf RateLimitConfig
}

var Cache = struct {
	sync.RWMutex
	m map[string]Parsed
}{m: make(map[string]Parsed)}

func fromCache(key string) (Parsed, bool) {
	Cache.RLock()
	val, ok := Cache.m[key]
	Cache.RUnlock()
	return val, ok
}

func toCache(key string, val Parsed) {
	val["ttl"] = time.Now().Add(5 * time.Minute)
	Cache.Lock()
	Cache.m[key] = val
	Cache.Unlock()
}

func Run(args string) ([]byte, error) {
	args = "show " + args
	argsList := strings.Split(args, " ")
	return exec.Command(ClientConf.BirdCmd, argsList...).Output()
}

func InstallRateLimitReset() {
	go func() {
		c := time.Tick(time.Second)

		for _ = range c {
			RateLimitConf.Lock()
			RateLimitConf.Conf.Reqs = 0
			RateLimitConf.Unlock()
		}
	}()
}

func checkRateLimit() bool {
	RateLimitConf.RLock()
	check := !RateLimitConf.Conf.Enabled
	RateLimitConf.RUnlock()
	if check {
		return true
	}

	RateLimitConf.RLock()
	check = RateLimitConf.Conf.Reqs > RateLimitConf.Conf.Max
	RateLimitConf.RUnlock()
	if check {
		return false
	}

	RateLimitConf.Lock()
	RateLimitConf.Conf.Reqs += 1
	RateLimitConf.Unlock()

	return true
}

func RunAndParse(cmd string, parser func([]byte) Parsed) (Parsed, bool) {
	if val, ok := fromCache(cmd); ok {
		return val, true
	}

	if !checkRateLimit() {
		return nil, false
	}

	out, err := Run(cmd)

	if err != nil {
		// ignore errors for now
		return Parsed{}, false
	}

	parsed := parser(out)
	toCache(cmd, parsed)
	return parsed, false
}

func Status() (Parsed, bool) {
	birdStatus, ok := RunAndParse("status", parseStatus)
	status := birdStatus["status"].(Parsed)

	// Last Reconfig Timestamp source:
	var lastReconfig string
	switch StatusConf.ReconfigTimestampSource {
	case "bird":
		lastReconfig = status["last_reconfig"].(string)
		break
	case "config_modified":
		lastReconfig = lastReconfigTimestampFromFileStat(
			ClientConf.ConfigFilename,
		)
	case "config_regex":
		lastReconfig = lastReconfigTimestampFromFileContent(
			ClientConf.ConfigFilename,
			StatusConf.ReconfigTimestampMatch,
		)
	}

	status["last_reconfig"] = lastReconfig

	// Filter fields
	for _, field := range StatusConf.FilterFields {
		status[field] = nil
	}

	birdStatus["status"] = status

	return birdStatus, ok
}

func Protocols() (Parsed, bool) {
	return RunAndParse("protocols all", parseProtocols)
}

func ProtocolsBgp() (Parsed, bool) {
	p, from_cache := Protocols()
	protocols := p["protocols"].([]string)

	bgpProto := Parsed{}

	for _, v := range protocols {
		if strings.Contains(v, " BGP ") {
			key := strings.Split(v, " ")[0]
			bgpProto[key] = parseBgp(v)
		}
	}

	return Parsed{"protocols": bgpProto}, from_cache
}

func Symbols() (Parsed, bool) {
	return RunAndParse("symbols", parseSymbols)
}

func RoutesPrefixed(prefix string) (Parsed, bool) {
	return RunAndParse("route all '"+prefix+"'", parseRoutes)
}

func RoutesProto(protocol string) (Parsed, bool) {
	return RunAndParse("route protocol '"+protocol+"' all",
		parseRoutes)
}

func RoutesProtoCount(protocol string) (Parsed, bool) {
	return RunAndParse("route protocol '"+protocol+"' count",
		parseRoutesCount)
}

func RoutesFiltered(protocol string) (Parsed, bool) {
	return RunAndParse("route protocol '"+protocol+"' filtered", parseRoutes)
}

func RoutesExport(protocol string) (Parsed, bool) {
	return RunAndParse("route export '"+protocol+"' all",
		parseRoutes)
}

func RoutesExportCount(protocol string) (Parsed, bool) {
	return RunAndParse("route export '"+protocol+"' count",
		parseRoutesCount)
}

func RoutesTable(table string) (Parsed, bool) {
	return RunAndParse("route table '"+table+"' all",
		parseRoutes)
}

func RoutesTableCount(table string) (Parsed, bool) {
	return RunAndParse("route table '"+table+"' count",
		parseRoutesCount)
}

func RoutesLookupTable(net string, table string) (Parsed, bool) {
	return RunAndParse("route for '"+net+"' table '"+table+"' all",
		parseRoutes)
}

func RoutesLookupProtocol(net string, protocol string) (Parsed, bool) {
	return RunAndParse("route for '"+net+"' protocol '"+protocol+"' all",
		parseRoutes)
}

{'fraction_non_alphanumeric': 0.13952649921884389, 'fraction_numerical': 0.0009614229059007331, 'mean_word_length': 4.864693446088795, 'pattern_counts': {'":': 8, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4279622', 'n_tokens_mistral': 2995, 'n_tokens_neox': 2883, 'n_words': 704}