dinit-env.cc 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. #include <iostream>
  2. #include <fstream>
  3. #include "dinit-log.h"
  4. #include "dinit-env.h"
  5. environment main_env;
  6. // Log a parse error when reading the environment file.
  7. static void log_bad_env(int linenum)
  8. {
  9. log(loglevel_t::ERROR, "Invalid environment variable setting in environment file (line ", linenum, ")");
  10. }
  11. static void log_bad_env_cmd(int linenum)
  12. {
  13. log(loglevel_t::ERROR, "Unknown command in environment file (line ", linenum, ")");
  14. }
  15. // Read and set environment variables from a file. May throw std::bad_alloc, std::system_error.
  16. void read_env_file(const char *env_file_path, bool log_warnings, environment &env, bool throw_on_open_failure, const char *basedir)
  17. {
  18. std::ifstream env_file;
  19. if (!basedir || env_file_path[0] == '/') {
  20. env_file.open(env_file_path);
  21. } else {
  22. std::string envp = basedir;
  23. if (*envp.rbegin() != '/') {
  24. envp += '/';
  25. }
  26. envp += env_file_path;
  27. env_file.open(envp.c_str());
  28. }
  29. if (!env_file) {
  30. if (throw_on_open_failure) {
  31. throw std::system_error(errno, std::generic_category());
  32. }
  33. return;
  34. }
  35. env_file.exceptions(std::ios::badbit);
  36. auto &clocale = std::locale::classic();
  37. std::string line;
  38. int linenum = 0;
  39. while (std::getline(env_file, line)) {
  40. linenum++;
  41. auto lpos = line.begin();
  42. auto lend = line.end();
  43. while (lpos != lend && std::isspace(*lpos, clocale)) {
  44. ++lpos;
  45. }
  46. if (lpos == lend) continue; // empty line
  47. if (*lpos == '#') {
  48. continue;
  49. }
  50. if (*lpos == '=') {
  51. if (log_warnings) {
  52. log_bad_env(linenum);
  53. }
  54. continue;
  55. }
  56. // "!COMMAND" form.
  57. if (*lpos == '!') {
  58. ++lpos; // lpos = first char of command
  59. auto epos = lpos;
  60. do {
  61. ++epos;
  62. } while(epos != lend && !std::isspace(*epos, clocale));
  63. const char *lpos_p = line.data() + (lpos - line.begin());
  64. string_view cmd {lpos_p, (size_t)(epos - lpos)};
  65. std::vector<string_view> cmd_args;
  66. while (epos != lend) {
  67. // skip whitespace
  68. while (std::isspace(*epos, clocale)) {
  69. ++epos;
  70. if (epos == lend) goto process_cmd; // no more args
  71. }
  72. // all non-ws is argument until next ws
  73. const char *arg_begin = line.c_str() + (epos - line.begin());
  74. auto arg_begin_i = epos;
  75. while (epos != lend && !std::isspace(*epos)) {
  76. ++epos;
  77. }
  78. cmd_args.push_back(string_view {arg_begin, (size_t)(epos - arg_begin_i)});
  79. }
  80. process_cmd:
  81. if (cmd == "clear") {
  82. env.clear_no_inherit();
  83. }
  84. else if (cmd == "unset") {
  85. for (string_view arg : cmd_args) {
  86. env.undefine_var(std::string(arg.data(), arg.length()));
  87. }
  88. }
  89. else if (cmd == "import") {
  90. for (string_view arg : cmd_args) {
  91. env.import_parent_var(std::string(arg.data(), arg.length()));
  92. }
  93. }
  94. else if (log_warnings) {
  95. log_bad_env_cmd(linenum);
  96. }
  97. continue;
  98. }
  99. // ENV=VALUE form.
  100. auto name_begin = lpos++;
  101. // skip until '=' or whitespace:
  102. while (lpos != lend && *lpos != '=' && !std::isspace(*lpos, clocale)) ++lpos;
  103. auto name_end = lpos;
  104. // skip whitespace:
  105. while (lpos != lend && std::isspace(*lpos, clocale)) ++lpos;
  106. if (lpos == lend || *lpos != '=') {
  107. if (log_warnings) {
  108. log_bad_env(linenum);
  109. }
  110. continue;
  111. }
  112. ++lpos;
  113. auto val_begin = lpos;
  114. auto val_end = lend;
  115. if (val_begin != (name_end + 1) || name_begin != line.begin()) {
  116. // there are spaces that we need to eliminate
  117. std::string name_and_val;
  118. name_and_val.reserve((name_end - name_begin) + 1 + (val_end - val_begin));
  119. name_and_val = line.substr(name_begin - line.begin(), name_end - name_begin);
  120. name_and_val.append(1, '=');
  121. name_and_val.append(val_begin, val_end);
  122. env.set_var(std::move(name_and_val));
  123. }
  124. else {
  125. line.shrink_to_fit();
  126. env.set_var(std::move(line));
  127. }
  128. }
  129. }