// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package net

import (
	
	
	
	
	
)

const (
	nssConfigPath = "/etc/nsswitch.conf"
)

var nssConfig nsswitchConfig

type nsswitchConfig struct {
	initOnce sync.Once // guards init of nsswitchConfig

	// ch is used as a semaphore that only allows one lookup at a
	// time to recheck nsswitch.conf
	ch          chan struct{} // guards lastChecked and modTime
	lastChecked time.Time     // last time nsswitch.conf was checked

	mu      sync.Mutex // protects nssConf
	nssConf *nssConf
}

func getSystemNSS() *nssConf {
	nssConfig.tryUpdate()
	nssConfig.mu.Lock()
	 := nssConfig.nssConf
	nssConfig.mu.Unlock()
	return 
}

// init initializes conf and is only called via conf.initOnce.
func ( *nsswitchConfig) () {
	.nssConf = parseNSSConfFile("/etc/nsswitch.conf")
	.lastChecked = time.Now()
	.ch = make(chan struct{}, 1)
}

// tryUpdate tries to update conf.
func ( *nsswitchConfig) () {
	.initOnce.Do(.init)

	// Ensure only one update at a time checks nsswitch.conf
	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
}

// nssConf represents the state of the machine's /etc/nsswitch.conf file.
type nssConf struct {
	mtime   time.Time              // time of nsswitch.conf modification
	err     error                  // any error encountered opening or parsing the file
	sources map[string][]nssSource // keyed by database (e.g. "hosts")
}

type nssSource struct {
	source   string // e.g. "compat", "files", "mdns4_minimal"
	criteria []nssCriterion
}

// standardCriteria reports all specified criteria have the default
// status actions.
func ( nssSource) () bool {
	for ,  := range .criteria {
		if !.standardStatusAction( == len(.criteria)-1) {
			return false
		}
	}
	return true
}

// nssCriterion is the parsed structure of one of the criteria in brackets
// after an NSS source name.
type nssCriterion struct {
	negate bool   // if "!" was present
	status string // e.g. "success", "unavail" (lowercase)
	action string // e.g. "return", "continue" (lowercase)
}

// standardStatusAction reports whether c is equivalent to not
// specifying the criterion at all. last is whether this criteria is the
// last in the list.
func ( nssCriterion) ( bool) bool {
	if .negate {
		return false
	}
	var  string
	switch .status {
	case "success":
		 = "return"
	case "notfound", "unavail", "tryagain":
		 = "continue"
	default:
		// Unknown status
		return false
	}
	if  && .action == "return" {
		return true
	}
	return .action == 
}

func parseNSSConfFile( string) *nssConf {
	,  := open()
	if  != nil {
		return &nssConf{err: }
	}
	defer .close()
	, ,  := .stat()
	if  != nil {
		return &nssConf{err: }
	}

	 := parseNSSConf()
	.mtime = 
	return 
}

func parseNSSConf( *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 {
				 = 
				 = "" // done
			} else {
				 = [:]
				 = trimSpace([+1:])
			}
			var  []nssCriterion
			// See if there's a criteria block in brackets.
			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 
}

// parses "foo=bar !foo=bar"
func parseCriteria( 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
}