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