package net
import (
)
const (
nssConfigPath = "/etc/nsswitch.conf"
)
var nssConfig nsswitchConfig
type nsswitchConfig struct {
initOnce sync.Once
ch chan struct{}
lastChecked time.Time
mu sync.Mutex
nssConf *nssConf
}
func () *nssConf {
nssConfig.tryUpdate()
nssConfig.mu.Lock()
:= nssConfig.nssConf
nssConfig.mu.Unlock()
return
}
func ( *nsswitchConfig) () {
.nssConf = parseNSSConfFile("/etc/nsswitch.conf")
.lastChecked = time.Now()
.ch = make(chan struct{}, 1)
}
func ( *nsswitchConfig) () {
.initOnce.Do(.init)
if !.tryAcquireSema() {
return
}
defer .releaseSema()
:= time.Now()
if .lastChecked.After(.Add(-5 * time.Second)) {
return
}
.lastChecked =
var time.Time
if , := os.Stat(nssConfigPath); == nil {
= .ModTime()
}
if .Equal(.nssConf.mtime) {
return
}
:= parseNSSConfFile(nssConfigPath)
.mu.Lock()
.nssConf =
.mu.Unlock()
}
func ( *nsswitchConfig) () {
.ch <- struct{}{}
}
func ( *nsswitchConfig) () bool {
select {
case .ch <- struct{}{}:
return true
default:
return false
}
}
func ( *nsswitchConfig) () {
<-.ch
}
type nssConf struct {
mtime time.Time
err error
sources map[string][]nssSource
}
type nssSource struct {
source string
criteria []nssCriterion
}
func ( nssSource) () bool {
for , := range .criteria {
if !.standardStatusAction( == len(.criteria)-1) {
return false
}
}
return true
}
type nssCriterion struct {
negate bool
status string
action string
}
func ( nssCriterion) ( bool) bool {
if .negate {
return false
}
var string
switch .status {
case "success":
= "return"
case "notfound", "unavail", "tryagain":
= "continue"
default:
return false
}
if && .action == "return" {
return true
}
return .action ==
}
func ( string) *nssConf {
, := open()
if != nil {
return &nssConf{err: }
}
defer .close()
, , := .stat()
if != nil {
return &nssConf{err: }
}
:= parseNSSConf()
.mtime =
return
}
func ( *file) *nssConf {
:= new(nssConf)
for , := .readLine(); ; , = .readLine() {
= trimSpace(removeComment())
if len() == 0 {
continue
}
:= bytealg.IndexByteString(, ':')
if == -1 {
.err = errors.New("no colon on line")
return
}
:= trimSpace([:])
:= [+1:]
for {
= trimSpace()
if len() == 0 {
break
}
:= bytealg.IndexByteString(, ' ')
var string
if == -1 {
=
= ""
} else {
= [:]
= trimSpace([+1:])
}
var []nssCriterion
if len() > 0 && [0] == '[' {
:= bytealg.IndexByteString(, ']')
if == -1 {
.err = errors.New("unclosed criterion bracket")
return
}
var error
, = parseCriteria([1:])
if != nil {
.err = errors.New("invalid criteria: " + [1:])
return
}
= [+1:]
}
if .sources == nil {
.sources = make(map[string][]nssSource)
}
.sources[] = append(.sources[], nssSource{
source: ,
criteria: ,
})
}
}
return
}
func ( string) ( []nssCriterion, error) {
= foreachField(, func( string) error {
:= false
if len() > 0 && [0] == '!' {
= true
= [1:]
}
if len() < 3 {
return errors.New("criterion too short")
}
:= bytealg.IndexByteString(, '=')
if == -1 {
return errors.New("criterion lacks equal sign")
}
if hasUpperCase() {
:= []byte()
lowerASCIIBytes()
= string()
}
= append(, nssCriterion{
negate: ,
status: [:],
action: [+1:],
})
return nil
})
return
}