GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/cache_plugin/cvmfs_cache_null.cc Lines: 0 218 0.0 %
Date: 2019-02-03 02:48:13 Branches: 0 84 0.0 %

Line Branch Exec Source
1
/**
2
 * This file is part of the CernVM File System.
3
 *
4
 * A demo external cache plugin.  All data is stored in std::string.  Feature-
5
 * complete but quite inefficient.
6
 */
7
8
#define __STDC_FORMAT_MACROS
9
10
#include <alloca.h>
11
#include <fcntl.h>
12
#include <inttypes.h>
13
#include <stdint.h>
14
#include <sys/types.h>
15
#include <sys/wait.h>
16
#include <unistd.h>
17
18
#include <cassert>
19
#include <cstdio>
20
#include <cstring>
21
#include <ctime>
22
#include <map>
23
#include <string>
24
#include <vector>
25
26
#include "libcvmfs_cache.h"
27
28
using namespace std;  // NOLINT
29
30
struct Object {
31
  struct cvmcache_hash id;
32
  string data;
33
  cvmcache_object_type type;
34
  int32_t refcnt;
35
  string description;
36
};
37
38
struct TxnInfo {
39
  struct cvmcache_hash id;
40
  Object partial_object;
41
};
42
43
struct Listing {
44
  Listing() : type(CVMCACHE_OBJECT_REGULAR), pos(0), elems(NULL) { }
45
  cvmcache_object_type type;
46
  uint64_t pos;
47
  vector<Object> *elems;
48
};
49
50
struct ComparableHash {
51
  ComparableHash() { memset(&hash, 0, sizeof(hash)); }
52
  explicit ComparableHash(const struct cvmcache_hash &h) : hash(h) { }
53
  struct cvmcache_hash hash;
54
  bool operator <(const ComparableHash &other) const {
55
    return cvmcache_hash_cmp(const_cast<cvmcache_hash *>(&(this->hash)),
56
                             const_cast<cvmcache_hash *>(&(other.hash))) < 0;
57
  }
58
};
59
60
map<uint64_t, TxnInfo> transactions;
61
map<ComparableHash, Object> storage;
62
map<uint64_t, Listing> listings;
63
64
struct cvmcache_context *ctx;
65
66
static int null_chrefcnt(struct cvmcache_hash *id, int32_t change_by) {
67
  ComparableHash h(*id);
68
  if (storage.find(h) == storage.end())
69
    return CVMCACHE_STATUS_NOENTRY;
70
71
  Object obj = storage[h];
72
  obj.refcnt += change_by;
73
  if (obj.refcnt < 0)
74
    return CVMCACHE_STATUS_BADCOUNT;
75
  storage[h] = obj;
76
  return CVMCACHE_STATUS_OK;
77
}
78
79
80
static int null_obj_info(
81
  struct cvmcache_hash *id,
82
  struct cvmcache_object_info *info)
83
{
84
  ComparableHash h(*id);
85
  if (storage.find(h) == storage.end())
86
    return CVMCACHE_STATUS_NOENTRY;
87
88
  Object obj = storage[h];
89
  info->size = obj.data.length();
90
  info->type = obj.type;
91
  info->pinned = obj.refcnt > 0;
92
  info->description = strdup(obj.description.c_str());
93
  return CVMCACHE_STATUS_OK;
94
}
95
96
97
static int null_pread(struct cvmcache_hash *id,
98
                    uint64_t offset,
99
                    uint32_t *size,
100
                    unsigned char *buffer)
101
{
102
  ComparableHash h(*id);
103
  string data = storage[h].data;
104
  if (offset > data.length())
105
    return CVMCACHE_STATUS_OUTOFBOUNDS;
106
  unsigned nbytes =
107
    std::min(*size, static_cast<uint32_t>(data.length() - offset));
108
  memcpy(buffer, data.data() + offset, nbytes);
109
  *size = nbytes;
110
  return CVMCACHE_STATUS_OK;
111
}
112
113
114
static int null_start_txn(
115
  struct cvmcache_hash *id,
116
  uint64_t txn_id,
117
  struct cvmcache_object_info *info)
118
{
119
  Object partial_object;
120
  partial_object.id = *id;
121
  partial_object.type = info->type;
122
  partial_object.refcnt = 1;
123
  if (info->description != NULL)
124
    partial_object.description = string(info->description);
125
  TxnInfo txn;
126
  txn.id = *id;
127
  txn.partial_object = partial_object;
128
  transactions[txn_id] = txn;
129
  return CVMCACHE_STATUS_OK;
130
}
131
132
133
static int null_write_txn(
134
  uint64_t txn_id,
135
  unsigned char *buffer,
136
  uint32_t size)
