dinit-env.h 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. #ifndef DINIT_ENV_H_INCLUDED
  2. #define DINIT_ENV_H_INCLUDED 1
  3. #include <unordered_map>
  4. #include <string>
  5. #include "dinit-util.h"
  6. #include "baseproc-sys.h"
  7. class environment;
  8. extern environment main_env;
  9. // Read an environment file and set variables in the current environment.
  10. // file - the file to read
  11. // log_warnings - if true, syntactic errors are logged
  12. // May throw bad_alloc or system_error.
  13. void read_env_file(const char *file, bool log_warnings, environment &env);
  14. // Note that our sets (defined as part of environment class below) allow searching based on a name
  15. // only (string_view) or "NAME=VALUE" assignment pair (std::string). It is important to always
  16. // search using the correct type.
  17. // Hash environment variable name only (not including value)
  18. struct hash_env_name
  19. {
  20. size_t operator()(const std::string &s) const
  21. {
  22. size_t eq_pos = s.find('=');
  23. return hash(string_view(s.data(), eq_pos));
  24. }
  25. size_t operator()(string_view s) const
  26. {
  27. return hash(s);
  28. }
  29. };
  30. // Comparison predicate for environment variables, checking name only
  31. struct env_equal_name
  32. {
  33. bool operator()(const std::string &a, const std::string &b) const noexcept
  34. {
  35. size_t a_eq_pos = a.find('=');
  36. size_t b_eq_pos = b.find('=');
  37. return string_view(a.data(), a_eq_pos) == string_view(b.data(), b_eq_pos);
  38. }
  39. // For comparison between a string and string_view, we can assume the view is just the name
  40. bool operator()(const std::string &a, string_view b) const noexcept
  41. {
  42. size_t a_eq_pos = a.find('=');
  43. return string_view(a.data(), a_eq_pos) == b;
  44. }
  45. bool operator()(string_view a, const std::string &b) const noexcept
  46. {
  47. return operator()(b, a);
  48. }
  49. };
  50. class environment
  51. {
  52. // Whether to keep the parent environment, as a whole. Individual variables can still be
  53. // modified or unset.
  54. bool keep_parent_env = true;
  55. // TODO keep natural order somehow
  56. using env_set = dinit_unordered_set<std::string, hash_env_name, env_equal_name>;
  57. using env_names = dinit_unordered_set<std::string,hash_sv,dinit_equal_to>;
  58. // Which specific variables to keep from parent environment (if keep_parent_env is false)
  59. env_names import_from_parent;
  60. // Which specific variables to remove (if keep_parent_env is true)
  61. env_names undefine;
  62. // set of variables modified or set:
  63. env_set set_vars;
  64. string_view find_var_name(string_view var)
  65. {
  66. const char *var_ch;
  67. for (var_ch = var.data(); *var_ch != '='; ++var_ch) {
  68. if (*var_ch == '\0') break;
  69. }
  70. return {var.data(), (size_t)(var_ch - var.data())};
  71. }
  72. public:
  73. struct env_map {
  74. // *non-owning* list of environment variables, i.e. list as suitable for exec
  75. std::vector<const char *> env_list;
  76. // map of variable name (via string_view) to its index in env_list
  77. std::unordered_map<string_view, unsigned, hash_sv> var_map;
  78. const char *lookup(string_view sv) const {
  79. auto it = var_map.find(sv);
  80. if (it != var_map.end()) {
  81. return env_list[it->second] + sv.size() + 1;
  82. }
  83. return nullptr;
  84. }
  85. };
  86. // return environment variable in form NAME=VALUE. Assumes that the real environment is the parent.
  87. string_view get(const std::string &name) const
  88. {
  89. auto it = set_vars.find(string_view(name));
  90. if (it != set_vars.end()) {
  91. return *it;
  92. }
  93. if (!keep_parent_env && !import_from_parent.contains(name)) {
  94. return {};
  95. }
  96. const char *val = bp_sys::getenv(name.c_str());
  97. if (val == nullptr) {
  98. return {};
  99. }
  100. const char *name_and_val = val - (name.length() + 1);
  101. size_t val_len = strlen(val);
  102. return {name_and_val, name.length() + 1 + val_len};
  103. }
  104. // Build a mapping excluding named variables (only called if the parent is the real environment).
  105. // Note that the return is non-owning, i.e. the variable values are backed by the environment object
  106. // and their lifetime is bounded to it.
  107. env_map build(const env_names &exclude) const {
  108. env_map mapping;
  109. if (keep_parent_env) {
  110. // import all from parent, excluding our own undefines + the exclude set
  111. if (bp_sys::environ != nullptr) {
  112. unsigned pos = 0;
  113. for (char **env = bp_sys::environ; *env != nullptr; ++env) {
  114. // find '='
  115. char *var_ch;
  116. for (var_ch = *env; *var_ch != '='; ++var_ch) {
  117. if (*var_ch == '\0') break;
  118. }
  119. // if this variable doesn't contain '=', ignore it
  120. if (*var_ch == '\0') continue;
  121. string_view name_view {*env, (size_t)(var_ch - *env)};
  122. if (undefine.contains(name_view) || exclude.contains(name_view)) {
  123. goto next_env_var;
  124. }
  125. mapping.env_list.push_back(*env);
  126. mapping.var_map.insert({name_view, pos});
  127. ++pos;
  128. next_env_var: ;
  129. }
  130. }
  131. }
  132. else {
  133. // import specific items from parent
  134. for (const std::string &import_name : import_from_parent) {
  135. // POSIX allows that getenv return its result in a static, per-thread buffer. Since this is
  136. // ridiculous, we'll assume that all implementations do the sane thing of simply returning
  137. // a pointer to the value part of the NAME=VALUE string in the actual environment array:
  138. const char *value = getenv(import_name.c_str());
  139. if (value == nullptr) continue;
  140. // go back through the name and the '=' to the beginning of NAME=VALUE:
  141. const char *name_and_val = value - (import_name.length() + 1);
  142. mapping.var_map.insert({import_name, mapping.env_list.size()});
  143. mapping.env_list.push_back(name_and_val);
  144. }
  145. }
  146. // add our own (excluding exclude set)
  147. for (const std::string &set_var : set_vars) {
  148. size_t eq_pos = set_var.find('=');
  149. string_view set_var_name = string_view(set_var.data(), eq_pos);
  150. if (!exclude.contains(set_var_name)) {
  151. auto iter = mapping.var_map.find(set_var_name);
  152. if (iter != mapping.var_map.end()) {
  153. unsigned pos = iter->second;
  154. mapping.env_list[pos] = set_var.c_str();
  155. }
  156. else {
  157. mapping.var_map[set_var_name] = mapping.env_list.size();
  158. mapping.env_list.push_back(set_var.c_str());
  159. }
  160. }
  161. }
  162. mapping.env_list.push_back(nullptr);
  163. return mapping;
  164. }
  165. env_map build(const environment &parent_env) const
  166. {
  167. env_map mapping;
  168. // first build base: variables from the parent(s) excluding those specifically excluded
  169. if (keep_parent_env) {
  170. mapping = parent_env.build(undefine);
  171. // remove final null entry:
  172. mapping.env_list.resize(mapping.env_list.size() - 1);
  173. }
  174. else {
  175. // import only those specifically chosen
  176. for (const std::string &import_name : import_from_parent) {
  177. string_view name_and_val = parent_env.get(import_name);
  178. if (name_and_val.empty()) continue;
  179. mapping.var_map.insert({import_name, mapping.env_list.size()});
  180. mapping.env_list.push_back(name_and_val.data());
  181. }
  182. }
  183. // add our own (excluding exclude set)
  184. for (const std::string &set_var : set_vars) {
  185. size_t eq_pos = set_var.find('=');
  186. string_view set_var_name = string_view(set_var.data(), eq_pos);
  187. auto iter = mapping.var_map.find(set_var_name);
  188. if (iter != mapping.var_map.end()) {
  189. unsigned pos = iter->second;
  190. mapping.env_list[pos] = set_var.c_str();
  191. }
  192. else {
  193. mapping.var_map[set_var_name] = mapping.env_list.size();
  194. mapping.env_list.push_back(set_var.c_str());
  195. }
  196. }
  197. mapping.env_list.push_back(nullptr);
  198. return mapping;
  199. }
  200. void set_var(std::string &&var_and_val)
  201. {
  202. string_view var_name = find_var_name(var_and_val);
  203. import_from_parent.erase(var_name);
  204. undefine.erase(var_name);
  205. auto insert_result = set_vars.insert(std::move(var_and_val));
  206. if (!insert_result.second) {
  207. *insert_result.first = var_and_val;
  208. }
  209. }
  210. void import_parent_var(std::string &&var_name)
  211. {
  212. undefine.erase(var_name);
  213. set_vars.erase(string_view(var_name));
  214. if (!keep_parent_env) {
  215. import_from_parent.insert(std::move(var_name));
  216. }
  217. }
  218. void undefine_var(std::string &&var_name)
  219. {
  220. import_from_parent.erase(var_name);
  221. set_vars.erase(string_view(var_name));
  222. if (keep_parent_env) {
  223. undefine.insert(std::move(var_name));
  224. }
  225. }
  226. void clear_no_inherit()
  227. {
  228. keep_parent_env = false;
  229. import_from_parent.clear();
  230. undefine.clear();
  231. set_vars.clear();
  232. }
  233. };
  234. #endif /* DINIT_ENV_H_INCLUDED */