Source file
src/net/nss.go
1
2
3
4
5 package net
6
7 import (
8 "errors"
9 "internal/bytealg"
10 "os"
11 "sync"
12 "time"
13 )
14
15 const (
16 nssConfigPath = "/etc/nsswitch.conf"
17 )
18
19 var nssConfig nsswitchConfig
20
21 type nsswitchConfig struct {
22 initOnce sync.Once
23
24
25
26 ch chan struct{}
27 lastChecked time.Time
28
29 mu sync.Mutex
30 nssConf *nssConf
31 }
32
33 func getSystemNSS() *nssConf {
34 nssConfig.tryUpdate()
35 nssConfig.mu.Lock()
36 conf := nssConfig.nssConf
37 nssConfig.mu.Unlock()
38 return conf
39 }
40
41
42 func (conf *nsswitchConfig) init() {
43 conf.nssConf = parseNSSConfFile("/etc/nsswitch.conf")
44 conf.lastChecked = time.Now()
45 conf.ch = make(chan struct{}, 1)
46 }
47
48
49 func (conf *nsswitchConfig) tryUpdate() {
50 conf.initOnce.Do(conf.init)
51
52
53 if !conf.tryAcquireSema() {
54 return
55 }
56 defer conf.releaseSema()
57
58 now := time.Now()
59 if conf.lastChecked.After(now.Add(-5 * time.Second)) {
60 return
61 }
62 conf.lastChecked = now
63
64 var mtime time.Time
65 if fi, err := os.Stat(nssConfigPath); err == nil {
66 mtime = fi.ModTime()
67 }
68 if mtime.Equal(conf.nssConf.mtime) {
69 return
70 }
71
72 nssConf := parseNSSConfFile(nssConfigPath)
73 conf.mu.Lock()
74 conf.nssConf = nssConf
75 conf.mu.Unlock()
76 }
77
78 func (conf *nsswitchConfig) acquireSema() {
79 conf.ch <- struct{}{}
80 }
81
82 func (conf *nsswitchConfig) tryAcquireSema() bool {
83 select {
84 case conf.ch <- struct{}{}:
85 return true
86 default:
87 return false
88 }
89 }
90
91 func (conf *nsswitchConfig) releaseSema() {
92 <-conf.ch
93 }
94
95
96 type nssConf struct {
97 mtime time.Time
98 err error
99 sources map[string][]nssSource
100 }
101
102 type nssSource struct {
103 source string
104 criteria []nssCriterion
105 }
106
107
108
109 func (s nssSource) standardCriteria() bool {
110 for i, crit := range s.criteria {
111 if !crit.standardStatusAction(i == len(s.criteria)-1) {
112 return false
113 }
114 }
115 return true
116 }
117
118
119
120 type nssCriterion struct {
121 negate bool
122 status string
123 action string
124 }
125
126
127
128
129 func (c nssCriterion) standardStatusAction(last bool) bool {
130 if c.negate {
131 return false
132 }
133 var def string
134 switch c.status {
135 case "success":
136 def = "return"
137 case "notfound", "unavail", "tryagain":
138 def = "continue"
139 default:
140
141 return false
142 }
143 if last && c.action == "return" {
144 return true
145 }
146 return c.action == def
147 }
148
149 func parseNSSConfFile(file string) *nssConf {
150 f, err := open(file)
151 if err != nil {
152 return &nssConf{err: err}
153 }
154 defer f.close()
155 mtime, _, err := f.stat()
156 if err != nil {
157 return &nssConf{err: err}
158 }
159
160 conf := parseNSSConf(f)
161 conf.mtime = mtime
162 return conf
163 }
164
165 func parseNSSConf(f *file) *nssConf {
166 conf := new(nssConf)
167 for line, ok := f.readLine(); ok; line, ok = f.readLine() {
168 line = trimSpace(removeComment(line))
169 if len(line) == 0 {
170 continue
171 }
172 colon := bytealg.IndexByteString(line, ':')
173 if colon == -1 {
174 conf.err = errors.New("no colon on line")
175 return conf
176 }
177 db := trimSpace(line[:colon])
178 srcs := line[colon+1:]
179 for {
180 srcs = trimSpace(srcs)
181 if len(srcs) == 0 {
182 break
183 }
184 sp := bytealg.IndexByteString(srcs, ' ')
185 var src string
186 if sp == -1 {
187 src = srcs
188 srcs = ""
189 } else {
190 src = srcs[:sp]
191 srcs = trimSpace(srcs[sp+1:])
192 }
193 var criteria []nssCriterion
194
195 if len(srcs) > 0 && srcs[0] == '[' {
196 bclose := bytealg.IndexByteString(srcs, ']')
197 if bclose == -1 {
198 conf.err = errors.New("unclosed criterion bracket")
199 return conf
200 }
201 var err error
202 criteria, err = parseCriteria(srcs[1:bclose])
203 if err != nil {
204 conf.err = errors.New("invalid criteria: " + srcs[1:bclose])
205 return conf
206 }
207 srcs = srcs[bclose+1:]
208 }
209 if conf.sources == nil {
210 conf.sources = make(map[string][]nssSource)
211 }
212 conf.sources[db] = append(conf.sources[db], nssSource{
213 source: src,
214 criteria: criteria,
215 })
216 }
217 }
218 return conf
219 }
220
221
222 func parseCriteria(x string) (c []nssCriterion, err error) {
223 err = foreachField(x, func(f string) error {
224 not := false
225 if len(f) > 0 && f[0] == '!' {
226 not = true
227 f = f[1:]
228 }
229 if len(f) < 3 {
230 return errors.New("criterion too short")
231 }
232 eq := bytealg.IndexByteString(f, '=')
233 if eq == -1 {
234 return errors.New("criterion lacks equal sign")
235 }
236 if hasUpperCase(f) {
237 lower := []byte(f)
238 lowerASCIIBytes(lower)
239 f = string(lower)
240 }
241 c = append(c, nssCriterion{
242 negate: not,
243 status: f[:eq],
244 action: f[eq+1:],
245 })
246 return nil
247 })
248 return
249 }
250
View as plain text