GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/swissknife_info.cc Lines: 0 126 0.0 %
Date: 2019-02-03 02:48:13 Branches: 0 118 0.0 %

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