ifupdown/ifaces.go

149 lines
2.9 KiB
Go

package ifupdown
import (
"bufio"
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"strings"
"sync"
)
type Interfaces map[string]*NetworkInterface
func (i Interfaces) buf() *bytes.Buffer {
buf := &bytes.Buffer{}
for _, iface := range i {
err := iface.write(func(s string) { _, _ = buf.Write([]byte(s)) })
if err != nil && !errors.Is(err, io.EOF) {
panic(err)
}
_, _ = buf.Write([]byte("\n"))
}
return buf
}
func (i Interfaces) Read(p []byte) (int, error) {
buf := i.buf()
defer pools.Buffers.Put(buf)
return buf.Read(p)
}
func (i Interfaces) String() string {
buf := i.buf()
defer pools.Buffers.Put(buf)
return buf.String()
}
func (i Interfaces) UnmarshalJSON(data []byte) error {
var ifaces map[string]*NetworkInterface
if err := json.Unmarshal(data, &ifaces); err != nil {
return err
}
for name, iface := range ifaces {
iface.Name = name
iface.allocated = true
i[name] = iface
}
return nil
}
type MultiParser struct {
Interfaces map[string]*NetworkInterface
Errs []error
buf []byte
mu *sync.Mutex
}
func NewMultiParser() *MultiParser {
return &MultiParser{
Interfaces: make(Interfaces),
Errs: make([]error, 0),
buf: make([]byte, 0),
mu: &sync.Mutex{},
}
}
func (p *MultiParser) Write(data []byte) (int, error) {
p.mu.Lock()
p.buf = append(p.buf, data...)
p.mu.Unlock()
return len(data), nil
}
func (p *MultiParser) Parse() (Interfaces, error) {
p.mu.Lock()
defer p.mu.Unlock()
scanner := bufio.NewScanner(strings.NewReader(string(p.buf)))
index := 0
currentIfaceName := ""
buf := pools.Buffers.Get()
defer pools.Buffers.Put(buf)
flush := func(name string) (*NetworkInterface, bool) {
if len(buf.Bytes()) == 0 {
return nil, false
}
defer buf.Reset()
newIface := NewNetworkInterface(name)
if _, err := buf.WriteTo(newIface); err != nil {
p.Errs = append(p.Errs, err)
return nil, false
}
p.Interfaces[newIface.Name] = newIface
return newIface, true
}
w := func(s string) {
_, _ = buf.WriteString(s)
_ = buf.WriteByte('\n')
}
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
upForChange := currentIfaceName == "" ||
(currentIfaceName != "" && !strings.Contains(line, currentIfaceName)) ||
index == 0
startDetected := len(strings.Fields(line)) > 1 &&
(strings.HasPrefix(line, "auto") ||
strings.HasPrefix(line, "allow-") ||
strings.HasPrefix(line, "iface"))
switch {
case line == "", strings.HasPrefix(line, "#"):
continue
case upForChange && startDetected:
newName := strings.Fields(line)[1]
if ifa, ok := flush(newName); ok {
currentIfaceName = ifa.Name
index++
}
w(line)
continue
default:
w(line)
}
}
if len(buf.Bytes()) > 0 {
flush("unknown")
}
var multiErr error
for _, err := range p.Errs {
switch {
case err == nil:
continue
case multiErr == nil:
multiErr = err
default:
multiErr = fmt.Errorf("%w, %w", multiErr, err)
}
}
return p.Interfaces, multiErr
}