From 274bd156fea8bc6a743c4d847ca58ed034ea25e4 Mon Sep 17 00:00:00 2001 From: "Benjamin S. Allen" Date: Sat, 8 Jun 2019 15:44:35 -0500 Subject: [PATCH] Support More Complex Cmdline Configuration Fixes #2755. Allows for quoted arguments with spaces in the kernel cmdline to be parsed properly. An example command line: ``` $ cat /proc/cmdline earlyprintk=serial console=ttyS0 rancher.autologin=ttyS0 rancher.defaults.hostname=ros-vm1 rancher.defaults.network.dns.nameservers=[192.168.64.1] rancher.network.interfaces.eth0.dhcp=true rancher.network.interfaces.eth1.dhcp=false rancher.network.interfaces.eth1.address=192.168.99.11/24 rancher.state.dev=LABEL=RANCHER_STATE rancher.state.autoformat=[/dev/vda] rancher.state.formatzero cc.ssh_authorized_keys=['ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOz8mD0tRrNsHBLHD5jVgmXO26JA7eKFZrj4Ic9KR2y3qXlxU9JCYYn/qDyTCmExt8Rw6SaU/BvgU7WT3Bjsi6c=','ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJj5mkpBHBBAW5XClcB5aFTWph+VCL7I0W8gm93AT5w4','ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKfb0O2qXgIgrtD5Mj7fBYdg4jMrT7wetBbkG2e4maDsRR3AtSYjEB3NeEifM8gdvIf0gYs1BNB/Ar76agaQGeqW+Ewb2LWdypr4Ipw09yWCrC9ttVbCnHuzVLYjML0CNgpjIRC+FC5r1X1gm2LufRN4orZ1NQvNhRRWJVT37vRtHo79TecK0DKQmy87Zpj3cNiI/5iObnTk56pZWpIAEiC5hEVkcVxmdkLJs3YonWVZzmK/Y8uvFtF+GhA6Jcpc38zDQHKsOjFWvj3qbWtVEQteNDxsM2pNeXY5wdrhRn4YSdKme9Cm7CdAogIdAdPtqPIfq/jY0QczS12qFZH7zt'] ``` Results in: ``` $ sudo ros config export rancher: defaults: hostname: ros-vm1 network: dns: nameservers: - 192.168.64.1 environment: EXTRA_CMDLINE: /init AAAAC3NzaC1lZDI1NTE5AAAAIJj5mkpBHBBAW5XClcB5aFTWph+VCL7I0W8gm93AT5w4','ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKfb0O2qXgIgrtD5Mj7fBYdg4jMrT7wetBbkG2e4maDsRR3AtSYjEB3NeEifM8gdvIf0gYs1BNB/Ar76agaQGeqW+Ewb2LWdypr4Ipw09yWCrC9ttVbCnHuzVLYjML0CNgpjIRC+FC5r1X1gm2LufRN4orZ1NQvNhRRWJVT37vRtHo79TecK0DKQmy87Zpj3cNiI/5iObnTk56pZWpIAEiC5hEVkcVxmdkLJs3YonWVZzmK/Y8uvFtF+GhA6Jcpc38zDQHKsOjFWvj3qbWtVEQteNDxsM2pNeXY5wdrhRn4YSdKme9Cm7CdAogIdAdPtqPIfq/jY0QczS12qFZH7zt'] network: interfaces: eth0: dhcp: true eth1: address: 192.168.99.11/24 dhcp: false state: autoformat: - /dev/vda dev: LABEL=RANCHER_STATE formatzero: true ssh_authorized_keys: - ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOz8mD0tRrNsHBLHD5jVgmXO26JA7eKFZrj4Ic9KR2y3qXlxU9JCYYn/qDyTCmExt8Rw6SaU/BvgU7WT3Bjsi6c= - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJj5mkpBHBBAW5XClcB5aFTWph+VCL7I0W8gm93AT5w4 - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKfb0O2qXgIgrtD5Mj7fBYdg4jMrT7wetBbkG2e4maDsRR3AtSYjEB3NeEifM8gdvIf0gYs1BNB/Ar76agaQGeqW+Ewb2LWdypr4Ipw09yWCrC9ttVbCnHuzVLYjML0CNgpjIRC+FC5r1X1gm2LufRN4orZ1NQvNhRRWJVT37vRtHo79TecK0DKQmy87Zpj3cNiI/5iObnTk56pZWpIAEiC5hEVkcVxmdkLJs3YonWVZzmK/Y8uvFtF+GhA6Jcpc38zDQHKsOjFWvj3qbWtVEQteNDxsM2pNeXY5wdrhRn4YSdKme9Cm7CdAogIdAdPtqPIfq/jY0QczS12qFZH7zt ``` --- config/cmdline/cmdline.go | 26 +++++++++++++++++++++++++- config/config_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/config/cmdline/cmdline.go b/config/cmdline/cmdline.go index 4dcb49cbb..09f448cb2 100644 --- a/config/cmdline/cmdline.go +++ b/config/cmdline/cmdline.go @@ -3,6 +3,7 @@ package cmdline import ( "io/ioutil" "strings" + "unicode" "github.com/rancher/os/pkg/util" @@ -125,11 +126,34 @@ func UnmarshalOrReturnString(value string) (result interface{}) { return } +//splitCmdLine splits on spaces except when a space is within a quoted or bracketed string. +func splitCmdLine(cmdLine string) []string { + lastRune := rune(0) + f := func(c rune) bool { + switch { + case c == lastRune: + lastRune = rune(0) + return false + case lastRune != rune(0): + return false + case unicode.In(c, unicode.Quotation_Mark): + lastRune = c + return false + case c == '[': + lastRune = ']' + return false + default: + return c == ' ' + } + } + return strings.FieldsFunc(cmdLine, f) +} + func Parse(cmdLine string, parseAll bool) map[interface{}]interface{} { result := map[interface{}]interface{}{} outer: - for _, part := range strings.Split(cmdLine, " ") { + for _, part := range splitCmdLine(cmdLine) { if strings.HasPrefix(part, "cc.") { part = part[3:] } else if !strings.HasPrefix(part, "rancher.") { diff --git a/config/config_test.go b/config/config_test.go index a74f37790..37145ca8a 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -123,6 +123,12 @@ func TestCmdlineParse(t *testing.T) { }, }, cmdline.Parse("rancher.key=a\nb", false), false) + assert.Equal(map[interface{}]interface{}{ + "rancher": map[interface{}]interface{}{ + "key": "a b", + }, + }, cmdline.Parse("rancher.key='a b'", false), false) + assert.Equal(map[interface{}]interface{}{ "rancher": map[interface{}]interface{}{ "key": "a:b", @@ -158,6 +164,24 @@ func TestCmdlineParse(t *testing.T) { "strArray": []interface{}{"url:http://192.168.1.100/cloud-config?a=b"}, }, }, cmdline.Parse("rancher.strArray=[url:http://192.168.1.100/cloud-config?a=b]", false), false) + + assert.Equal(map[interface{}]interface{}{ + "rancher": map[interface{}]interface{}{ + "strArray": []interface{}{"part1 part2", "part3"}, + }, + }, cmdline.Parse("rancher.strArray=['part1 part2',part3]", false), false) + + assert.Equal(map[interface{}]interface{}{ + "rancher": map[interface{}]interface{}{ + "strArray": []interface{}{"part1 part2", "part3"}, + }, + }, cmdline.Parse("rancher.strArray=[\"part1 part2\",part3]", false), false) + + assert.Equal(map[interface{}]interface{}{ + "rancher": map[interface{}]interface{}{ + "strArray": []interface{}{"part1 part2", "part3"}, + }, + }, cmdline.Parse("rancher.strArray=[ \"part1 part2\", part3 ]", false), false) } func TestGet(t *testing.T) {