137
{
138
  TxnInfo txn = transactions[txn_id];
139
  txn.partial_object.data += string(reinterpret_cast<char *>(buffer), size);
140
  transactions[txn_id] = txn;
141
  return CVMCACHE_STATUS_OK;
142
}
143
144
145
static int null_commit_txn(uint64_t txn_id) {
146
  TxnInfo txn = transactions[txn_id];
147
  ComparableHash h(txn.id);
148
  storage[h] = txn.partial_object;
149
  transactions.erase(txn_id);
150
  return CVMCACHE_STATUS_OK;
151
}
152
153
static int null_abort_txn(uint64_t txn_id) {
154
  transactions.erase(txn_id);
155
  return CVMCACHE_STATUS_OK;
156
}
157
158
static int null_info(struct cvmcache_info *info) {
159
  info->size_bytes = uint64_t(-1);
160
  info->used_bytes = info->pinned_bytes = 0;
161
  for (map<ComparableHash, Object>::const_iterator i = storage.begin(),
162
       i_end = storage.end(); i != i_end; ++i)
163
  {
164
    info->used_bytes += i->second.data.length();
165
    if (i->second.refcnt > 0)
166
      info->pinned_bytes += i->second.data.length();
167
  }
168
  info->no_shrink = 0;
169
  return CVMCACHE_STATUS_OK;
170
}
171
172
static int null_shrink(uint64_t shrink_to, uint64_t *used) {
173
  struct cvmcache_info info;
174
  null_info(&info);
175
  *used = info.used_bytes;
176
  if (info.used_bytes <= shrink_to)
177
    return CVMCACHE_STATUS_OK;
178
179
  // Volatile objects
180
  for (map<ComparableHash, Object>::iterator i = storage.begin(),
181
       i_end = storage.end(); i != i_end; )
182
  {
183
    if ((i->second.refcnt > 0) || (i->second.type != CVMCACHE_OBJECT_VOLATILE))
184
    {
185
      ++i;
186
      continue;
187
    }
188
    unsigned length = i->second.data.length();
189
    map<ComparableHash, Object>::iterator delete_me = i++;
190
    storage.erase(delete_me);
191
    info.used_bytes -= length;
192
    if (info.used_bytes <= shrink_to) {
193
      *used = info.used_bytes;
194
      return CVMCACHE_STATUS_OK;
195
    }
196
  }
197
  // All other objects
198
  for (map<ComparableHash, Object>::iterator i = storage.begin(),
199
       i_end = storage.end(); i != i_end; )
200
  {
201
    if (i->second.refcnt > 0) {
202
      ++i;
203
      continue;
204
    }
205
    unsigned length = i->second.data.length();
206
    map<ComparableHash, Object>::iterator delete_me = i++;
207
    storage.erase(delete_me);
208
    info.used_bytes -= length;
209
    if (info.used_bytes <= shrink_to) {
210
      *used = info.used_bytes;
211
      return CVMCACHE_STATUS_OK;
212
    }
213
  }
214
215
  *used = info.used_bytes;
216
  return CVMCACHE_STATUS_PARTIAL;
217
}
218
219
static int null_listing_begin(
220
  uint64_t lst_id,
221
  enum cvmcache_object_type type)
222
{
223
  Listing lst;
224
  lst.type = type;
225
  lst.elems = new vector<Object>();
226
  for (map<ComparableHash, Object>::const_iterator i = storage.begin(),
227
       i_end = storage.end(); i != i_end; ++i)
228
  {
229
    lst.elems->push_back(i->second);
230
  }
231
  listings[lst_id] = lst;
232
  return CVMCACHE_STATUS_OK;
233
}
234
235
static int null_listing_next(
236
  int64_t listing_id,
237
  struct cvmcache_object_info *item)
