Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
config_utils.h
1 #ifndef CONFIG_UTILS_H_
2 #define CONFIG_UTILS_H_
3 
4 #include <string>
5 
6 #include <boost/property_tree/ini_parser.hpp>
7 
8 #include "logging/logging.h"
9 #include "types.h"
10 #include "utils.h"
11 
12 /*
13  The following uses a small amount of template hackery to provide a nice
14  interface to load the sota.toml config file. StripQuotesFromStrings is
15  templated, and passes everything that isn't a string straight through.
16  Strings in toml are always double-quoted, and we remove them by specializing
17  StripQuotesFromStrings for std::string.
18 
19  The end result is that the sequence of calls in Config::updateFromToml are
20  pretty much a direct expression of the required behaviour: load this variable
21  from this config entry, and print a warning at the
22 
23  Note that default values are defined by Config's default constructor.
24  */
25 
26 template <typename T>
27 inline T StripQuotesFromStrings(const T& value);
28 
29 template <>
30 inline std::string StripQuotesFromStrings<std::string>(const std::string& value) {
31  return Utils::stripQuotes(value);
32 }
33 
34 template <typename T>
35 inline T StripQuotesFromStrings(const T& value) {
36  return value;
37 }
38 
39 template <typename T>
40 inline T addQuotesToStrings(const T& value);
41 
42 template <>
43 inline std::string addQuotesToStrings<std::string>(const std::string& value) {
44  return Utils::addQuotes(value);
45 }
46 
47 template <typename T>
48 inline T addQuotesToStrings(const T& value) {
49  return value;
50 }
51 
52 template <typename T>
53 inline void writeOption(std::ostream& sink, const T& data, const std::string& option_name) {
54  sink << option_name << " = " << addQuotesToStrings(data) << "\n";
55 }
56 
57 template <typename T>
58 inline void CopyFromConfig(T& dest, const std::string& option_name, const boost::property_tree::ptree& pt) {
59  boost::optional<T> value = pt.get_optional<T>(option_name);
60  if (value.is_initialized()) {
61  dest = StripQuotesFromStrings(value.get());
62  }
63 }
64 
65 template <>
66 inline void CopyFromConfig(KeyType& dest, const std::string& option_name, const boost::property_tree::ptree& pt) {
67  boost::optional<std::string> value = pt.get_optional<std::string>(option_name);
68  if (value.is_initialized()) {
69  std::string key_type{StripQuotesFromStrings(value.get())};
70  if (key_type == "RSA2048") {
71  dest = KeyType::kRSA2048;
72  } else if (key_type == "RSA3072") {
73  dest = KeyType::kRSA3072;
74  } else if (key_type == "RSA4096") {
75  dest = KeyType::kRSA4096;
76  } else if (key_type == "ED25519") {
77  dest = KeyType::kED25519;
78  } else {
79  dest = KeyType::kUnknown;
80  }
81  }
82 }
83 
84 template <>
85 inline void CopyFromConfig(CryptoSource& dest, const std::string& option_name, const boost::property_tree::ptree& pt) {
86  boost::optional<std::string> value = pt.get_optional<std::string>(option_name);
87  if (value.is_initialized()) {
88  std::string crypto_source{StripQuotesFromStrings(value.get())};
89  if (crypto_source == "pkcs11") {
90  dest = CryptoSource::kPkcs11;
91  } else {
92  dest = CryptoSource::kFile;
93  }
94  }
95 }
96 
97 template <>
98 inline void CopyFromConfig(BasedPath& dest, const std::string& option_name, const boost::property_tree::ptree& pt) {
99  boost::optional<std::string> value = pt.get_optional<std::string>(option_name);
100  if (value.is_initialized()) {
101  BasedPath bp{StripQuotesFromStrings(value.get())};
102  dest = bp;
103  }
104 }
105 
106 template <typename T>
107 inline void CopySubtreeFromConfig(T& dest, const std::string& subtree_name, const boost::property_tree::ptree& pt) {
108  auto subtree = pt.get_child_optional(subtree_name);
109  if (subtree.is_initialized()) {
110  dest.updateFromPropertyTree(subtree.get());
111  } else {
112  // call with empty tree so that default value warnings are preserved
113  dest.updateFromPropertyTree(boost::property_tree::ptree());
114  }
115 }
116 
117 template <typename T>
118 inline void WriteSectionToStream(T& sec, const std::string& section_name, std::ostream& os) {
119  os << std::boolalpha;
120  os << "[" << section_name << "]\n";
121  sec.writeToStream(os);
122  os << "\n";
123 }
124 
125 class BaseConfig {
126  public:
127  virtual ~BaseConfig() = default;
128  void updateFromToml(const boost::filesystem::path& filename) {
129  LOG_INFO << "Reading config: " << filename;
130  if (!boost::filesystem::exists(filename)) {
131  throw std::runtime_error("Config file " + filename.string() + " does not exist.");
132  }
133  boost::property_tree::ptree pt;
134  boost::property_tree::ini_parser::read_ini(filename.string(), pt);
135  updateFromPropertyTree(pt);
136  }
137  virtual void updateFromPropertyTree(const boost::property_tree::ptree& pt) = 0;
138 
139  protected:
140  void updateFromDirs(const std::vector<boost::filesystem::path>& configs) {
141  std::map<std::string, boost::filesystem::path> configs_map;
142  for (const auto& config : configs) {
143  if (!boost::filesystem::exists(config)) {
144  continue;
145  }
146  if (boost::filesystem::is_directory(config)) {
147  for (const auto& config_file : Utils::getDirEntriesByExt(config, ".toml")) {
148  configs_map[config_file.filename().string()] = config_file;
149  }
150  } else {
151  configs_map[config.filename().string()] = config;
152  }
153  }
154  for (const auto& config_file : configs_map) {
155  updateFromToml(config_file.second);
156  }
157  }
158 
159  void checkDirs(const std::vector<boost::filesystem::path>& configs) {
160  for (const auto& config : configs) {
161  if (!boost::filesystem::exists(config)) {
162  throw std::runtime_error("Config directory " + config.string() + " does not exist.");
163  }
164  }
165  }
166 
167  std::vector<boost::filesystem::path> config_dirs_ = {"/usr/lib/sota/conf.d", "/etc/sota/conf.d/"};
168 };
169 
170 #endif // CONFIG_UTILS_H_
types.h
BaseConfig
Definition: config_utils.h:125
BasedPath
Definition: utils.h:101
data
General data structures.
Definition: types.cc:54