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