diff --git a/main.go b/main.go index 109360c..6cf9696 100644 --- a/main.go +++ b/main.go @@ -818,6 +818,104 @@ The validate command expects a configuration file as its only argument. Local fi return action.ListBootEntries(cfg) }, }, + { + Name: "grubedit", + Usage: "grubedit [set] KEY=VALUE", + Description: "Show or edit grub variables.\n\n" + + "Use `grubedit set KEY=VALUE` to set a grub variable. If the value is empty, it will remove the variable.\n\n" + + "Use `grubedit` to list all the grub variables found in the grub environment file.", + Action: func(c *cli.Context) error { + config, err := agentConfig.Scan(collector.Directories(constants.GetUserConfigDirs()...), collector.NoLogs, collector.StrictValidation(c.Bool("strict-validation"))) + if err != nil { + return err + } + grubEnv := c.String("grub-file") + if _, err := config.Fs.Stat(grubEnv); err != nil && os.IsNotExist(err) { + // If file doesn't exist, we just log it and return + // Is not really an error, its just that there is no file so no vars are set + config.Logger.Logger.Info().Msgf("No file %s found", grubEnv) + return nil + } + vars, err := utils.ReadPersistentVariables(grubEnv, config) + if err != nil { + config.Logger.Logger.Error().Err(err).Msgf("Error reading grub variables from %s", grubEnv) + return err + } + if len(vars) == 0 { + config.Logger.Logger.Info().Msgf("No grub variables found in %s", grubEnv) + } else { + // Format the variables into a nice aligned list + maxKeyLen := 0 + for k := range vars { + if len(k) > maxKeyLen { + maxKeyLen = len(k) + } + } + var b strings.Builder + for k, v := range vars { + b.WriteString(fmt.Sprintf("%-*s = %s\n", maxKeyLen, k, v)) + } + config.Logger.Logger.Info().Msgf("Grub variables found in %s:\n%s", grubEnv, b.String()) + } + return nil + }, + Subcommands: []*cli.Command{ + { + Name: "set", + Usage: "Add or Edit a grub variable. Setting an existing variable to an empty value will remove it", + UsageText: "grubedit set KEY=VALUE", + Before: func(c *cli.Context) error { + if c.Args().Len() != 1 { + cli.HelpPrinter(c.App.Writer, "File and key=value arguments missing\n\n", c.Command) + _ = cli.ShowSubcommandHelp(c) + return fmt.Errorf("") + } + return nil + }, + Action: func(c *cli.Context) error { + config, err := agentConfig.Scan(collector.Directories(constants.GetUserConfigDirs()...), collector.NoLogs, collector.StrictValidation(c.Bool("strict-validation"))) + if err != nil { + return err + } + + // Transform the key=value argument into a map + parts := strings.SplitN(c.Args().First(), "=", 2) + vars := make(map[string]string) + if len(parts) != 2 { + return fmt.Errorf("key=value argument must be in the format KEY=VALUE") + } + if parts[1] == "" { + // If the value is empty, we remove the variable + vars[parts[0]] = "" + } else { + // Otherwise we set the variable to the value + vars[parts[0]] = parts[1] + } + + grubEnv := c.String("grub-file") + err = utils.SetPersistentVariables(grubEnv, vars, config) + if err != nil { + config.Logger.Logger.Error().Err(err).Msgf("Error setting grub variables in %s", c.Args().First()) + return err + } + if parts[1] == "" { + config.Logger.Logger.Info().Msgf("Removed grub variable %s from %s", parts[0], grubEnv) + } else { + config.Logger.Logger.Info().Msgf("Set grub variable %s=%s in %s", parts[0], parts[1], grubEnv) + } + + return nil + }, + }, + }, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "grub-file", + Usage: "Path to the grub environment file to list or set variables to/from.", + Value: filepath.Join(constants.OEMPath, constants.GrubEnv), + }, + }, + }, { Name: "sysext", Usage: "sysext subcommands", diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index b2f5340..47c1421 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -27,6 +27,7 @@ import ( const ( GrubConf = "/etc/cos/grub.cfg" GrubOEMEnv = "grub_oem_env" + GrubEnv = "grubenv" // grubenv is a file used by grub to store environment variables. Goes into OEM so its runtime modifiable GrubDefEntry = "Kairos" DefaultTty = "tty1" BiosPartName = "bios" diff --git a/pkg/utils/grub.go b/pkg/utils/grub.go index 891a7ce..2161f89 100644 --- a/pkg/utils/grub.go +++ b/pkg/utils/grub.go @@ -474,6 +474,9 @@ func ReadPersistentVariables(grubEnvFile string, c *agentConfig.Config) (map[str if strings.HasPrefix(a, "#") { continue } + if a == "" { + continue + } splitted := strings.Split(a, "=") if len(splitted) == 2 { vars[splitted[0]] = splitted[1]