GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/cache_plugin/cvmfs_cache_null.cc
Date: 2025-06-22 02:36:02
Exec Total Coverage
Lines: 0 234 0.0%
Branches: 0 82 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)))
57 < 0;
58 }
59 };
60
61 map<uint64_t, TxnInfo> transactions;
62 map<ComparableHash, Object> storage;
63 map<uint64_t, Listing> listings;
64 map<std::string, cvmcache_breadcrumb> breadcrumbs;
65
66 struct cvmcache_context *ctx;
67
68 static int null_chrefcnt(struct cvmcache_hash *id, int32_t change_by) {
69 const ComparableHash h(*id);
70 if (storage.find(h) == storage.end())
71 return CVMCACHE_STATUS_NOENTRY;
72
73 Object obj = storage[h];
74 obj.refcnt += change_by;
75 if (obj.refcnt < 0)
76 return CVMCACHE_STATUS_BADCOUNT;
77 storage[h] = obj;
78 return CVMCACHE_STATUS_OK;
79 }
80
81
82 static int null_obj_info(struct cvmcache_hash *id,
83 struct cvmcache_object_info *info) {
84 const ComparableHash h(*id);
85 if (storage.find(h) == storage.end())
86 return CVMCACHE_STATUS_NOENTRY;
87
88 const 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 const ComparableHash h(*id);
102 string data = storage[h].data;
103 if (offset > data.length())
104 return CVMCACHE_STATUS_OUTOFBOUNDS;
105 const unsigned nbytes =
106 std::min(*size, static_cast<uint32_t>(data.length() - offset));
107 memcpy(buffer, data.data() + offset, nbytes);
108 *size = nbytes;
109 return CVMCACHE_STATUS_OK;
110 }
111
112
113 static int null_start_txn(struct cvmcache_hash *id,
114 uint64_t txn_id,
115 struct cvmcache_object_info *info) {
116 Object partial_object;
117 partial_object.id = *id;
118 partial_object.type = info->type;
119 partial_object.refcnt = 1;
120 if (info->description != NULL)
121 partial_object.description = string(info->description);
122 TxnInfo txn;
123 txn.id = *id;
124 txn.partial_object = partial_object;
125 transactions[txn_id] = txn;
126 return CVMCACHE_STATUS_OK;
127 }
128
129
130 static int null_write_txn(uint64_t txn_id,
131 unsigned char *buffer,
132 uint32_t size) {
133 TxnInfo txn = transactions[txn_id];
134 txn.partial_object.data += string(reinterpret_cast<char *>(buffer), size);
135 transactions[txn_id] = txn;
136 return CVMCACHE_STATUS_OK;
137 }
138
139
140 static int null_commit_txn(uint64_t txn_id) {
141 const TxnInfo txn = transactions[txn_id];
142 const ComparableHash h(txn.id);
143 storage[h] = txn.partial_object;
144 transactions.erase(txn_id);
145 return CVMCACHE_STATUS_OK;
146 }
147
148 static int null_abort_txn(uint64_t txn_id) {
149 transactions.erase(txn_id);
150 return CVMCACHE_STATUS_OK;
151 }
152
153 static int null_info(struct cvmcache_info *info) {
154 info->size_bytes = uint64_t(-1);
155 info->used_bytes = info->pinned_bytes = 0;
156 for (map<ComparableHash, Object>::const_iterator i = storage.begin(),
157 i_end = storage.end();
158 i != i_end;
159 ++i) {
160 info->used_bytes += i->second.data.length();
161 if (i->second.refcnt > 0)
162 info->pinned_bytes += i->second.data.length();
163 }
164 info->no_shrink = 0;
165 return CVMCACHE_STATUS_OK;
166 }
167
168 static int null_shrink(uint64_t shrink_to, uint64_t *used) {
169 struct cvmcache_info info;
170 null_info(&info);
171 *used = info.used_bytes;
172 if (info.used_bytes <= shrink_to)
173 return CVMCACHE_STATUS_OK;
174
175 // Volatile objects
176 for (map<ComparableHash, Object>::iterator i = storage.begin(),
177 i_end = storage.end();
178 i != i_end;) {
179 if ((i->second.refcnt > 0)
180 || (i->second.type != CVMCACHE_OBJECT_VOLATILE)) {
181 ++i;
182 continue;
183 }
184 const unsigned length = i->second.data.length();
185 const map<ComparableHash, Object>::iterator delete_me = i++;
186 storage.erase(delete_me);
187 info.used_bytes -= length;
188 if (info.used_bytes <= shrink_to) {
189 *used = info.used_bytes;
190 return CVMCACHE_STATUS_OK;
191 }
192 }
193 // All other objects
194 for (map<ComparableHash, Object>::iterator i = storage.begin(),
195 i_end = storage.end();
196 i != i_end;) {
197 if (i->second.refcnt > 0) {
198 ++i;
199 continue;
200 }
201 const unsigned length = i->second.data.length();
202 const map<ComparableHash, Object>::iterator delete_me = i++;
203 storage.erase(delete_me);
204 info.used_bytes -= length;
205 if (info.used_bytes <= shrink_to) {
206 *used = info.used_bytes;
207 return CVMCACHE_STATUS_OK;
208 }
209 }
210
211 *used = info.used_bytes;
212 return CVMCACHE_STATUS_PARTIAL;
213 }
214
215 static int null_listing_begin(uint64_t lst_id, enum cvmcache_object_type type) {
216 Listing lst;
217 lst.type = type;
218 lst.elems = new vector<Object>();
219 for (map<ComparableHash, Object>::const_iterator i = storage.begin(),
220 i_end = storage.end();
221 i != i_end;
222 ++i) {
223 lst.elems->push_back(i->second);
224 }
225 listings[lst_id] = lst;
226 return CVMCACHE_STATUS_OK;
227 }
228
229 static int null_listing_next(int64_t listing_id,
230 struct cvmcache_object_info *item) {
231 Listing lst = listings[listing_id];
232 do {
233 if (lst.pos >= lst.elems->size())
234 return CVMCACHE_STATUS_OUTOFBOUNDS;
235
236 vector<Object> *elems = lst.elems;
237 if ((*elems)[lst.pos].type == lst.type) {
238 item->id = (*elems)[lst.pos].id;
239 item->size = (*elems)[lst.pos].data.length();
240 item->type = (*elems)[lst.pos].type;
241 item->pinned = (*elems)[lst.pos].refcnt > 0;
242 item->description = (*elems)[lst.pos].description.empty()
243 ? NULL
244 : strdup((*elems)[lst.pos].description.c_str());
245 break;
246 }
247 lst.pos++;
248 } while (true);
249 lst.pos++;
250 listings[listing_id] = lst;
251 return CVMCACHE_STATUS_OK;
252 }
253
254 static int null_listing_end(int64_t listing_id) {
255 delete listings[listing_id].elems;
256 listings.erase(listing_id);
257 return CVMCACHE_STATUS_OK;
258 }
259
260 static int null_breadcrumb_store(const char *fqrn,
261 const cvmcache_breadcrumb *breadcrumb) {
262 breadcrumbs[fqrn] = *breadcrumb;
263 return CVMCACHE_STATUS_OK;
264 }
265
266 static int null_breadcrumb_load(const char *fqrn,
267 cvmcache_breadcrumb *breadcrumb) {
268 const map<std::string, cvmcache_breadcrumb>::const_iterator itr =
269 breadcrumbs.find(fqrn);
270 if (itr == breadcrumbs.end())
271 return CVMCACHE_STATUS_NOENTRY;
272 *breadcrumb = itr->second;
273 return CVMCACHE_STATUS_OK;
274 }
275
276 static void Usage(const char *progname) {
277 printf("%s <config file>\n", progname);
278 }
279
280
281 int main(int argc, char **argv) {
282 if (argc < 2) {
283 Usage(argv[0]);
284 return 1;
285 }
286
287 cvmcache_init_global();
288
289 cvmcache_option_map *options = cvmcache_options_init();
290 if (cvmcache_options_parse(options, argv[1]) != 0) {
291 printf("cannot parse options file %s\n", argv[1]);
292 return 1;
293 }
294 char *locator = cvmcache_options_get(options, "CVMFS_CACHE_PLUGIN_LOCATOR");
295 if (locator == NULL) {
296 printf("CVMFS_CACHE_PLUGIN_LOCATOR missing\n");
297 cvmcache_options_fini(options);
298 return 1;
299 }
300 char *test_mode = cvmcache_options_get(options, "CVMFS_CACHE_PLUGIN_TEST");
301
302 if (!test_mode)
303 cvmcache_spawn_watchdog(NULL);
304
305 struct cvmcache_callbacks callbacks;
306 memset(&callbacks, 0, sizeof(callbacks));
307 callbacks.cvmcache_chrefcnt = null_chrefcnt;
308 callbacks.cvmcache_obj_info = null_obj_info;
309 callbacks.cvmcache_pread = null_pread;
310 callbacks.cvmcache_start_txn = null_start_txn;
311 callbacks.cvmcache_write_txn = null_write_txn;
312 callbacks.cvmcache_commit_txn = null_commit_txn;
313 callbacks.cvmcache_abort_txn = null_abort_txn;
314 callbacks.cvmcache_info = null_info;
315 callbacks.cvmcache_shrink = null_shrink;
316 callbacks.cvmcache_listing_begin = null_listing_begin;
317 callbacks.cvmcache_listing_next = null_listing_next;
318 callbacks.cvmcache_listing_end = null_listing_end;
319 callbacks.cvmcache_breadcrumb_store = null_breadcrumb_store;
320 callbacks.cvmcache_breadcrumb_load = null_breadcrumb_load;
321 callbacks.capabilities = CVMCACHE_CAP_ALL_V2;
322
323 ctx = cvmcache_init(&callbacks);
324 const int retval = cvmcache_listen(ctx, locator);
325 if (!retval) {
326 fprintf(stderr, "failed to listen on %s\n", locator);
327 return 1;
328 }
329
330 if (test_mode) {
331 // Daemonize, print out PID
332 pid_t pid;
333 int statloc;
334 if ((pid = fork()) == 0) {
335 if ((pid = fork()) == 0) {
336 const int null_read = open("/dev/null", O_RDONLY);
337 const int null_write = open("/dev/null", O_WRONLY);
338 assert((null_read >= 0) && (null_write >= 0));
339 int retval = dup2(null_read, 0);
340 assert(retval == 0);
341 retval = dup2(null_write, 1);
342 assert(retval == 1);
343 retval = dup2(null_write, 2);
344 assert(retval == 2);
345 close(null_read);
346 close(null_write);
347 } else {
348 assert(pid > 0);
349 printf("%d\n", pid);
350 fflush(stdout);
351 fsync(1);
352 _exit(0);
353 }
354 } else {
355 assert(pid > 0);
356 waitpid(pid, &statloc, 0);
357 _exit(0);
358 }
359 }
360
361 printf("Listening for cvmfs clients on %s\n", locator);
362 printf("NOTE: this process needs to run as user cvmfs\n\n");
363
364 // Starts the I/O processing thread
365 cvmcache_process_requests(ctx, 0);
366
367 if (test_mode)
368 while (true)
369 sleep(1);
370
371 if (!cvmcache_is_supervised()) {
372 printf("Press <R ENTER> to ask clients to release nested catalogs\n");
373 printf("Press <Ctrl+D> to quit\n");
374 while (true) {
375 char buf;
376 const int retval = read(fileno(stdin), &buf, 1);
377 if (retval != 1)
378 break;
379 if (buf == 'R') {
380 printf(" ... asking clients to release nested catalogs\n");
381 cvmcache_ask_detach(ctx);
382 }
383 }
384 cvmcache_terminate(ctx);
385 }
386
387 cvmcache_wait_for(ctx);
388 printf(" ... good bye\n");
389
390 cvmcache_options_free(locator);
391 cvmcache_options_fini(options);
392 cvmcache_terminate_watchdog();
393 cvmcache_cleanup_global();
394 return 0;
395 }
396