CernVM-FS  2.12.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
swissknife_info.cc
Go to the documentation of this file.
1 
8 #define __STDC_FORMAT_MACROS
9 
10 #include "swissknife_info.h"
11 
12 
13 #include <string>
14 
15 #include "crypto/hash.h"
16 #include "manifest.h"
17 #include "network/download.h"
18 #include "util/logging.h"
19 #include "util/posix.h"
20 #include "util/string.h"
21 
22 using namespace std; // NOLINT
23 
24 namespace swissknife {
25 
29 static bool IsRemote(const string &repository) {
30  return HasPrefix(repository, "http://", false) ||
31  HasPrefix(repository, "https://", false);
32 }
33 
37 bool CommandInfo::Exists(const string &repository, const string &file) const {
38  if (IsRemote(repository)) {
39  const string url = repository + "/" + file;
40  download::JobInfo head(&url, false);
41  return download_manager()->Fetch(&head) == download::kFailOk;
42  } else {
43  return FileExists(file);
44  }
45 }
46 
47 ParameterList CommandInfo::GetParams() const {
49  r.push_back(Parameter::Mandatory('r', "repository directory / url"));
50  r.push_back(Parameter::Optional('u', "repository mount point"));
51  r.push_back(Parameter::Optional('l', "log level (0-4, default: 2)"));
52  r.push_back(Parameter::Optional('@', "proxy url"));
53  r.push_back(Parameter::Switch('c', "show root catalog hash"));
54  r.push_back(Parameter::Switch('C', "show mounted root catalog hash"));
55  r.push_back(Parameter::Switch('n', "show fully qualified repository name"));
56  r.push_back(Parameter::Switch('t', "show time stamp"));
57  r.push_back(Parameter::Switch('m',
58  "check if repository is marked as "
59  "replication master copy"));
60  r.push_back(Parameter::Switch('v', "repository revision number"));
61  r.push_back(Parameter::Switch('g',
62  "check if repository is garbage "
63  "collectable"));
64  r.push_back(Parameter::Switch('o',
65  "check if the repository maintains a "
66  "reference log file"));
67  r.push_back(Parameter::Switch('h', "print results in human readable form"));
68  r.push_back(Parameter::Switch('L', "follow HTTP redirects"));
69  r.push_back(Parameter::Switch('X',
70  "show whether external data is supported "
71  "in the root catalog."));
72  r.push_back(Parameter::Switch('M', "print repository meta info."));
73  r.push_back(Parameter::Switch('R', "print raw manifest."));
74  r.push_back(Parameter::Switch('e', "check if the repository is empty"));
75  return r;
76 }
77 
79  if (args.find('l') != args.end()) {
80  unsigned log_level =
81  kLogLevel0 << String2Uint64(*args.find('l')->second);
82  if (log_level > kLogNone) {
83  LogCvmfs(kLogCvmfs, kLogStderr, "invalid log level");
84  return 1;
85  }
86  SetLogVerbosity(static_cast<LogLevels>(log_level));
87  }
88  const string mount_point =
89  (args.find('u') != args.end()) ? *args.find('u')->second : "";
90  const string repository = MakeCanonicalPath(*args.find('r')->second);
91 
92  // sanity check
93  if (args.count('C') > 0 && mount_point.empty()) {
94  LogCvmfs(kLogCvmfs, kLogStderr, "need a CernVM-FS mountpoint (-u) for -C");
95  return 1;
96  }
97 
98  if (IsRemote(repository)) {
99  const bool follow_redirects = args.count('L') > 0;
100  const string proxy =
101  (args.find('@') != args.end()) ? *args.find('@')->second : "";
102  if (!this->InitDownloadManager(follow_redirects, proxy)) {
103  return 1;
104  }
105  }
106 
107  // Check if we should be human readable
108  const bool human_readable = (args.count('h') > 0);
109 
110  if (args.count('e') > 0) {
111  string manifest_path = IsRemote(repository)
112  ? ".cvmfspublished"
113  : repository + "/.cvmfspublished";
114  bool is_empty = !Exists(repository, manifest_path);
115  LogCvmfs(kLogCvmfs, kLogStdout, "%s%s",
116  (human_readable) ? "Empty Repository: " : "",
117  StringifyBool(is_empty).c_str());
118  if (is_empty) return 0;
119  }
120 
121  // Load manifest file
122  // Repository can be HTTP address or on local file system
123  // TODO(jblomer): do this using Manifest::Fetch
124  // currently this is not possible, since Manifest::Fetch asks for the
125  // repository name... Which we want to figure out with the tool at hand.
126  // Possible Fix: Allow for a Manifest::Fetch with an empty name.
128  if (IsRemote(repository)) {
129  const string url = repository + "/.cvmfspublished";
130  cvmfs::MemSink manifest_memsink;
131  download::JobInfo download_manifest(&url, false, false, NULL,
132  &manifest_memsink);
133  download::Failures retval = download_manager()->Fetch(&download_manifest);
134  if (retval != download::kFailOk) {
135  LogCvmfs(kLogCvmfs, kLogStderr, "failed to download manifest (%d - %s)",
136  retval, download::Code2Ascii(retval));
137  return 1;
138  }
139 
140  manifest = manifest::Manifest::LoadMem(manifest_memsink.data(),
141  manifest_memsink.pos());
142  } else {
143  if (chdir(repository.c_str()) != 0) {
144  LogCvmfs(kLogCvmfs, kLogStderr, "failed to switch to directory %s",
145  repository.c_str());
146  return 1;
147  }
148  manifest = manifest::Manifest::LoadFile(".cvmfspublished");
149  }
150 
151  if (!manifest.IsValid()) {
152  LogCvmfs(kLogCvmfs, kLogStderr, "failed to load repository manifest");
153  return 1;
154  }
155 
156  // Validate Manifest
157  const string certificate_path = "data/" + manifest->certificate().MakePath();
158  if (!Exists(repository, certificate_path)) {
159  LogCvmfs(kLogCvmfs, kLogStderr, "failed to find certificate (%s)",
160  certificate_path.c_str());
161  return 1;
162  }
163 
164  // Get information from the mount point
165  if (args.count('C') > 0) {
166  assert(!mount_point.empty());
167  const std::string root_hash_xattr = "user.root_hash";
168  std::string root_hash;
169  const bool success =
170  platform_getxattr(mount_point, root_hash_xattr, &root_hash);
171  if (!success) {
173  "failed to retrieve extended attribute "
174  " '%s' from '%s' (errno: %d)",
175  root_hash_xattr.c_str(), mount_point.c_str(), errno);
176  return 1;
177  }
178  LogCvmfs(kLogCvmfs, kLogStdout, "%s%s",
179  (human_readable) ? "Mounted Root Hash: " : "",
180  root_hash.c_str());
181  }
182 
183  // Get information about external data
184  if (args.count('X') > 0) {
185  assert(!mount_point.empty());
186  const std::string external_data_xattr = "user.external_data";
187  std::string external_data;
188  const bool success =
189  platform_getxattr(mount_point, external_data_xattr, &external_data);
190  if (!success) {
192  "failed to retrieve extended attribute "
193  " '%s' from '%s' (errno: %d)",
194  external_data_xattr.c_str(), mount_point.c_str(), errno);
195  return 1;
196  }
197  LogCvmfs(kLogCvmfs, kLogStdout, "%s%s",
198  (human_readable) ? "External data enabled: " : "",
199  external_data.c_str());
200  }
201 
202  // Get information from the Manifest
203  if (args.count('c') > 0) {
204  LogCvmfs(kLogCvmfs, kLogStdout, "%s%s",
205  (human_readable) ? "Root Catalog Hash: " : "",
206  manifest->catalog_hash().ToString().c_str());
207  }
208 
209  if (args.count('n') > 0) {
210  LogCvmfs(kLogCvmfs, kLogStdout, "%s%s",
211  (human_readable) ? "Fully Qualified Repository Name: " : "",
212  manifest->repository_name().c_str());
213  }
214 
215  if (args.count('t') > 0) {
216  LogCvmfs(kLogCvmfs, kLogStdout, "%s%lu",
217  (human_readable) ? "Time Stamp: " : "",
218  manifest->publish_timestamp());
219  }
220 
221  if (args.count('m') > 0) {
222  LogCvmfs(kLogCvmfs, kLogStdout, "%s%s",
223  (human_readable) ? "Replication Master Copy: " : "",
224  (Exists(repository, ".cvmfs_master_replica")) ? "true" : "false");
225  }
226 
227  if (args.count('v') > 0) {
228  LogCvmfs(kLogCvmfs, kLogStdout, "%s%s",
229  (human_readable) ? "Revision: " : "",
230  (StringifyInt(manifest->revision())).c_str());
231  }
232 
233  if (args.count('g') > 0) {
234  LogCvmfs(kLogCvmfs, kLogStdout, "%s%s",
235  (human_readable) ? "Garbage Collectable: " : "",
236  (StringifyBool(manifest->garbage_collectable())).c_str());
237  }
238 
239  if (args.count('o') > 0) {
240  LogCvmfs(kLogCvmfs, kLogStdout, "%s%s",
241  (human_readable) ? "Maintains Reference Log: " : "",
242  (Exists(repository, ".cvmfsreflog")) ? "true" : "false");
243  }
244 
245  if (args.count('M') > 0) {
246  shash::Any meta_info(manifest->meta_info());
247  if (meta_info.IsNull()) {
248  if (human_readable)
249  LogCvmfs(kLogCvmfs, kLogStderr, "no meta info available");
250  return 0;
251  }
252  const string url = repository + "/data/" + meta_info.MakePath();
253  cvmfs::MemSink metainfo_memsink;
254  download::JobInfo download_metainfo(&url, true, false, &meta_info,
255  &metainfo_memsink);
256  download::Failures retval = download_manager()->Fetch(&download_metainfo);
257  if (retval != download::kFailOk) {
258  if (human_readable)
260  "failed to download meta info (%d - %s)", retval,
261  download::Code2Ascii(retval));
262  return 1;
263  }
264  string info(reinterpret_cast<char*>(metainfo_memsink.data()),
265  metainfo_memsink.pos());
266  LogCvmfs(kLogCvmfs, kLogStdout | kLogNoLinebreak, "%s", info.c_str());
267  }
268 
269  if (args.count('R') > 0) {
271  manifest->ExportString().c_str());
272  }
273 
274  return 0;
275 }
276 
277 //------------------------------------------------------------------------------
278 
279 int CommandVersion::Main(const ArgumentList &args) {
280  LogCvmfs(kLogCvmfs, kLogStdout, "%s", CVMFS_VERSION);
281  return 0;
282 }
283 
284 } // namespace swissknife
static bool IsRemote(const string &repository)
void SetLogVerbosity(const LogLevels max_level)
Definition: logging.cc:261
const manifest::Manifest * manifest() const
Definition: repository.h:125
static Manifest * LoadMem(const unsigned char *buffer, const unsigned length)
Definition: manifest.cc:82
unsigned char * data()
Definition: sink_mem.h:122
std::vector< Parameter > ParameterList
Definition: swissknife.h:71
assert((mem||(size==0))&&"Out Of Memory")
string StringifyBool(const bool value)
Definition: string.cc:76
bool FileExists(const std::string &path)
Definition: posix.cc:802
const char * Code2Ascii(const Failures error)
bool platform_getxattr(const std::string &path, const std::string &name, std::string *value)
string StringifyInt(const int64_t value)
Definition: string.cc:78
bool IsValid() const
Definition: pointer.h:43
bool HasPrefix(const string &str, const string &prefix, const bool ignore_case)
Definition: string.cc:285
size_t pos()
Definition: sink_mem.h:121
uint64_t String2Uint64(const string &value)
Definition: string.cc:246
std::map< char, SharedPtr< std::string > > ArgumentList
Definition: swissknife.h:72
std::string meta_info() const
Definition: repository.h:128
std::string MakeCanonicalPath(const std::string &path)
Definition: posix.cc:98
static Manifest * LoadFile(const std::string &path)
Definition: manifest.cc:92
int Main(const ArgumentList &args)
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:528