CernVM-FS  2.12.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
options.cc
Go to the documentation of this file.
1 
9 #include "cvmfs_config.h"
10 #include "options.h"
11 
12 #include <fcntl.h>
13 #include <sys/wait.h>
14 #include <unistd.h>
15 
16 #include <cassert>
17 #include <cstdio>
18 #include <cstdlib>
19 #include <utility>
20 
21 #include "sanitizer.h"
22 #include "util/exception.h"
23 #include "util/logging.h"
24 #include "util/posix.h"
25 #include "util/string.h"
26 
27 using namespace std; // NOLINT
28 
29 #ifdef CVMFS_NAMESPACE_GUARD
30 namespace CVMFS_NAMESPACE_GUARD {
31 #endif
32 
33 
34 static string EscapeShell(const std::string &raw) {
35  for (unsigned i = 0, l = raw.length(); i < l; ++i) {
36  if (!(((raw[i] >= '0') && (raw[i] <= '9')) ||
37  ((raw[i] >= 'A') && (raw[i] <= 'Z')) ||
38  ((raw[i] >= 'a') && (raw[i] <= 'z')) ||
39  (raw[i] == '/') || (raw[i] == ':') || (raw[i] == '.') ||
40  (raw[i] == '_') || (raw[i] == '-') || (raw[i] == ',')))
41  {
42  goto escape_shell_quote;
43  }
44  }
45  return raw;
46 
47  escape_shell_quote:
48  string result = "'";
49  for (unsigned i = 0, l = raw.length(); i < l; ++i) {
50  if (raw[i] == '\'')
51  result += "\\";
52  result += raw[i];
53  }
54  result += "'";
55  return result;
56 }
57 
58 
59 string OptionsManager::TrimParameter(const string &parameter) {
60  string result = Trim(parameter);
61  // Strip "readonly"
62  if (result.find("readonly ") == 0) {
63  result = result.substr(9);
64  result = Trim(result);
65  } else if (result.find("export ") == 0) {
66  result = result.substr(7);
67  result = Trim(result);
68  } else if (result.find("eval ") == 0) {
69  result = result.substr(5);
70  result = Trim(result);
71  }
72  return result;
73 }
74 
76  vector <string> *tokens) {
77  size_t comment_idx = line->find("#");
78  if (comment_idx != string::npos)
79  *line = line->substr(0, comment_idx);
80  *line = Trim(*line);
81  if (line->empty())
82  return "";
83  *tokens = SplitString(*line, '=');
84  if (tokens->size() < 2)
85  return "";
86  string parameter = TrimParameter((*tokens)[0]);
87  if (parameter.find(" ") != string::npos)
88  return "";
89  return parameter;
90 }
91 
93  OptionsTemplateManager *opt_templ_mgr_param) {
94  delete opt_templ_mgr_;
95  if (opt_templ_mgr_param != NULL) {
96  opt_templ_mgr_ = opt_templ_mgr_param;
97  } else {
98  opt_templ_mgr_ = new OptionsTemplateManager();
99  }
100  for (std::map<std::string, std::string>::iterator it
101  = templatable_values_.begin();
102  it != templatable_values_.end();
103  it++) {
104  config_[it->first].value = it->second;
105  opt_templ_mgr_->ParseString(&(config_[it->first].value));
106  UpdateEnvironment(it->first, config_[it->first]);
107  }
108 }
109 
110 bool SimpleOptionsParser::TryParsePath(const string &config_file) {
111  LogCvmfs(kLogCvmfs, kLogDebug, "Fast-parsing config file %s",
112  config_file.c_str());
113  string line;
114  FILE *fconfig = fopen(config_file.c_str(), "r");
115  if (fconfig == NULL)
116  return false;
117 
118  // Read line by line and extract parameters
119  while (GetLineFile(fconfig, &line)) {
120  vector <string> tokens;
121  string parameter = SanitizeParameterAssignment(&line, &tokens);
122  if (parameter.empty())
123  continue;
124 
125  // Strip quotes from value
126  tokens.erase(tokens.begin());
127  string value = Trim(JoinStrings(tokens, "="));
128  unsigned value_length = value.length();
129  if (value_length > 2) {
130  if ( ((value[0] == '"') && ((value[value_length - 1] == '"'))) ||
131  ((value[0] == '\'') && ((value[value_length - 1] == '\''))) )
132  {
133  value = value.substr(1, value_length - 2);
134  }
135  }
136 
137  ConfigValue config_value;
138  config_value.source = config_file;
139  config_value.value = value;
140  PopulateParameter(parameter, config_value);
141  }
142  fclose(fconfig);
143  return true;
144 }
145 
146 void BashOptionsManager::ParsePath(const string &config_file,
147  const bool external) {
148  LogCvmfs(kLogCvmfs, kLogDebug, "Parsing config file %s", config_file.c_str());
149  int retval;
150  int pipe_open[2];
151  int pipe_quit[2];
152  pid_t pid_child = 0;
153  if (external) {
154  // cvmfs can run in the process group of automount in which case
155  // autofs won't mount an additional config repository. We create a
156  // short-lived process that detaches from the process group and triggers
157  // autofs to mount the config repository, if necessary. It holds a file
158  // handle to the config file until the main process opened the file, too.
159  MakePipe(pipe_open);
160  MakePipe(pipe_quit);
161  switch (pid_child = fork()) {
162  case -1:
163  PANIC(NULL);
164  case 0: { // Child
165  close(pipe_open[0]);
166  close(pipe_quit[1]);
167  // If this is not a process group leader, create a new session
168  if (getpgrp() != getpid()) {
169  pid_t new_session = setsid();
170  assert(new_session != (pid_t)-1);
171  }
172  (void)open(config_file.c_str(), O_RDONLY);
173  char ready = 'R';
174  WritePipe(pipe_open[1], &ready, 1);
175  retval = read(pipe_quit[0], &ready, 1);
176  _exit(retval); // Don't flush shared file descriptors
177  }
178  }
179  // Parent
180  close(pipe_open[1]);
181  close(pipe_quit[0]);
182  char ready = 0;
183  ReadPipe(pipe_open[0], &ready, 1);
184  assert(ready == 'R');
185  close(pipe_open[0]);
186  }
187  const string config_path = GetParentPath(config_file);
188  FILE *fconfig = fopen(config_file.c_str(), "r");
189  if (pid_child > 0) {
190  char c = 'C';
191  WritePipe(pipe_quit[1], &c, 1);
192  int statloc;
193  waitpid(pid_child, &statloc, 0);
194  close(pipe_quit[1]);
195  }
196  if (!fconfig) {
197  if (external && !DirectoryExists(config_path)) {
198  string repo_required;
199  if (GetValue("CVMFS_CONFIG_REPO_REQUIRED", &repo_required) &&
200  IsOn(repo_required)) {
202  "required configuration repository directory does not exist: %s",
203  config_path.c_str());
204  // Do not crash as in abort(), which can trigger core file creation
205  // from the mount helper
206  exit(1);
207  }
208 
210  "configuration repository directory does not exist: %s",
211  config_path.c_str());
212  }
213  return;
214  }
215 
216  int fd_stdin;
217  int fd_stdout;
218  int fd_stderr;
219  retval = Shell(&fd_stdin, &fd_stdout, &fd_stderr);
220  assert(retval);
221 
222  // Let the shell read the file
223  string line;
224  const string newline = "\n";
225  const string cd = "cd \"" + ((config_path == "") ? "/" : config_path) + "\"" +
226  newline;
227  WritePipe(fd_stdin, cd.data(), cd.length());
228  while (GetLineFile(fconfig, &line)) {
229  WritePipe(fd_stdin, line.data(), line.length());
230  WritePipe(fd_stdin, newline.data(), newline.length());
231  }
232  rewind(fconfig);
233 
234  // Read line by line and extract parameters
235  while (GetLineFile(fconfig, &line)) {
236  vector <string> tokens;
237  string parameter = SanitizeParameterAssignment(&line, &tokens);
238  if (parameter.empty())
239  continue;
240 
241  ConfigValue value;
242  value.source = config_file;
243  const string sh_echo = "echo $" + parameter + "\n";
244  WritePipe(fd_stdin, sh_echo.data(), sh_echo.length());
245  GetLineFd(fd_stdout, &value.value);
246  PopulateParameter(parameter, value);
247  }
248 
249  close(fd_stderr);
250  close(fd_stdout);
251  close(fd_stdin);
252  fclose(fconfig);
253 }
254 
255 
256 bool OptionsManager::HasConfigRepository(const string &fqrn,
257  string *config_path) {
258  string cvmfs_mount_dir;
259  if (!GetValue("CVMFS_MOUNT_DIR", &cvmfs_mount_dir)) {
260  LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr, "CVMFS_MOUNT_DIR missing");
261  return false;
262  }
263 
264  string config_repository;
265  if (GetValue("CVMFS_CONFIG_REPOSITORY", &config_repository)) {
266  if (config_repository.empty() || (config_repository == fqrn))
267  return false;
268  sanitizer::RepositorySanitizer repository_sanitizer;
269  if (!repository_sanitizer.IsValid(config_repository)) {
271  "invalid CVMFS_CONFIG_REPOSITORY: %s",
272  config_repository.c_str());
273  return false;
274  }
275  *config_path = cvmfs_mount_dir + "/" + config_repository + "/etc/cvmfs/";
276  return true;
277  }
278  return false;
279 }
280 
281 
282 void OptionsManager::ParseDefault(const string &fqrn) {
283  if (taint_environment_) {
284  int retval = setenv("CVMFS_FQRN", fqrn.c_str(), 1);
285  assert(retval == 0);
286  }
287 
288  protected_parameters_.clear();
289  ParsePath("/etc/cvmfs/default.conf", false);
290  vector<string> dist_defaults =
291  FindFilesBySuffix("/etc/cvmfs/default.d", ".conf");
292  for (unsigned i = 0; i < dist_defaults.size(); ++i) {
293  ParsePath(dist_defaults[i], false);
294  }
295  ProtectParameter("CVMFS_CONFIG_REPOSITORY");
296  string external_config_path;
297  if ((fqrn != "") && HasConfigRepository(fqrn, &external_config_path))
298  ParsePath(external_config_path + "default.conf", true);
299  ParsePath("/etc/cvmfs/default.local", false);
300 
301  if (fqrn != "") {
302  string domain;
303  vector<string> tokens = SplitString(fqrn, '.');
304  assert(tokens.size() > 1);
305  tokens.erase(tokens.begin());
306  domain = JoinStrings(tokens, ".");
307 
308  if (HasConfigRepository(fqrn, &external_config_path))
309  ParsePath(external_config_path+ "domain.d/" + domain + ".conf",
310  true);
311  ParsePath("/etc/cvmfs/domain.d/" + domain + ".conf", false);
312  ParsePath("/etc/cvmfs/domain.d/" + domain + ".local", false);
313 
314  if (HasConfigRepository(fqrn, &external_config_path))
315  ParsePath(external_config_path + "config.d/" + fqrn + ".conf", true);
316  ParsePath("/etc/cvmfs/config.d/" + fqrn + ".conf", false);
317  ParsePath("/etc/cvmfs/config.d/" + fqrn + ".local", false);
318  }
319 }
320 
321 
323  const string &param,
324  ConfigValue val) {
325  map<string, string>::const_iterator iter = protected_parameters_.find(param);
326  if ((iter != protected_parameters_.end()) && (iter->second != val.value)) {
328  "error in cvmfs configuration: attempt to change protected %s "
329  "from %s to %s",
330  param.c_str(), iter->second.c_str(), val.value.c_str());
331  return;
332  }
333  ParseValue(param, &val);
334  config_[param] = val;
335  UpdateEnvironment(param, val);
336 }
337 
339  const string &param,
340  ConfigValue val) {
341  if (taint_environment_) {
342  int retval = setenv(param.c_str(), val.value.c_str(), 1);
343  assert(retval == 0);
344  }
345 }
346 
347 void OptionsManager::ParseValue(std::string param, ConfigValue *val) {
348  string orig = val->value;
349  bool has_templ = opt_templ_mgr_->ParseString(&(val->value));
350  if (has_templ) {
351  templatable_values_[param] = orig;
352  }
353 }
354 
355 
356 void OptionsManager::ProtectParameter(const string &param) {
357  string value;
358  // We don't care about the result. If param does not yet exists, we lock it
359  // to the empty string.
360  (void) GetValue(param, &value);
361  protected_parameters_[param] = value;
362 }
363 
364 
366  config_.clear();
367 }
368 
369 
370 bool OptionsManager::IsDefined(const std::string &key) {
371  map<string, ConfigValue>::const_iterator iter = config_.find(key);
372  return iter != config_.end();
373 }
374 
375 
376 bool OptionsManager::GetValue(const string &key, string *value) const {
377  map<string, ConfigValue>::const_iterator iter = config_.find(key);
378  if (iter != config_.end()) {
379  *value = iter->second.value;
380  return true;
381  }
382  *value = "";
383  return false;
384 }
385 
386 
387 std::string OptionsManager::GetValueOrDie(const string &key) {
388  std::string value;
389  bool retval = GetValue(key, &value);
390  if (!retval) {
392  "%s configuration parameter missing", key.c_str());
393  }
394  return value;
395 }
396 
397 
398 bool OptionsManager::GetSource(const string &key, string *value) {
399  map<string, ConfigValue>::const_iterator iter = config_.find(key);
400  if (iter != config_.end()) {
401  *value = iter->second.source;
402  return true;
403  }
404  *value = "";
405  return false;
406 }
407 
408 
409 bool OptionsManager::IsOn(const std::string &param_value) const {
410  const string uppercase = ToUpper(param_value);
411  return ((uppercase == "YES") || (uppercase == "ON") || (uppercase == "1") ||
412  (uppercase == "TRUE"));
413 }
414 
415 
416 bool OptionsManager::IsOff(const std::string &param_value) const {
417  const string uppercase = ToUpper(param_value);
418  return ((uppercase == "NO") || (uppercase == "OFF") || (uppercase == "0") ||
419  (uppercase == "FALSE"));
420 }
421 
422 
423 vector<string> OptionsManager::GetAllKeys() {
424  vector<string> result;
425  for (map<string, ConfigValue>::const_iterator i = config_.begin(),
426  iEnd = config_.end(); i != iEnd; ++i)
427  {
428  result.push_back(i->first);
429  }
430  return result;
431 }
432 
433 
435  const string &key_prefix,
436  bool strip_prefix)
437 {
438  vector<string> result;
439  for (map<string, ConfigValue>::const_iterator i = config_.begin(),
440  iEnd = config_.end(); i != iEnd; ++i)
441  {
442  const bool ignore_prefix = false;
443  if (HasPrefix(i->first, key_prefix, ignore_prefix)) {
444  const string output_key = strip_prefix
445  ? i->first.substr(key_prefix.length())
446  : i->first;
447  result.push_back(output_key + "=" + i->second.value);
448  }
449  }
450  return result;
451 }
452 
453 
455  string result;
456  vector<string> keys = GetAllKeys();
457  for (unsigned i = 0, l = keys.size(); i < l; ++i) {
458  bool retval;
459  string value;
460  string source;
461 
462  retval = GetValue(keys[i], &value);
463  assert(retval);
464  retval = GetSource(keys[i], &source);
465  assert(retval);
466  result += keys[i] + "=" + EscapeShell(value) +
467  " # from " + source + "\n";
468  }
469  return result;
470 }
471 
472 
473 void OptionsManager::SetValue(const string &key, const string &value) {
474  ConfigValue config_value;
475  config_value.source = "@INTERNAL@";
476  config_value.value = value;
477  PopulateParameter(key, config_value);
478 }
479 
480 
481 void OptionsManager::UnsetValue(const string &key) {
482  protected_parameters_.erase(key);
483  config_.erase(key);
484  if (taint_environment_)
485  unsetenv(key.c_str());
486 }
487 
489  ::kTemplateIdentFqrn = "fqrn";
490 
492  ::kTemplateIdentOrg = "org";
493 
495  std::string fqrn) {
496  SetTemplate(kTemplateIdentFqrn, fqrn);
497  vector<string> fqrn_parts = SplitString(fqrn, '.');
498  SetTemplate(kTemplateIdentOrg, fqrn_parts[0]);
499 }
500 
501 void OptionsTemplateManager::SetTemplate(std::string name, std::string val) {
502  templates_[name] = val;
503 }
504 
505 std::string OptionsTemplateManager::GetTemplate(std::string name) {
506  if (templates_.count(name)) {
507  return templates_[name];
508  } else {
509  std::string var_name = "@" + name + "@";
510  LogCvmfs(kLogCvmfs, kLogDebug, "Undeclared variable: %s",
511  var_name.c_str());
512  return var_name;
513  }
514 }
515 
516 bool OptionsTemplateManager::ParseString(std::string *input) {
517  std::string result;
518  std::string in = *input;
519  bool has_vars = false;
520  int mode = 0;
521  std::string stock;
522  for (std::string::size_type i = 0; i < in.size(); i++) {
523  switch (mode) {
524  case 0:
525  if (in[i] == '@') {
526  mode = 1;
527  } else {
528  result += in[i];
529  }
530  break;
531  case 1:
532  if (in[i] == '@') {
533  mode = 0;
534  result += GetTemplate(stock);
535  stock = "";
536  has_vars = true;
537  } else {
538  stock += in[i];
539  }
540  break;
541  }
542  }
543  if (mode == 1) {
544  result += "@" + stock;
545  }
546  *input = result;
547  return has_vars;
548 }
549 
550 bool OptionsTemplateManager::HasTemplate(std::string name) {
551  return templates_.count(name);
552 }
553 
554 #ifdef CVMFS_NAMESPACE_GUARD
555 } // namespace CVMFS_NAMESPACE_GUARD
556 #endif
std::string GetValueOrDie(const std::string &key)
Definition: options.cc:387
std::string GetTemplate(std::string name)
Definition: options.cc:505
static const char * kTemplateIdentOrg
Definition: options.h:37
static const char * kTemplateIdentFqrn
Definition: options.h:36
#define PANIC(...)
Definition: exception.h:29
string Trim(const string &raw, bool trim_newline)
Definition: string.cc:428
bool GetSource(const std::string &key, std::string *value)
Definition: options.cc:398
string JoinStrings(const vector< string > &strings, const string &joint)
Definition: string.cc:325
static string EscapeShell(const std::string &raw)
Definition: options.cc:34
DefaultOptionsTemplateManager(std::string fqrn)
Definition: options.cc:494
bool Shell(int *fd_stdin, int *fd_stdout, int *fd_stderr)
Definition: posix.cc:1715
bool IsOn(const std::string &param_value) const
Definition: options.cc:409
std::vector< std::string > GetEnvironmentSubset(const std::string &key_prefix, bool strip_prefix)
Definition: options.cc:434
void PopulateParameter(const std::string &param, const ConfigValue val)
Definition: options.cc:322
std::string SanitizeParameterAssignment(std::string *line, std::vector< std::string > *tokens)
Definition: options.cc:75
assert((mem||(size==0))&&"Out Of Memory")
bool IsOff(const std::string &param_value) const
Definition: options.cc:416
void ParseDefault(const std::string &fqrn)
Definition: options.cc:282
bool HasConfigRepository(const std::string &fqrn, std::string *config_path)
Definition: options.cc:256
void MakePipe(int pipe_fd[2])
Definition: posix.cc:492
void SwitchTemplateManager(OptionsTemplateManager *opt_templ_mgr_param)
Definition: options.cc:92
bool IsValid(const std::string &input) const
Definition: sanitizer.cc:114
void SetValue(const std::string &key, const std::string &value)
Definition: options.cc:473
bool GetLineFile(FILE *f, std::string *line)
Definition: string.cc:386
string ToUpper(const string &mixed_case)
Definition: string.cc:476
bool TryParsePath(const std::string &config_file)
Definition: options.cc:110
vector< string > SplitString(const string &str, char delim)
Definition: string.cc:290
std::string Dump()
Definition: options.cc:454
std::vector< std::string > GetAllKeys()
Definition: options.cc:423
void ParseValue(const std::string param, ConfigValue *val)
Definition: options.cc:347
bool HasPrefix(const string &str, const string &prefix, const bool ignore_case)
Definition: string.cc:267
bool GetValue(const std::string &key, std::string *value) const
Definition: options.cc:376
bool DirectoryExists(const std::string &path)
Definition: posix.cc:813
std::string TrimParameter(const std::string &parameter)
Definition: options.cc:59
void ParsePath(const std::string &config_file, const bool external)
Definition: options.cc:146
bool GetLineFd(const int fd, std::string *line)
Definition: string.cc:404
void SetTemplate(std::string name, std::string val)
Definition: options.cc:501
void UpdateEnvironment(const std::string &param, ConfigValue val)
Definition: options.cc:338
bool ParseString(std::string *input)
Definition: options.cc:516
PathString GetParentPath(const PathString &path)
Definition: shortstring.cc:15
void ClearConfig()
Definition: options.cc:365
bool IsDefined(const std::string &key)
Definition: options.cc:370
void WritePipe(int fd, const void *buf, size_t nbyte)
Definition: posix.cc:501
void ReadPipe(int fd, void *buf, size_t nbyte)
Definition: posix.cc:513
std::vector< std::string > FindFilesBySuffix(const std::string &dir, const std::string &suffix)
Definition: posix.cc:1124
void ProtectParameter(const std::string &param)
Definition: options.cc:356
void UnsetValue(const std::string &key)
Definition: options.cc:481
bool HasTemplate(std::string name)
Definition: options.cc:550
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:528