238
{
239
  Listing lst = listings[listing_id];
240
  do {
241
    if (lst.pos >= lst.elems->size())
242
      return CVMCACHE_STATUS_OUTOFBOUNDS;
243
244
    vector<Object> *elems = lst.elems;
245
    if ((*elems)[lst.pos].type == lst.type) {
246
      item->id = (*elems)[lst.pos].id;
247
      item->size = (*elems)[lst.pos].data.length();
248
      item->type = (*elems)[lst.pos].type;
249
      item->pinned = (*elems)[lst.pos].refcnt > 0;
250
      item->description = (*elems)[lst.pos].description.empty()
251
                          ? NULL
252
                          : strdup((*elems)[lst.pos].description.c_str());
253
      break;
254
    }
255
    lst.pos++;
256
  } while (true);
257
  lst.pos++;
258
  listings[listing_id] = lst;
259
  return CVMCACHE_STATUS_OK;
260
}
261
262
static int null_listing_end(int64_t listing_id) {
263
  delete listings[listing_id].elems;
264
  listings.erase(listing_id);
265
  return CVMCACHE_STATUS_OK;
266
}
267
268
static void Usage(const char *progname) {
269
  printf("%s <config file>\n", progname);
270
}
271
272
273
int main(int argc, char **argv) {
274
  if (argc < 2) {
275
    Usage(argv[0]);
276
    return 1;
277
  }
278
279
  cvmcache_init_global();
280
281
  cvmcache_option_map *options = cvmcache_options_init();
282
  if (cvmcache_options_parse(options, argv[1]) != 0) {
283
    printf("cannot parse options file %s\n", argv[1]);
284
    return 1;
285
  }
286
  char *locator = cvmcache_options_get(options, "CVMFS_CACHE_PLUGIN_LOCATOR");
287
  if (locator == NULL) {
288
    printf("CVMFS_CACHE_PLUGIN_LOCATOR missing\n");
289
    cvmcache_options_fini(options);
290
    return 1;
291
  }
292
  char *test_mode = cvmcache_options_get(options, "CVMFS_CACHE_PLUGIN_TEST");
293
294
  if (!test_mode)
295
    cvmcache_spawn_watchdog(NULL);
296
297
  struct cvmcache_callbacks callbacks;
298
  memset(&callbacks, 0, sizeof(callbacks));
299
  callbacks.cvmcache_chrefcnt = null_chrefcnt;
300
  callbacks.cvmcache_obj_info = null_obj_info;
301
  callbacks.cvmcache_pread = null_pread;
302
  callbacks.cvmcache_start_txn = null_start_txn;
303
  callbacks.cvmcache_write_txn = null_write_txn;
304
  callbacks.cvmcache_commit_txn = null_commit_txn;
305
  callbacks.cvmcache_abort_txn = null_abort_txn;
306
  callbacks.cvmcache_info = null_info;
307
  callbacks.cvmcache_shrink = null_shrink;
308
  callbacks.cvmcache_listing_begin = null_listing_begin;
309
  callbacks.cvmcache_listing_next = null_listing_next;
310
  callbacks.cvmcache_listing_end = null_listing_end;
311
  callbacks.capabilities = CVMCACHE_CAP_ALL_V1;
312
313
  ctx = cvmcache_init(&callbacks);
314
  int retval = cvmcache_listen(ctx, locator);
315
  if (!retval) {
316
    fprintf(stderr, "failed to listen on %s\n", locator);
317
    return 1;
318
  }
319
320
  if (test_mode) {
321
    // Daemonize, print out PID
322
    pid_t pid;
323
    int statloc;
324
    if ((pid = fork()) == 0) {
325
      if ((pid = fork()) == 0) {
326
        int null_read = open("/dev/null", O_RDONLY);
327
        int null_write = open("/dev/null", O_WRONLY);
328
        assert((null_read >= 0) && (null_write >= 0));
329
        int retval = dup2(null_read, 0);
330
        assert(retval == 0);
331
        retval = dup2(null_write, 1);
332
        assert(retval == 1);
333
        retval = dup2(null_write, 2);
334
        assert(retval == 2);
335
        close(null_read);
336
        close(null_write);
337
      } else {
338
        assert(pid > 0);
339
        printf("%d\n", pid);
340
        fflush(stdout);
341
        fsync(1);
342
        _exit(0);
343
      }
344
    } else {
345
      assert(pid > 0);
346
      waitpid(pid, &statloc, 0);
347
      _exit(0);
348
    }
349
  }
350
351
  printf("Listening for cvmfs clients on %s\n", locator);
352
  printf("NOTE: this process needs to run as user cvmfs\n\n");
353
354
  // Starts the I/O processing thread
355
  cvmcache_process_requests(ctx, 0);
356
357
  if (test_mode)
358
    while (true) sleep(1);
359
360
  if (!cvmcache_is_supervised()) {
361
    printf("Press <R ENTER> to ask clients to release nested catalogs\n");
362
    printf("Press <Ctrl+D> to quit\n");
363
    while (true) {
364
      char buf;
365
      int retval = read(fileno(stdin), &buf, 1);
366
      if (retval != 1)
367
        break;
368
      if (buf == 'R') {
369
        printf("  ... asking clients to release nested catalogs\n");
370
        cvmcache_ask_detach(ctx);
371
      }
372
    }
373
    cvmcache_terminate(ctx);
374
  }
375
376
  cvmcache_wait_for(ctx);
377
  printf("  ... good bye\n");
378
379
  cvmcache_options_free(locator);
380
  cvmcache_options_fini(options);
381
  cvmcache_terminate_watchdog();
382
  cvmcache_cleanup_global();
383
  return 0;
384
}