Use named values when serializing

As described by openbmc/phosphor-settingsd#16, the code has a problem
when a new D-Bus property is added to a settings object that has a
modified property, meaning it has been serialized to file.  This is
because the file doesn't say which property the value is meant for - the
fields are only named value0, value1, etc. So if the new property is
alphabetically before the original property, the code will read the
value0 value into the new property though it was meant to be for the
original one.

To fix this, start using cereal's Name/Value Pair (NVP) feature, with
the field names named after the property, like:
    "QuiesceOnHwError": true

This requires the cereal version to bump from 1 to 2.  When saving, the
code will always write using version 2 using NVPs.  When loading it will
check the version in the file and if it was 1 it will read it using the
original non-NVP method, otherwise it will use the NVP method.

Example generated load and save functions:

```
template<class Archive>
void save(Archive& a,
          const Impl& setting,
          [[maybe_unused]] const std::uint32_t version)
{
    a(cereal::make_nvp("AutoReboot", setting.autoReboot()));
}

template<class Archive>
void load(Archive& a,
          Impl& setting,
          const std::uint32_t version)
{
    decltype(setting.autoReboot()) AutoReboot{};

    if (version < CLASS_VERSION_WITH_NVP)
    {
        a(AutoReboot);
    }
    else
    {
        try
        {
            a(CEREAL_NVP(AutoReboot));
        }
        catch (const cereal::Exception& e)
        {
            std::cerr << "Could not restore property AutoReboot on /xyz/openbmc_project/control/host0/auto_reboot, "
                      << "setting to default value.\n";
            AutoReboot = true;
        }
    }

    setting.autoReboot(AutoReboot);
}
```

This does require that the property names be unique across all
interfaces on that object path.  So far that isn't an issue for anything
I've seen, and there is a check in the code that will cause a compile
failure if that occurs.  If necessary, support could be added for this
but it didn't seem necessary as that is fairly unlikely.

Now if a new property is added to an object path with existing
serialized data, the code can see that that field isn't in the file and
will just initialize that property to the default value given in the
YAML.

If the version of code before this commit is applied to a system after
this code has already been run, it is still able to read in the values
even though they have real names instead of the valueN ones since other
than the name they are still in the same order and format.

Resolves openbmc/phosphor-settingsd#16

Change-Id: Ia8f4712d7098dd530895b1db4e4d0a7f88f4b9dc
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
1 file changed