CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
cvmfs_cache_posix.cc
Go to the documentation of this file.
1 
6 #include <signal.h>
7 #include <sys/types.h>
8 #include <sys/wait.h>
9 
10 #include <cstring>
11 #include <string>
12 
14 #include "cache_posix.h"
15 #include "smallhash.h"
16 #include "util/atomic.h"
17 #include "util/logging.h"
18 #include "util/posix.h"
19 #include "util/string.h"
20 
21 bool operator==(const cvmcache_hash &a, const cvmcache_hash &b) {
22  return memcmp(a.digest, b.digest, 20) == 0;
23 }
24 
25 bool operator!=(const cvmcache_hash &a, const cvmcache_hash &b) {
26  return memcmp(a.digest, b.digest, 20) != 0;
27 }
28 
29 namespace {
30 
31 struct CacheObject {
32  uint32_t refcnt;
33  int fd;
34  uint64_t size;
35 };
36 
37 struct Txn {
38  struct cvmcache_hash hash;
39  uint64_t size;
41  void *txn;
42  int fd;
43 };
44 
45 struct Listing {
47  std::vector<std::string> list;
48  std::vector<std::string>::iterator it;
49 };
50 
51 struct Settings {
53  : is_alien(false)
54  , cache_base_defined(false)
55  , cache_dir_defined(false)
56  , quota_limit(0) { }
57 
58  bool IsValid() {
59  if (is_alien && quota_limit > 0) {
60  error_reason = "Alien cache cannot be managed (no quota manager allowed)";
61  return false;
62  }
63  if (is_alien && workspace == "") {
64  error_reason = "Workspace option needs to be set for alien cache";
65  return false;
66  }
67  if ((is_alien ? 1 : 0) + (cache_base_defined ? 1 : 0)
68  + (cache_dir_defined ? 1 : 0)
69  != 1) {
70  error_reason = "CVMFS_CACHE_DIR, CVMFS_CACHE_BASE and CVMFS_CACHE_ALIEN "
71  "are mutually exclusive. Exactly one needs to be defined.";
72  return false;
73  }
74  return true;
75  }
76 
77  bool is_alien;
84  int64_t quota_limit;
85  std::string cache_path;
90  std::string workspace;
91 
92  std::string error_reason;
93 };
94 
95 
98  char *optarg = NULL;
99 
100  if ((optarg = cvmcache_options_get(options, "CVMFS_CACHE_QUOTA_LIMIT"))) {
101  settings.quota_limit = String2Int64(optarg) * 1024 * 1024;
102  cvmcache_options_free(optarg);
103  }
104 
105  if ((optarg = cvmcache_options_get(options, "CVMFS_CACHE_BASE"))) {
106  settings.cache_base_defined = true;
107  settings.cache_path = MakeCanonicalPath(optarg);
108  settings.cache_path += "/shared"; // this cache is always shared
109  settings.workspace = settings.cache_path; // default value for workspace
110  cvmcache_options_free(optarg);
111  }
112 
113  if ((optarg = cvmcache_options_get(options, "CVMFS_CACHE_DIR"))) {
114  settings.cache_dir_defined = true;
115  settings.cache_path = optarg;
116  settings.workspace = settings.cache_path; // default value for workspace
117  cvmcache_options_free(optarg);
118  }
119  if ((optarg = cvmcache_options_get(options, "CVMFS_CACHE_ALIEN"))) {
120  settings.is_alien = true;
121  settings.cache_path = optarg;
122  cvmcache_options_free(optarg);
123  }
124 
125  if ((optarg = cvmcache_options_get(options, "CVMFS_CACHE_WORKSPACE"))) {
126  // Used for the shared quota manager
127  settings.workspace = optarg;
128  cvmcache_options_free(optarg);
129  }
130  return settings;
131 }
132 
133 uint32_t cvmcache_hash_hasher(const struct cvmcache_hash &key) {
134  return (uint32_t) * (reinterpret_cast<const uint32_t *>(key.digest) + 1);
135 }
136 
137 uint32_t uint64_hasher(const uint64_t &key) { return (uint32_t)key; }
138 
140  shash::Any hash;
141  memcpy(hash.digest, h->digest, sizeof(h->digest));
142  hash.algorithm = static_cast<shash::Algorithms>(h->algorithm);
143  return hash;
144 }
145 
146 struct cvmcache_hash Cpphash2Chash(const shash::Any &hash) {
147  struct cvmcache_hash h;
148  memset(h.digest, 0, 20); // ensure deterministic digest
149  memcpy(h.digest, hash.digest, sizeof(h.digest));
150  h.algorithm = hash.algorithm;
151  return h;
152 }
153 
160 uint64_t g_pinned_size;
161 uint64_t g_used_size;
162 uint64_t g_capacity;
163 
164 int posix_chrefcnt(struct cvmcache_hash *id, int32_t change_by) {
165  CacheObject object;
166  if (!g_opened_objects->Lookup(*id, &object)) {
167  if (change_by < 0) {
169  }
170  CacheManager::LabeledObject labeled_object(Chash2Cpphash(id));
171  int fd = g_cache_mgr->Open(labeled_object);
172  if (fd < 0) {
174  }
175  object.fd = fd;
176  object.size = g_cache_mgr->GetSize(fd);
177  object.refcnt = 0;
178  g_pinned_size += object.size;
179  } else if (static_cast<int32_t>(object.refcnt) + change_by < 0) {
181  }
182 
183  object.refcnt += change_by;
184  if (object.refcnt == 0) {
185  if (g_cache_mgr->Close(object.fd) != 0) {
186  return CVMCACHE_STATUS_IOERR;
187  }
188  g_pinned_size -= object.size;
189  g_opened_objects->Erase(*id);
190  } else {
191  g_opened_objects->Insert(*id, object);
192  }
193  return CVMCACHE_STATUS_OK;
194 }
195 
196 // Only gives info for opened objects.
197 // Should be fine, since cvmfs only requests info for opened objects.
199  struct cvmcache_object_info *info) {
200  CacheObject object;
201  if (!g_opened_objects->Lookup(*id, &object)) {
203  }
204  info->id = *id;
205  info->size = object.size;
206  return CVMCACHE_STATUS_OK;
207 }
208 
209 int posix_pread(struct cvmcache_hash *id, uint64_t offset, uint32_t *size,
210  unsigned char *buffer) {
211  CacheObject object;
212  if (!g_opened_objects->Lookup(*id, &object)) {
214  }
215  if (offset > object.size) {
217  }
218  int64_t bytes_read = g_cache_mgr->Pread(object.fd, buffer, *size, offset);
219  if (bytes_read < 0) {
220  return CVMCACHE_STATUS_IOERR;
221  }
222  *size = static_cast<uint32_t>(bytes_read);
223  return CVMCACHE_STATUS_OK;
224 }
225 
227  uint64_t txn_id,
228  struct cvmcache_object_info *info) {
229  // cachemgr deletes txn in commit_txn
230  void *txn = malloc(g_cache_mgr->SizeOfTxn());
231  int fd = g_cache_mgr->StartTxn(Chash2Cpphash(id), info->size, txn);
232  if (fd < 0) {
233  return CVMCACHE_STATUS_IOERR;
234  }
235  Txn transaction;
236  transaction.fd = fd;
237  transaction.hash = *id;
238  transaction.txn = txn;
239  transaction.size = info->size;
240  transaction.type = info->type;
241  g_transactions->Insert(txn_id, transaction);
242 
243  CacheManager::Label label;
244  if (info->type == CVMCACHE_OBJECT_CATALOG) {
246  } else if (info->type == CVMCACHE_OBJECT_VOLATILE) {
248  }
249  if (info->description) {
250  label.path = info->description;
251  }
252  g_cache_mgr->CtrlTxn(label, 0, txn);
253  return CVMCACHE_STATUS_OK;
254 }
255 
256 int posix_write_txn(uint64_t txn_id, unsigned char *buffer, uint32_t size) {
257  Txn transaction;
258  if (!g_transactions->Lookup(txn_id, &transaction)) {
260  }
261  int64_t bytes_written = g_cache_mgr->Write(buffer, size, transaction.txn);
262  if ((bytes_written >= 0) && (static_cast<uint32_t>(bytes_written) == size)) {
263  return CVMCACHE_STATUS_OK;
264  } else {
265  return CVMCACHE_STATUS_IOERR;
266  }
267 }
268 
269 int posix_commit_txn(uint64_t txn_id) {
270  Txn transaction;
271  if (!g_transactions->Lookup(txn_id, &transaction)) {
273  }
274  CacheObject object;
275  if (!g_opened_objects->Lookup(transaction.hash, &object)) {
276  object.fd = g_cache_mgr->OpenFromTxn(transaction.txn);
277  if (object.fd < 0) {
278  return CVMCACHE_STATUS_IOERR;
279  }
280  int result = g_cache_mgr->CommitTxn(transaction.txn);
281  if (result) {
282  return CVMCACHE_STATUS_IOERR;
283  }
284  object.refcnt = 0;
285  object.size = g_cache_mgr->GetSize(object.fd);
286  g_pinned_size += object.size;
287  g_used_size += object.size;
288  } else {
289  if (g_cache_mgr->AbortTxn(transaction.txn) != 0) {
290  return CVMCACHE_STATUS_IOERR;
291  }
292  }
293  object.refcnt += 1;
294 
295  g_opened_objects->Insert(transaction.hash, object);
296  g_transactions->Erase(txn_id);
297  return CVMCACHE_STATUS_OK;
298 }
299 
300 int posix_abort_txn(uint64_t txn_id) {
301  Txn transaction;
302  if (!g_transactions->Lookup(txn_id, &transaction)) {
304  }
305  if (g_cache_mgr->AbortTxn(transaction.txn)) {
306  return CVMCACHE_STATUS_IOERR;
307  }
308  g_transactions->Erase(txn_id);
309  return CVMCACHE_STATUS_OK;
310 }
311 
312 int posix_info(struct cvmcache_info *info) {
313  info->no_shrink = -1;
314  info->size_bytes = g_capacity;
315  info->used_bytes = g_used_size;
316  info->pinned_bytes = g_pinned_size;
317  return CVMCACHE_STATUS_OK;
318 }
319 
320 int posix_breadcrumb_store(const char *fqrn,
321  const cvmcache_breadcrumb *breadcrumb) {
323  breadcrumb->timestamp, breadcrumb->revision);
324  if (!g_cache_mgr->StoreBreadcrumb(fqrn, bc)) {
325  return CVMCACHE_STATUS_IOERR;
326  }
327  return CVMCACHE_STATUS_OK;
328 }
329 
330 int posix_breadcrumb_load(const char *fqrn, cvmcache_breadcrumb *breadcrumb) {
332  if (!bc.IsValid()) {
334  }
335  breadcrumb->catalog_hash = Cpphash2Chash(bc.catalog_hash);
336  breadcrumb->timestamp = bc.timestamp;
337  breadcrumb->revision = bc.revision;
338  return CVMCACHE_STATUS_OK;
339 }
340 
341 void handle_sigint(int sig) {
343  atomic_inc32(&g_terminated);
344 }
345 
346 } // namespace
347 
348 int main(int argc, char **argv) {
349  if (argc < 2) {
350  fprintf(stderr, "Missing argument: path to config file\n");
351  return 1;
352  }
353 
355 
357  if (cvmcache_options_parse(options, argv[1]) != 0) {
359  "cannot parse options file %s", argv[1]);
360  return 1;
361  }
362  char *debug_log = cvmcache_options_get(options,
363  "CVMFS_CACHE_PLUGIN_DEBUGLOG");
364  if (debug_log != NULL) {
365  SetLogDebugFile(debug_log);
366  cvmcache_options_free(debug_log);
367  } else {
368  SetLogDebugFile("/dev/null");
369  }
370  char *locator = cvmcache_options_get(options, "CVMFS_CACHE_PLUGIN_LOCATOR");
371  if (locator == NULL) {
373  "CVMFS_CACHE_PLUGIN_LOCATOR missing");
374  cvmcache_options_fini(options);
375  return 1;
376  }
377  char *test_mode = cvmcache_options_get(options, "CVMFS_CACHE_PLUGIN_TEST");
378  if (!test_mode) {
379  char *watchdog_crash_dump_path = cvmcache_options_get(
380  options, "CVMFS_CACHE_PLUGIN_CRASH_DUMP");
381  cvmcache_spawn_watchdog(watchdog_crash_dump_path);
382  if (watchdog_crash_dump_path)
383  cvmcache_options_free(watchdog_crash_dump_path);
384  }
385 
386  Settings settings = GetSettings(options);
387  if (!settings.IsValid()) {
389  "Invalid config in file %s: %s", argv[1],
390  settings.error_reason.c_str());
391  return 1;
392  }
393 
394  g_cache_mgr = PosixCacheManager::Create(settings.cache_path,
395  settings.is_alien);
396 
397  cvmcache_hash empty_hash;
398  empty_hash.algorithm = 0;
399  memset(empty_hash.digest, 0, 20);
403  g_opened_objects->Init(32, empty_hash, cvmcache_hash_hasher);
404  g_transactions->Init(32, (uint64_t(-1)), uint64_hasher);
405  g_listings->Init(32, (uint64_t(-1)), uint64_hasher);
406  g_pinned_size = 0;
407  g_used_size = 0;
409 
410  struct cvmcache_callbacks callbacks;
411  memset(&callbacks, 0, sizeof(callbacks));
412  callbacks.cvmcache_chrefcnt = posix_chrefcnt;
413  callbacks.cvmcache_obj_info = posix_obj_info;
414  callbacks.cvmcache_pread = posix_pread;
419  callbacks.cvmcache_info = posix_info;
424 
425  g_ctx = cvmcache_init(&callbacks);
426  int retval = cvmcache_listen(g_ctx, locator);
427  if (!retval) {
428  LogCvmfs(kLogCache, kLogStderr | kLogSyslogErr, "failed to listen on %s",
429  locator);
430  return 1;
431  }
432 
433  if (test_mode) {
434  // Daemonize, print out PID
435  pid_t pid;
436  int statloc;
437  if ((pid = fork()) == 0) {
438  if ((pid = fork()) == 0) {
439  int null_read = open("/dev/null", O_RDONLY);
440  int null_write = open("/dev/null", O_WRONLY);
441  assert((null_read >= 0) && (null_write >= 0));
442  int retval = dup2(null_read, 0);
443  assert(retval == 0);
444  retval = dup2(null_write, 1);
445  assert(retval == 1);
446  retval = dup2(null_write, 2);
447  assert(retval == 2);
448  close(null_read);
449  close(null_write);
450  } else {
451  assert(pid > 0);
452  printf("%d\n", pid);
453  fflush(stdout);
454  fsync(1);
455  _exit(0);
456  }
457  } else {
458  assert(pid > 0);
459  waitpid(pid, &statloc, 0);
460  _exit(0);
461  }
462  }
463 
464  LogCvmfs(kLogCache, kLogStdout, "Listening for cvmfs clients on %s", locator);
465 
467 
468  if (!cvmcache_is_supervised()) {
470  "Running unsupervised. Quit by SIGINT (CTRL+C)");
471  atomic_init32(&g_terminated);
472  signal(SIGINT, handle_sigint);
473  while (atomic_read32(&g_terminated) == 0)
474  sleep(1);
475  }
476 
478 
479  delete g_opened_objects;
480  g_opened_objects = NULL;
481  delete g_transactions;
482  g_transactions = NULL;
483  delete g_listings;
484  g_listings = NULL;
485  delete g_cache_mgr;
486  g_cache_mgr = NULL;
487 
488  cvmcache_options_free(locator);
489  cvmcache_options_fini(options);
490 
493  return 0;
494 }
int posix_breadcrumb_store(const char *fqrn, const cvmcache_breadcrumb *breadcrumb)
struct cvmcache_hash catalog_hash
int(* cvmcache_chrefcnt)(struct cvmcache_hash *id, int32_t change_by)
struct cvmcache_hash Cpphash2Chash(const shash::Any &hash)
virtual int64_t Write(const void *buf, uint64_t size, void *txn)
Definition: cache_posix.cc:617
SmallHashDynamic< uint64_t, Txn > * g_transactions
virtual int Open(const LabeledObject &object)
Definition: cache_posix.cc:410
cvmcache_object_type
uint64_t timestamp
Definition: manifest.h:39
virtual int CommitTxn(void *txn)
Definition: cache_posix.cc:149
int(* cvmcache_info)(struct cvmcache_info *info)
shash::Any catalog_hash
Definition: manifest.h:38
void cvmcache_terminate(struct cvmcache_context *ctx)
static const int kLabelCatalog
Definition: cache.h:80
uint32_t cvmcache_hash_hasher(const struct cvmcache_hash &key)
int cvmcache_listen(struct cvmcache_context *ctx, char *locator)
Settings GetSettings(cvmcache_option_map *options)
struct cvmcache_hash id
void cvmcache_cleanup_global()
virtual int AbortTxn(void *txn)
Definition: cache_posix.cc:114
void cvmcache_terminate_watchdog()
int posix_pread(struct cvmcache_hash *id, uint64_t offset, uint32_t *size, unsigned char *buffer)
const SettingsPublisher & settings() const
Definition: repository.h:316
virtual int64_t GetSize(int fd)
Definition: cache_posix.cc:401
int posix_breadcrumb_load(const char *fqrn, cvmcache_breadcrumb *breadcrumb)
assert((mem||(size==0))&&"Out Of Memory")
uint64_t pinned_bytes
virtual void CtrlTxn(const Label &label, const int flags, void *txn)
Definition: cache_posix.cc:288
int cvmcache_is_supervised()
Algorithms algorithm
Definition: hash.h:122
int(* cvmcache_pread)(struct cvmcache_hash *id, uint64_t offset, uint32_t *size, unsigned char *buffer)
int(* cvmcache_breadcrumb_load)(const char *fqrn, cvmcache_breadcrumb *breadcrumb)
int posix_info(struct cvmcache_info *info)
#define SetLogDebugFile(filename)
unsigned char digest[digest_size_]
Definition: hash.h:121
virtual int OpenFromTxn(void *txn)
Definition: cache_posix.cc:430
void cvmcache_wait_for(struct cvmcache_context *ctx)
uint64_t used_bytes
Algorithms
Definition: hash.h:41
int main()
Definition: helper_allow.cc:16
uint64_t revision
Definition: manifest.h:40
int32_t atomic_int32
Definition: atomic.h:17
int64_t String2Int64(const string &value)
Definition: string.cc:234
static PosixCacheManager * Create(const std::string &cache_path, const bool alien_cache, const RenameWorkarounds rename_workaround=kRenameNormal, const bool do_refcount=true)
Definition: cache_posix.cc:268
int(* cvmcache_write_txn)(uint64_t txn_id, unsigned char *buffer, uint32_t size)
virtual int StartTxn(const shash::Any &id, uint64_t size, void *txn)
Definition: cache_posix.cc:525
virtual manifest::Breadcrumb LoadBreadcrumb(const std::string &fqrn)
Definition: cache_posix.cc:589
SmallHashDynamic< struct cvmcache_hash, CacheObject > * g_opened_objects
unsigned char digest[20]
SmallHashDynamic< uint64_t, Listing > * g_listings
bool operator!=(const cvmcache_hash &a, const cvmcache_hash &b)
uint32_t uint64_hasher(const uint64_t &key)
void cvmcache_options_free(char *value)
void cvmcache_spawn_watchdog(const char *crash_dump_file)
void cvmcache_options_fini(cvmcache_option_map *opts)
int posix_start_txn(struct cvmcache_hash *id, uint64_t txn_id, struct cvmcache_object_info *info)
uint64_t size_bytes
enum cvmcache_object_type type
struct cvmcache_context * cvmcache_init(struct cvmcache_callbacks *callbacks)
void cvmcache_init_global()
static const int kLabelVolatile
Definition: cache.h:82
int cvmcache_options_parse(cvmcache_option_map *opts, const char *path)
#define CVMCACHE_SIZE_UNKNOWN
bool operator==(const cvmcache_hash &a, const cvmcache_hash &b)
void cvmcache_process_requests(struct cvmcache_context *ctx, unsigned nworkers)
int(* cvmcache_breadcrumb_store)(const char *fqrn, const cvmcache_breadcrumb *breadcrumb)
int posix_write_txn(uint64_t txn_id, unsigned char *buffer, uint32_t size)
char * cvmcache_options_get(cvmcache_option_map *opts, const char *key)
int(* cvmcache_commit_txn)(uint64_t txn_id)
int(* cvmcache_start_txn)(struct cvmcache_hash *id, uint64_t txn_id, struct cvmcache_object_info *info)
bool IsValid() const
Definition: manifest.h:33
int(* cvmcache_abort_txn)(uint64_t txn_id)
std::string path
Definition: cache.h:133
int posix_chrefcnt(struct cvmcache_hash *id, int32_t change_by)
std::string MakeCanonicalPath(const std::string &path)
Definition: posix.cc:98
int posix_obj_info(struct cvmcache_hash *id, struct cvmcache_object_info *info)
virtual bool StoreBreadcrumb(const manifest::Manifest &manifest)
Definition: cache_posix.cc:595
int(* cvmcache_obj_info)(struct cvmcache_hash *id, struct cvmcache_object_info *info)
shash::Any Chash2Cpphash(const struct cvmcache_hash *h)
static void size_t size
Definition: smalloc.h:54
virtual int Close(int fd)
Definition: cache_posix.cc:141
virtual int64_t Pread(int fd, void *buf, uint64_t size, uint64_t offset)
Definition: cache_posix.cc:448
virtual uint32_t SizeOfTxn()
Definition: cache_posix.h:87
cvmcache_option_map * cvmcache_options_init()
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:545