Merge pull request #24825 from giuseppe/simplify-systemd-parser
systemd: simplify parser and fix infinite loop
This commit is contained in:
commit
3cffc6bcaf
|
|
@ -48,7 +48,6 @@ type UnitFileParser struct {
|
||||||
|
|
||||||
currentGroup *unitGroup
|
currentGroup *unitGroup
|
||||||
pendingComments []*unitLine
|
pendingComments []*unitLine
|
||||||
lineNr int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUnitLine(key string, value string, isComment bool) *unitLine {
|
func newUnitLine(key string, value string, isComment bool) *unitLine {
|
||||||
|
|
@ -347,7 +346,7 @@ func (p *UnitFileParser) parseKeyValuePair(line string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *UnitFileParser) parseLine(line string) error {
|
func (p *UnitFileParser) parseLine(line string, lineNr int) error {
|
||||||
switch {
|
switch {
|
||||||
case lineIsComment(line):
|
case lineIsComment(line):
|
||||||
return p.parseComment(line)
|
return p.parseComment(line)
|
||||||
|
|
@ -356,7 +355,7 @@ func (p *UnitFileParser) parseLine(line string) error {
|
||||||
case lineIsKeyValuePair(line):
|
case lineIsKeyValuePair(line):
|
||||||
return p.parseKeyValuePair(line)
|
return p.parseKeyValuePair(line)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("file contains line %d: “%s” which is not a key-value pair, group, or comment", p.lineNr, line)
|
return fmt.Errorf("file contains line %d: “%s” which is not a key-value pair, group, or comment", lineNr, line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -376,53 +375,39 @@ func (p *UnitFileParser) flushPendingComments(toComment bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func nextLine(data string, afterPos int) (string, string) {
|
|
||||||
rest := data[afterPos:]
|
|
||||||
if i := strings.Index(rest, "\n"); i >= 0 {
|
|
||||||
return strings.TrimSpace(data[:i+afterPos]), data[i+afterPos+1:]
|
|
||||||
}
|
|
||||||
return data, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func trimSpacesFromLines(data string) string {
|
|
||||||
lines := strings.Split(data, "\n")
|
|
||||||
for i, line := range lines {
|
|
||||||
lines[i] = strings.TrimSpace(line)
|
|
||||||
}
|
|
||||||
return strings.Join(lines, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse an already loaded unit file (in the form of a string)
|
// Parse an already loaded unit file (in the form of a string)
|
||||||
func (f *UnitFile) Parse(data string) error {
|
func (f *UnitFile) Parse(data string) error {
|
||||||
p := &UnitFileParser{
|
p := &UnitFileParser{
|
||||||
file: f,
|
file: f,
|
||||||
lineNr: 1,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data = trimSpacesFromLines(data)
|
lines := strings.Split(strings.TrimSuffix(data, "\n"), "\n")
|
||||||
|
remaining := ""
|
||||||
|
|
||||||
for len(data) > 0 {
|
for lineNr, line := range lines {
|
||||||
origdata := data
|
line = strings.TrimSpace(line)
|
||||||
nLines := 1
|
if lineIsComment(line) {
|
||||||
var line string
|
// ignore the comment is inside a continuation line.
|
||||||
line, data = nextLine(data, 0)
|
if remaining != "" {
|
||||||
|
continue
|
||||||
if !lineIsComment(line) {
|
}
|
||||||
// Handle multi-line continuations
|
} else {
|
||||||
// Note: This doesn't support comments in the middle of the continuation, which systemd does
|
if strings.HasSuffix(line, "\\") {
|
||||||
if lineIsKeyValuePair(line) {
|
line = line[:len(line)-1]
|
||||||
for len(data) > 0 && line[len(line)-1] == '\\' {
|
if lineNr != len(lines)-1 {
|
||||||
line, data = nextLine(origdata, len(line)+1)
|
remaining += line
|
||||||
nLines++
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// check whether the line is a continuation of the previous line
|
||||||
|
if remaining != "" {
|
||||||
|
line = remaining + line
|
||||||
|
remaining = ""
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if err := p.parseLine(line); err != nil {
|
if err := p.parseLine(line, lineNr+1); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
p.lineNr += nLines
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.currentGroup == nil {
|
if p.currentGroup == nil {
|
||||||
|
|
@ -690,7 +675,6 @@ func (f *UnitFile) LookupInt(groupName string, key string, defaultValue int64) i
|
||||||
}
|
}
|
||||||
|
|
||||||
intVal, err := convertNumber(v)
|
intVal, err := convertNumber(v)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
@ -119,7 +120,10 @@ After=dbus.socket
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
BusName=org.freedesktop.login1
|
BusName=org.freedesktop.login1
|
||||||
CapabilityBoundingSet=CAP_SYS_ADMIN CAP_MAC_ADMIN CAP_AUDIT_CONTROL CAP_CHOWN CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_FOWNER CAP_SYS_TTY_CONFIG CAP_LINUX_IMMUTABLE
|
CapabilityBoundingSet=CAP_SYS_ADMIN CAP_MAC_ADMIN CAP_AUDIT_CONTROL CAP_CHOWN CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE \
|
||||||
|
# comment inside a continuation line \
|
||||||
|
CAP_FOWNER \
|
||||||
|
CAP_SYS_TTY_CONFIG CAP_LINUX_IMMUTABLE
|
||||||
DeviceAllow=block-* r
|
DeviceAllow=block-* r
|
||||||
DeviceAllow=char-/dev/console rw
|
DeviceAllow=char-/dev/console rw
|
||||||
DeviceAllow=char-drm rw
|
DeviceAllow=char-drm rw
|
||||||
|
|
@ -158,8 +162,8 @@ SystemCallFilter=@system-service
|
||||||
|
|
||||||
# Increase the default a bit in order to allow many simultaneous logins since
|
# Increase the default a bit in order to allow many simultaneous logins since
|
||||||
# we keep one fd open per session.
|
# we keep one fd open per session.
|
||||||
LimitNOFILE=524288
|
LimitNOFILE=524288 \`
|
||||||
`
|
|
||||||
const systemdnetworkdService = `# SPDX-License-Identifier: LGPL-2.1-or-later
|
const systemdnetworkdService = `# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
#
|
#
|
||||||
# This file is part of systemd.
|
# This file is part of systemd.
|
||||||
|
|
@ -264,6 +268,23 @@ var sampleDropinPaths = map[string][]string{
|
||||||
sampleDropinTemplateInstance: sampleDropinTemplateInstancePaths,
|
sampleDropinTemplateInstance: sampleDropinTemplateInstancePaths,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func filterComments(input string) string {
|
||||||
|
lines := strings.Split(input, "\n")
|
||||||
|
filtered := make([]string, 0, len(lines))
|
||||||
|
for _, line := range lines {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if strings.HasPrefix(line, "#") || strings.HasPrefix(line, ";") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filtered = append(filtered, line)
|
||||||
|
}
|
||||||
|
// merge continuation lines
|
||||||
|
joined := strings.ReplaceAll(strings.Join(filtered, "\n"), "\\\n", "")
|
||||||
|
|
||||||
|
// and remove any trailing new line, backslash or space
|
||||||
|
return strings.TrimRight(joined, "\n\\ ")
|
||||||
|
}
|
||||||
|
|
||||||
func TestRanges_Roundtrip(t *testing.T) {
|
func TestRanges_Roundtrip(t *testing.T) {
|
||||||
for i := range samples {
|
for i := range samples {
|
||||||
sample := samples[i]
|
sample := samples[i]
|
||||||
|
|
@ -278,7 +299,7 @@ func TestRanges_Roundtrip(t *testing.T) {
|
||||||
panic(e)
|
panic(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, sample, asStr)
|
assert.Equal(t, filterComments(sample), filterComments(asStr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -292,3 +313,16 @@ func TestUnitDropinPaths_Search(t *testing.T) {
|
||||||
assert.True(t, reflect.DeepEqual(expectedPaths, generatedPaths))
|
assert.True(t, reflect.DeepEqual(expectedPaths, generatedPaths))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FuzzParser(f *testing.F) {
|
||||||
|
for _, sample := range samples {
|
||||||
|
f.Add([]byte(sample))
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, orig []byte) {
|
||||||
|
unitFile := NewUnitFile()
|
||||||
|
unitFile.Path = "foo/bar"
|
||||||
|
unitFile.Filename = "bar"
|
||||||
|
_ = unitFile.Parse(string(orig))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
[Container]
|
||||||
|
Exec=true \
|
||||||
|
|
||||||
|
# must have a blank line above, but this line can be anything (including another blank line)
|
||||||
Loading…
Reference in New Issue