diff --git a/kernelargs.go b/kernelargs.go index fba88c70..c2fab768 100644 --- a/kernelargs.go +++ b/kernelargs.go @@ -13,46 +13,85 @@ package firecracker -import "strings" +import ( + "fmt" + "sort" + "strings" +) -// kernelArgs serializes+deserializes kernel boot parameters from/into a map. +// kernelArg represents a key and optional value pair for passing an argument +// into the kernel. Additionally, it also saves the position of the argument +// in the whole command line input. This is important because the kernel stops reading +// everything after `--` and passes these keys into the init process +type kernelArg struct { + position uint + key string + value *string +} + +func (karg kernelArg) String() string { + if karg.value != nil { + return fmt.Sprintf("%s=%s", karg.key, *karg.value) + } + return karg.key +} + +// kernelArgs serializes + deserializes kernel boot parameters from/into a map. // Kernel docs: https://www.kernel.org/doc/Documentation/admin-guide/kernel-parameters.txt // -// "key=value" will result in map["key"] = &"value" -// "key=" will result in map["key"] = &"" -// "key" will result in map["key"] = nil -type kernelArgs map[string]*string +// "key=value flag emptykey=" will be converted to +// map["key"] = { position: 0, key: "key", value: &"value" } +// map["flag"] = { position: 1, key: "flag", value: nil } +// map["emptykey"] = { position: 2, key: "emptykey", value: &"" } +type kernelArgs map[string]kernelArg -// serialize the kernelArgs back to a string that can be provided -// to the kernel +// Sorts the arguments by its position +// and serializes the map back into a single string func (kargs kernelArgs) String() string { - var fields []string - for key, value := range kargs { - field := key - if value != nil { - field += "=" + *value - } - fields = append(fields, field) + sortedArgs := make([]kernelArg, 0) + for _, arg := range kargs { + sortedArgs = append(sortedArgs, arg) + } + sort.SliceStable(sortedArgs, func(i, j int) bool { + return sortedArgs[i].position < sortedArgs[j].position + }) + + args := make([]string, 0) + for _, arg := range sortedArgs { + args = append(args, arg.String()) + } + return strings.Join(args, " ") +} + +// Add a new kernel argument to the kernelArgs, also the position is saved +func (kargs kernelArgs) Add(key string, value *string) { + kargs[key] = kernelArg{ + position: uint(len(kargs)), + key: key, + value: value, } - return strings.Join(fields, " ") } -// deserialize the provided string to a kernelArgs map +// Parses an input string and deserializes it into a map +// saving its position in the command line func parseKernelArgs(rawString string) kernelArgs { - argMap := make(map[string]*string) - for _, kv := range strings.Fields(rawString) { + args := make(map[string]kernelArg) + for index, kv := range strings.Fields(rawString) { // only split into up to 2 fields (before and after the first "=") kvSplit := strings.SplitN(kv, "=", 2) key := kvSplit[0] - var value *string if len(kvSplit) == 2 { value = &kvSplit[1] } - argMap[key] = value + args[key] = kernelArg{ + position: uint(index), + key: key, + value: value, + } } - return argMap + return args } diff --git a/kernelargs_test.go b/kernelargs_test.go index b6e0c21a..068d0c57 100644 --- a/kernelargs_test.go +++ b/kernelargs_test.go @@ -34,14 +34,42 @@ func TestKernelArgsSerder(t *testing.T) { booVal, ) - expectedParsedArgs := kernelArgs(map[string]*string{ - "foo": &fooVal, - "doo": &dooVal, - "blah": nil, - "huh": &emptyVal, - "bleh": nil, - "duh": &emptyVal, - "boo": &booVal, + expectedParsedArgs := kernelArgs(map[string]kernelArg{ + "foo": { + position: 0, + key: "foo", + value: &fooVal, + }, + "blah": { + position: 1, + key: "blah", + value: nil, + }, + "doo": { + position: 2, + key: "doo", + value: &dooVal, + }, + "huh": { + position: 3, + key: "huh", + value: &emptyVal, + }, + "bleh": { + position: 4, + key: "bleh", + value: nil, + }, + "duh": { + position: 5, + key: "duh", + value: &emptyVal, + }, + "boo": { + position: 6, + key: "boo", + value: &booVal, + }, }) actualParsedArgs := parseKernelArgs(argsString) diff --git a/machine.go b/machine.go index 56fa3e25..4d7c2ff7 100644 --- a/machine.go +++ b/machine.go @@ -504,7 +504,7 @@ func (m *Machine) setupKernelArgs(ctx context.Context) error { // Validation that we are not overriding an existing "ip=" setting happens in the network validation if staticIPInterface := m.Cfg.NetworkInterfaces.staticIPInterface(); staticIPInterface != nil { ipBootParam := staticIPInterface.StaticConfiguration.IPConfiguration.ipBootParam() - kernelArgs["ip"] = &ipBootParam + kernelArgs.Add("ip", &ipBootParam) } m.Cfg.KernelArgs = kernelArgs.String() @@ -649,7 +649,7 @@ func (m *Machine) startVMM(ctx context.Context) error { return nil } -//StopVMM stops the current VMM. +// StopVMM stops the current VMM. func (m *Machine) StopVMM() error { return m.stopVMM() }