package net
import (
"errors"
"internal/bytealg"
"os"
"sync"
"time"
)
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 getSystemNSS() *nssConf {
nssConfig .tryUpdate ()
nssConfig .mu .Lock ()
conf := nssConfig .nssConf
nssConfig .mu .Unlock ()
return conf
}
func (conf *nsswitchConfig ) init () {
conf .nssConf = parseNSSConfFile ("/etc/nsswitch.conf" )
conf .lastChecked = time .Now ()
conf .ch = make (chan struct {}, 1 )
}
func (conf *nsswitchConfig ) tryUpdate () {
conf .initOnce .Do (conf .init )
if !conf .tryAcquireSema () {
return
}
defer conf .releaseSema ()
now := time .Now ()
if conf .lastChecked .After (now .Add (-5 * time .Second )) {
return
}
conf .lastChecked = now
var mtime time .Time
if fi , err := os .Stat (nssConfigPath ); err == nil {
mtime = fi .ModTime ()
}
if mtime .Equal (conf .nssConf .mtime ) {
return
}
nssConf := parseNSSConfFile (nssConfigPath )
conf .mu .Lock ()
conf .nssConf = nssConf
conf .mu .Unlock ()
}
func (conf *nsswitchConfig ) acquireSema () {
conf .ch <- struct {}{}
}
func (conf *nsswitchConfig ) tryAcquireSema () bool {
select {
case conf .ch <- struct {}{}:
return true
default :
return false
}
}
func (conf *nsswitchConfig ) releaseSema () {
<-conf .ch
}
type nssConf struct {
mtime time .Time
err error
sources map [string ][]nssSource
}
type nssSource struct {
source string
criteria []nssCriterion
}
func (s nssSource ) standardCriteria () bool {
for i , crit := range s .criteria {
if !crit .standardStatusAction (i == len (s .criteria )-1 ) {
return false
}
}
return true
}
type nssCriterion struct {
negate bool
status string
action string
}
func (c nssCriterion ) standardStatusAction (last bool ) bool {
if c .negate {
return false
}
var def string
switch c .status {
case "success" :
def = "return"
case "notfound" , "unavail" , "tryagain" :
def = "continue"
default :
return false
}
if last && c .action == "return" {
return true
}
return c .action == def
}
func parseNSSConfFile(file string ) *nssConf {
f , err := open (file )
if err != nil {
return &nssConf {err : err }
}
defer f .close ()
mtime , _ , err := f .stat ()
if err != nil {
return &nssConf {err : err }
}
conf := parseNSSConf (f )
conf .mtime = mtime
return conf
}
func parseNSSConf(f *file ) *nssConf {
conf := new (nssConf )
for line , ok := f .readLine (); ok ; line , ok = f .readLine () {
line = trimSpace (removeComment (line ))
if len (line ) == 0 {
continue
}
colon := bytealg .IndexByteString (line , ':' )
if colon == -1 {
conf .err = errors .New ("no colon on line" )
return conf
}
db := trimSpace (line [:colon ])
srcs := line [colon +1 :]
for {
srcs = trimSpace (srcs )
if len (srcs ) == 0 {
break
}
sp := bytealg .IndexByteString (srcs , ' ' )
var src string
if sp == -1 {
src = srcs
srcs = ""
} else {
src = srcs [:sp ]
srcs = trimSpace (srcs [sp +1 :])
}
var criteria []nssCriterion
if len (srcs ) > 0 && srcs [0 ] == '[' {
bclose := bytealg .IndexByteString (srcs , ']' )
if bclose == -1 {
conf .err = errors .New ("unclosed criterion bracket" )
return conf
}
var err error
criteria , err = parseCriteria (srcs [1 :bclose ])
if err != nil {
conf .err = errors .New ("invalid criteria: " + srcs [1 :bclose ])
return conf
}
srcs = srcs [bclose +1 :]
}
if conf .sources == nil {
conf .sources = make (map [string ][]nssSource )
}
conf .sources [db ] = append (conf .sources [db ], nssSource {
source : src ,
criteria : criteria ,
})
}
}
return conf
}
func parseCriteria(x string ) (c []nssCriterion , err error ) {
err = foreachField (x , func (f string ) error {
not := false
if len (f ) > 0 && f [0 ] == '!' {
not = true
f = f [1 :]
}
if len (f ) < 3 {
return errors .New ("criterion too short" )
}
eq := bytealg .IndexByteString (f , '=' )
if eq == -1 {
return errors .New ("criterion lacks equal sign" )
}
if hasUpperCase (f ) {
lower := []byte (f )
lowerASCIIBytes (lower )
f = string (lower )
}
c = append (c , nssCriterion {
negate : not ,
status : f [:eq ],
action : f [eq +1 :],
})
return nil
})
return
}
The pages are generated with Golds v0.7.0-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .