GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/quota_posix.cc
Date: 2026-02-15 02:35:50
Exec Total Coverage
Lines: 954 1391 68.6%
Branches: 749 1924 38.9%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * This module implements a "managed local cache".
5 * This way, we are able to track access times of files in the cache
6 * and remove files based on least recently used strategy.
7 *
8 * We setup another SQLite catalog, a "cache catalog", that helps us
9 * in the bookkeeping of files, file sizes and access times.
10 *
11 * We might choose to not manage the local cache. This is indicated
12 * by limit == 0 and everything succeeds in that case.
13 */
14
15 #define __STDC_LIMIT_MACROS
16 #define __STDC_FORMAT_MACROS
17
18
19 #include "quota_posix.h"
20
21 #include <dirent.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <inttypes.h>
25 #include <pthread.h>
26 #include <signal.h>
27 #include <stdint.h>
28 #include <sys/dir.h>
29 #include <sys/stat.h>
30 #include <sys/xattr.h>
31
32 #ifndef __APPLE__
33 #include <sys/statfs.h>
34 #endif
35 #include <sys/statvfs.h>
36 #include <sys/types.h>
37 #include <sys/wait.h>
38 #include <unistd.h>
39
40 #include <algorithm>
41 #include <cassert>
42 #include <cstdio>
43 #include <cstdlib>
44 #include <cstring>
45 #include <limits>
46 #include <map>
47 #include <set>
48 #include <string>
49 #include <vector>
50
51 #include "crypto/hash.h"
52 #include "duplex_sqlite3.h"
53 #include "monitor.h"
54 #include "statistics.h"
55 #include "util/concurrency.h"
56 #include "util/exception.h"
57 #include "util/logging.h"
58 #include "util/platform.h"
59 #include "util/pointer.h"
60 #include "util/posix.h"
61 #include "util/smalloc.h"
62 #include "util/string.h"
63
64 using namespace std; // NOLINT
65
66
67 736 int PosixQuotaManager::BindReturnPipe(int pipe_wronly) {
68
2/2
✓ Branch 0 taken 720 times.
✓ Branch 1 taken 16 times.
736 if (!shared_)
69 720 return pipe_wronly;
70
71 // Connect writer's end
72
1/2
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 const int result = open(
73
2/4
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 16 times.
✗ Branch 6 not taken.
32 (workspace_dir_ + "/pipe" + StringifyInt(pipe_wronly)).c_str(),
74 O_WRONLY | O_NONBLOCK);
75
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 if (result >= 0) {
76 8 Nonblock2Block(result);
77 } else {
78 8 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
79 8 "failed to bind return pipe (%d)", errno);
80 }
81 16 return result;
82 }
83
84
85 517 void PosixQuotaManager::CheckHighPinWatermark() {
86 517 const uint64_t watermark = kHighPinWatermark * cleanup_threshold_ / 100;
87
3/4
✓ Branch 0 taken 517 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✓ Branch 3 taken 493 times.
517 if ((cleanup_threshold_ > 0) && (pinned_ > watermark)) {
88 24 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
89 "high watermark of pinned files (%" PRIu64 "M > %" PRIu64 "M)",
90 24 pinned_ / (1024 * 1024), watermark / (1024 * 1024));
91
2/4
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 24 times.
✗ Branch 6 not taken.
24 BroadcastBackchannels("R"); // clients: please release pinned catalogs
92 }
93 517 }
94
95
96 void PosixQuotaManager::CleanupPipes() {
97 DIR *dirp = opendir(workspace_dir_.c_str());
98 assert(dirp != NULL);
99
100 platform_dirent64 *dent;
101 bool found_leftovers = false;
102 while ((dent = platform_readdir(dirp)) != NULL) {
103 const string name = dent->d_name;
104 const string path = workspace_dir_ + "/" + name;
105 platform_stat64 info;
106 const int retval = platform_stat(path.c_str(), &info);
107 if (retval != 0)
108 continue;
109 if (S_ISFIFO(info.st_mode) && (name.substr(0, 4) == "pipe")) {
110 if (!found_leftovers) {
111 LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogWarn,
112 "removing left-over FIFOs from cache directory");
113 }
114 found_leftovers = true;
115 unlink(path.c_str());
116 }
117 }
118 closedir(dirp);
119 }
120
121
122 /**
123 * Cleans up in data cache, until cache size is below leave_size.
124 * The actual unlinking is done in a separate process (fork).
125 *
126 * \return True on success, false otherwise
127 */
128 72 bool PosixQuotaManager::Cleanup(const uint64_t leave_size) {
129
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
72 if (!spawned_)
130 return DoCleanup(leave_size);
131
132 bool result;
133 int pipe_cleanup[2];
134
1/2
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
72 MakeReturnPipe(pipe_cleanup);
135
136 72 LruCommand cmd;
137 72 cmd.command_type = kCleanup;
138 72 cmd.size = leave_size;
139 72 cmd.return_pipe = pipe_cleanup[1];
140
141
1/2
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
72 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
142
1/2
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
72 ManagedReadHalfPipe(pipe_cleanup[0], &result, sizeof(result));
143
1/2
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
72 CloseReturnPipe(pipe_cleanup);
144
145 72 return result;
146 }
147
148
149 1172 void PosixQuotaManager::CloseDatabase() {
150
1/2
✓ Branch 0 taken 1172 times.
✗ Branch 1 not taken.
1172 if (stmt_list_catalogs_)
151 1172 sqlite3_finalize(stmt_list_catalogs_);
152
1/2
✓ Branch 0 taken 1172 times.
✗ Branch 1 not taken.
1172 if (stmt_list_pinned_)
153 1172 sqlite3_finalize(stmt_list_pinned_);
154
1/2
✓ Branch 0 taken 1172 times.
✗ Branch 1 not taken.
1172 if (stmt_list_volatile_)
155 1172 sqlite3_finalize(stmt_list_volatile_);
156
1/2
✓ Branch 0 taken 1172 times.
✗ Branch 1 not taken.
1172 if (stmt_list_)
157 1172 sqlite3_finalize(stmt_list_);
158
1/2
✓ Branch 0 taken 1172 times.
✗ Branch 1 not taken.
1172 if (stmt_lru_)
159 1172 sqlite3_finalize(stmt_lru_);
160
1/2
✓ Branch 0 taken 1172 times.
✗ Branch 1 not taken.
1172 if (stmt_rm_)
161 1172 sqlite3_finalize(stmt_rm_);
162
1/2
✓ Branch 0 taken 1172 times.
✗ Branch 1 not taken.
1172 if (stmt_rm_batch_)
163 1172 sqlite3_finalize(stmt_rm_batch_);
164
1/2
✓ Branch 0 taken 1172 times.
✗ Branch 1 not taken.
1172 if (stmt_size_)
165 1172 sqlite3_finalize(stmt_size_);
166
1/2
✓ Branch 0 taken 1172 times.
✗ Branch 1 not taken.
1172 if (stmt_touch_)
167 1172 sqlite3_finalize(stmt_touch_);
168
1/2
✓ Branch 0 taken 1172 times.
✗ Branch 1 not taken.
1172 if (stmt_unpin_)
169 1172 sqlite3_finalize(stmt_unpin_);
170
1/2
✓ Branch 0 taken 1172 times.
✗ Branch 1 not taken.
1172 if (stmt_block_)
171 1172 sqlite3_finalize(stmt_block_);
172
1/2
✓ Branch 0 taken 1172 times.
✗ Branch 1 not taken.
1172 if (stmt_unblock_)
173 1172 sqlite3_finalize(stmt_unblock_);
174
1/2
✓ Branch 0 taken 1172 times.
✗ Branch 1 not taken.
1172 if (stmt_new_)
175 1172 sqlite3_finalize(stmt_new_);
176
1/2
✓ Branch 0 taken 1172 times.
✗ Branch 1 not taken.
1172 if (database_)
177 1172 sqlite3_close(database_);
178 1172 UnlockFile(fd_lock_cachedb_);
179
180 1172 stmt_list_catalogs_ = NULL;
181 1172 stmt_list_pinned_ = NULL;
182 1172 stmt_list_volatile_ = NULL;
183 1172 stmt_list_ = NULL;
184 1172 stmt_rm_ = NULL;
185 1172 stmt_rm_batch_ = NULL;
186 1172 stmt_size_ = NULL;
187 1172 stmt_touch_ = NULL;
188 1172 stmt_unpin_ = NULL;
189 1172 stmt_block_ = NULL;
190 1172 stmt_unblock_ = NULL;
191 1172 stmt_new_ = NULL;
192 1172 database_ = NULL;
193
194 1172 pinned_chunks_.clear();
195 1172 }
196
197
198 696 void PosixQuotaManager::CloseReturnPipe(int pipe[2]) {
199
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 680 times.
696 if (shared_) {
200 16 close(pipe[0]);
201 16 UnlinkReturnPipe(pipe[1]);
202 } else {
203 680 ClosePipe(pipe);
204 }
205 696 }
206
207
208 801104 bool PosixQuotaManager::Contains(const string &hash_str) {
209 801104 bool result = false;
210
211 801104 sqlite3_bind_text(stmt_size_, 1, &hash_str[0], hash_str.length(),
212 SQLITE_STATIC);
213
2/2
✓ Branch 1 taken 435 times.
✓ Branch 2 taken 800669 times.
801104 if (sqlite3_step(stmt_size_) == SQLITE_ROW)
214 435 result = true;
215 801104 sqlite3_reset(stmt_size_);
216 801104 LogCvmfs(kLogQuota, kLogDebug, "contains %s returns %d", hash_str.c_str(),
217 result);
218
219 801104 return result;
220 }
221
222
223 1157 void PosixQuotaManager::CheckFreeSpace() {
224
3/4
✓ Branch 0 taken 1157 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 1149 times.
1157 if ((limit_ == 0) || (gauge_ >= limit_))
225 8 return;
226
227 struct statvfs vfs_info;
228
1/2
✓ Branch 1 taken 1149 times.
✗ Branch 2 not taken.
1149 const int retval = statvfs((cache_dir_ + "/cachedb").c_str(), &vfs_info);
229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1149 times.
1149 if (retval != 0) {
230 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
231 "failed to query %s for free space (%d)", cache_dir_.c_str(),
232 errno);
233 return;
234 }
235 1149 const int64_t free_space_byte = vfs_info.f_bavail * vfs_info.f_bsize;
236
1/2
✓ Branch 1 taken 1149 times.
✗ Branch 2 not taken.
1149 LogCvmfs(kLogQuota, kLogDebug, "free space: %" PRId64 " MB",
237 free_space_byte / (1024 * 1024));
238
239 1149 const int64_t required_byte = limit_ - gauge_;
240
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1149 times.
1149 if (free_space_byte < required_byte) {
241 LogCvmfs(kLogQuota, kLogSyslogWarn,
242 "too little free space on the file system hosting the cache,"
243 " %" PRId64 " MB available",
244 free_space_byte / (1024 * 1024));
245 }
246 }
247
248
249 1181 PosixQuotaManager *PosixQuotaManager::Create(const string &cache_workspace,
250 const uint64_t limit,
251 const uint64_t cleanup_threshold,
252 const bool rebuild_database) {
253
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1165 times.
1181 if (cleanup_threshold >= limit) {
254 16 LogCvmfs(kLogQuota, kLogDebug,
255 "invalid parameters: limit %" PRIu64 ", "
256 "cleanup_threshold %" PRIu64,
257 limit, cleanup_threshold);
258 16 return NULL;
259 }
260
261 PosixQuotaManager *quota_manager = new PosixQuotaManager(
262
1/2
✓ Branch 2 taken 1165 times.
✗ Branch 3 not taken.
1165 limit, cleanup_threshold, cache_workspace);
263
264 // Initialize cache catalog
265
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 1157 times.
1165 if (!quota_manager->InitDatabase(rebuild_database)) {
266
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 delete quota_manager;
267 8 return NULL;
268 }
269 1157 quota_manager->CheckFreeSpace();
270 1157 MakePipe(quota_manager->pipe_lru_);
271
272 1157 quota_manager->protocol_revision_ = kProtocolRevision;
273 1157 quota_manager->initialized_ = true;
274 1157 return quota_manager;
275 }
276
277
278 /**
279 * Connects to a running shared local quota manager. Creates one if necessary.
280 */
281 16 PosixQuotaManager *PosixQuotaManager::CreateShared(
282 const std::string &exe_path,
283 const std::string &cache_workspace,
284 const uint64_t limit,
285 const uint64_t cleanup_threshold,
286 bool foreground) {
287 16 string cache_dir;
288 16 string workspace_dir;
289
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
16 ParseDirectories(cache_workspace, &cache_dir, &workspace_dir);
290
291 pid_t new_cachemgr_pid;
292
293 // Create lock file: only one fuse client at a time
294
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
16 const int fd_lockfile = LockFile(workspace_dir + "/lock_cachemgr");
295
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 if (fd_lockfile < 0) {
296
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 LogCvmfs(kLogQuota, kLogDebug, "could not open lock file %s (%d)",
297
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
16 (workspace_dir + "/lock_cachemgr").c_str(), errno);
298 8 return NULL;
299 }
300
301 PosixQuotaManager *quota_mgr = new PosixQuotaManager(limit, cleanup_threshold,
302
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 cache_workspace);
303 8 quota_mgr->shared_ = true;
304 8 quota_mgr->spawned_ = true;
305
306 // Try to connect to pipe
307
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 const string fifo_path = workspace_dir + "/cachemgr";
308
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 LogCvmfs(kLogQuota, kLogDebug, "trying to connect to existing pipe");
309
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 quota_mgr->pipe_lru_[1] = open(fifo_path.c_str(), O_WRONLY | O_NONBLOCK);
310
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (quota_mgr->pipe_lru_[1] >= 0) {
311 const int fd_lockfile_rw = open((workspace_dir + "/lock_cachemgr").c_str(),
312 O_RDWR, 0600);
313 unsigned lockfile_magicnumber = 0;
314 const ssize_t result_mn = SafeRead(fd_lockfile_rw, &lockfile_magicnumber,
315 sizeof(lockfile_magicnumber));
316 const ssize_t result = SafeRead(fd_lockfile_rw, &new_cachemgr_pid,
317 sizeof(new_cachemgr_pid));
318 close(fd_lockfile_rw);
319
320 if ((lockfile_magicnumber != kLockFileMagicNumber) || (result < 0)
321 || (result_mn < 0)
322 || (static_cast<size_t>(result) < sizeof(new_cachemgr_pid))) {
323 if (result != 0) {
324 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
325 "could not read cache manager pid from lockfile");
326 UnlockFile(fd_lockfile);
327 delete quota_mgr;
328 return NULL;
329 } else {
330 // support reload from old versions of the cache manager
331 // lock file is empty in this case, try a plain ReadHalfPipe to get pid
332 quota_mgr->SetCacheMgrPid(quota_mgr->GetPid());
333 }
334 } else {
335 quota_mgr->SetCacheMgrPid(new_cachemgr_pid);
336 }
337
338
339 LogCvmfs(kLogQuota, kLogDebug, "connected to existing cache manager pipe");
340 quota_mgr->initialized_ = true;
341 Nonblock2Block(quota_mgr->pipe_lru_[1]);
342 UnlockFile(fd_lockfile);
343 quota_mgr->GetLimits(&quota_mgr->limit_, &quota_mgr->cleanup_threshold_);
344 LogCvmfs(kLogQuota, kLogDebug,
345 "received limit %" PRIu64 ", threshold %" PRIu64,
346 quota_mgr->limit_, quota_mgr->cleanup_threshold_);
347 if (FileExists(workspace_dir + "/cachemgr.protocol")) {
348 quota_mgr->protocol_revision_ = quota_mgr->GetProtocolRevision();
349 LogCvmfs(kLogQuota, kLogDebug, "connected protocol revision %u",
350 quota_mgr->protocol_revision_);
351 } else {
352 LogCvmfs(kLogQuota, kLogDebug, "connected to ancient cache manager");
353 }
354 return quota_mgr;
355 }
356 8 const int connect_error = errno;
357
358 // Lock file: let existing cache manager finish first
359
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 const int fd_lockfile_fifo = LockFile(workspace_dir + "/lock_cachemgr.fifo");
360
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (fd_lockfile_fifo < 0) {
361 LogCvmfs(kLogQuota, kLogDebug, "could not open lock file %s (%d)",
362 (workspace_dir + "/lock_cachemgr.fifo").c_str(), errno);
363 UnlockFile(fd_lockfile);
364 delete quota_mgr;
365 return NULL;
366 }
367
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 UnlockFile(fd_lockfile_fifo);
368
369
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (connect_error == ENXIO) {
370 LogCvmfs(kLogQuota, kLogDebug, "left-over FIFO found, unlinking");
371 unlink(fifo_path.c_str());
372 }
373
374 // Creating a new FIFO for the cache manager (to be bound later)
375 8 int retval = mkfifo(fifo_path.c_str(), 0600);
376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (retval != 0) {
377 LogCvmfs(kLogQuota, kLogDebug, "failed to create cache manager FIFO (%d)",
378 errno);
379 UnlockFile(fd_lockfile);
380 delete quota_mgr;
381 return NULL;
382 }
383
384 // Create new cache manager
385 int pipe_boot[2];
386 int pipe_handshake[2];
387
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 MakePipe(pipe_boot);
388
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 MakePipe(pipe_handshake);
389
390 8 vector<string> command_line;
391
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 command_line.push_back(exe_path);
392
2/4
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
8 command_line.push_back("__cachemgr__");
393
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 command_line.push_back(cache_workspace);
394
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 command_line.push_back(StringifyInt(pipe_boot[1]));
395
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 command_line.push_back(StringifyInt(pipe_handshake[0]));
396
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 command_line.push_back(StringifyInt(limit));
397
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 command_line.push_back(StringifyInt(cleanup_threshold));
398 // do not propagate foreground in order to reliably get pid from exec
399 // instead, daemonize right here
400
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 command_line.push_back(StringifyInt(true)); // foreground
401
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
8 command_line.push_back(StringifyInt(GetLogSyslogLevel()));
402
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
8 command_line.push_back(StringifyInt(GetLogSyslogFacility()));
403
5/14
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 8 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
8 command_line.push_back(GetLogDebugFile() + ":" + GetLogMicroSyslog());
404
405 8 set<int> preserve_filedes;
406
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 preserve_filedes.insert(0);
407
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 preserve_filedes.insert(1);
408
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 preserve_filedes.insert(2);
409
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 preserve_filedes.insert(pipe_boot[1]);
410
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 preserve_filedes.insert(pipe_handshake[0]);
411
412
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (foreground) {
413
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 retval = ManagedExec(command_line, preserve_filedes, map<int, int>(),
414 /*drop_credentials*/ false,
415 /*clear_env*/ false,
416 /*double_fork*/ true, &new_cachemgr_pid);
417 } else {
418 retval = ExecAsDaemon(command_line, &new_cachemgr_pid);
419 }
420
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (!retval) {
421 UnlockFile(fd_lockfile);
422 ClosePipe(pipe_boot);
423 ClosePipe(pipe_handshake);
424 delete quota_mgr;
425 LogCvmfs(kLogQuota, kLogDebug, "failed to start cache manager");
426 return NULL;
427 }
428
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 LogCvmfs(kLogQuota, kLogDebug, "new cache manager pid: %d", new_cachemgr_pid);
429 8 quota_mgr->SetCacheMgrPid(new_cachemgr_pid);
430
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
8 const int fd_lockfile_rw = open((workspace_dir + "/lock_cachemgr").c_str(),
431 O_RDWR | O_TRUNC, 0600);
432 8 const unsigned magic_number = PosixQuotaManager::kLockFileMagicNumber;
433
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 const bool result_mn = SafeWrite(fd_lockfile_rw, &magic_number,
434 sizeof(magic_number));
435
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 const bool result = SafeWrite(fd_lockfile_rw, &new_cachemgr_pid,
436 sizeof(new_cachemgr_pid));
437
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if (!result || !result_mn) {
438 PANIC(kLogSyslogErr, "could not write cache manager pid to lockfile");
439 }
440
441
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 close(fd_lockfile_rw);
442 // Wait for cache manager to be ready
443
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 close(pipe_boot[1]);
444
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 close(pipe_handshake[0]);
445 char buf;
446
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 if (read(pipe_boot[0], &buf, 1) != 1) {
447
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 UnlockFile(fd_lockfile);
448
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 close(pipe_boot[0]);
449
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 close(pipe_handshake[1]);
450
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 delete quota_mgr;
451
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
452 "cache manager did not start");
453 8 return NULL;
454 }
455 close(pipe_boot[0]);
456
457 // Connect write end
458 quota_mgr->pipe_lru_[1] = open(fifo_path.c_str(), O_WRONLY | O_NONBLOCK);
459 if (quota_mgr->pipe_lru_[1] < 0) {
460 LogCvmfs(kLogQuota, kLogDebug,
461 "failed to connect to newly created FIFO (%d)", errno);
462 close(pipe_handshake[1]);
463 UnlockFile(fd_lockfile);
464 delete quota_mgr;
465 return NULL;
466 }
467
468 // Finalize handshake
469 buf = 'C';
470 if (write(pipe_handshake[1], &buf, 1) != 1) {
471 UnlockFile(fd_lockfile);
472 close(pipe_handshake[1]);
473 LogCvmfs(kLogQuota, kLogDebug, "could not finalize handshake");
474 delete quota_mgr;
475 return NULL;
476 }
477 close(pipe_handshake[1]);
478
479 Nonblock2Block(quota_mgr->pipe_lru_[1]);
480 LogCvmfs(kLogQuota, kLogDebug, "connected to a new cache manager");
481 quota_mgr->protocol_revision_ = kProtocolRevision;
482
483 UnlockFile(fd_lockfile);
484
485 quota_mgr->initialized_ = true;
486 quota_mgr->GetLimits(&quota_mgr->limit_, &quota_mgr->cleanup_threshold_);
487 LogCvmfs(kLogQuota, kLogDebug,
488 "received limit %" PRIu64 ", "
489 "threshold %" PRIu64,
490 quota_mgr->limit_, quota_mgr->cleanup_threshold_);
491 return quota_mgr;
492 16 }
493
494
495 80 bool PosixQuotaManager::DoCleanup(const uint64_t leave_size) {
496
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 64 times.
80 if (gauge_ <= leave_size)
497 16 return true;
498
499 // TODO(jblomer) transaction
500
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 LogCvmfs(kLogQuota, kLogSyslog | kLogDebug,
501 "clean up cache until at most %lu KB is used", leave_size / 1024);
502
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 LogCvmfs(kLogQuota, kLogDebug, "gauge %" PRIu64, gauge_);
503
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 cleanup_recorder_.Tick();
504
505 bool result;
506 64 vector<string> trash;
507
508 // Note that volatile files start counting from the smallest int64 number:
509 // the absolute sequence number with the first bit set in two's complement.
510 // So -1 can be a marker that will never appear in the database.
511 64 int64_t max_acseq = -1;
512 64 std::vector<EvictCandidate> lru_ordered_open;
513
514 do {
515
1/2
✓ Branch 1 taken 464 times.
✗ Branch 2 not taken.
464 sqlite3_reset(stmt_lru_);
516
3/4
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 400 times.
✓ Branch 3 taken 464 times.
✗ Branch 4 not taken.
528 sqlite3_bind_int64(stmt_lru_, 1,
517 64 (max_acseq == -1) ? std::numeric_limits<int64_t>::min()
518 : (max_acseq + 1));
519
520 464 std::vector<EvictCandidate> candidates;
521
1/2
✓ Branch 1 taken 464 times.
✗ Branch 2 not taken.
464 candidates.reserve(kEvictBatchSize);
522 464 string hash_str;
523 464 unsigned i = 0;
524
3/4
✓ Branch 1 taken 416544 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 416080 times.
✓ Branch 4 taken 464 times.
416544 while (sqlite3_step(stmt_lru_) == SQLITE_ROW) {
525 hash_str = reinterpret_cast<const char *>(
526
2/4
✓ Branch 1 taken 416080 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 416080 times.
✗ Branch 5 not taken.
416080 sqlite3_column_text(stmt_lru_, 0));
527
1/2
✓ Branch 2 taken 416080 times.
✗ Branch 3 not taken.
416080 LogCvmfs(kLogQuota, kLogDebug, "add %s to candidates for eviction",
528 hash_str.c_str());
529
1/2
✓ Branch 1 taken 416080 times.
✗ Branch 2 not taken.
416080 candidates.push_back(
530
1/2
✓ Branch 1 taken 416080 times.
✗ Branch 2 not taken.
416080 EvictCandidate(shash::MkFromHexPtr(shash::HexPtr(hash_str)),
531 416080 sqlite3_column_int64(stmt_lru_, 1),
532
2/4
✓ Branch 1 taken 416080 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 416080 times.
✗ Branch 5 not taken.
416080 sqlite3_column_int64(stmt_lru_, 2)));
533 416080 i++;
534 }
535
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 456 times.
464 if (candidates.empty()) {
536
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 LogCvmfs(kLogQuota, kLogDebug, "no more entries to evict");
537 8 break;
538 }
539
540 456 const unsigned N = candidates.size();
541
542 456 open_files_.clear();
543
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 456 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
912 open_files_ = (cleanup_unused_first_) ? CollectAllOpenHashes()
544 456 : std::vector<shash::Short>();
545
546
2/2
✓ Branch 0 taken 400072 times.
✓ Branch 1 taken 400 times.
400472 for (i = 0; i < N; ++i) {
547 // That's a critical condition. We must not delete a not yet inserted
548 // pinned file as it is already reserved (but will be inserted later).
549 // Instead, set the pin bit in the db to not run into an endless loop
550
1/2
✓ Branch 2 taken 400072 times.
✗ Branch 3 not taken.
400072 const bool is_pinned = pinned_chunks_.find(candidates[i].hash)
551 800144 != pinned_chunks_.end();
552
553 // Avoid evicting open files hopping there are enough more recently used
554 // files to satisfy the cleanup request
555 /*
556 const bool is_open = std::find_if(
557 open_files_.begin(), open_files_.end(),
558 [&candidates, &i](const auto &elem) -> bool {
559 return elem.Collide(candidates[i].hash);
560 })
561 != open_files_.end();
562 */
563 400072 bool is_open=false;
564
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 400072 times.
400072 for(auto it=open_files_.begin();it!=open_files_.end();++it){
565 if (it->Collide(candidates[i].hash)){is_open=true; break;}
566 }
567
568
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 400064 times.
400072 if (is_pinned) {
569
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 SkipEviction(candidates[i]);
570 8 continue;
571 }
572
573
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 400064 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
400064 if (cleanup_unused_first_ and is_open) {
574 SkipEviction(candidates[i]);
575 lru_ordered_open.push_back(candidates[i]);
576 continue;
577 }
578
579
2/4
✓ Branch 1 taken 400064 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 400064 times.
✗ Branch 5 not taken.
800128 trash.push_back(cache_dir_ + "/"
580
2/4
✓ Branch 2 taken 400064 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 400064 times.
✗ Branch 6 not taken.
1200192 + candidates[i].hash.MakePathWithoutSuffix());
581 400064 gauge_ -= candidates[i].size;
582 400064 max_acseq = candidates[i].acseq;
583
1/2
✓ Branch 2 taken 400064 times.
✗ Branch 3 not taken.
400064 LogCvmfs(kLogQuota, kLogDebug, "lru cleanup %s, new gauge %" PRIu64,
584
1/2
✓ Branch 2 taken 400064 times.
✗ Branch 3 not taken.
800128 candidates[i].hash.ToString().c_str(), gauge_);
585
586
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 400008 times.
400064 if (gauge_ <= leave_size)
587 56 break;
588 }
589
6/6
✓ Branch 1 taken 456 times.
✓ Branch 2 taken 8 times.
✓ Branch 4 taken 456 times.
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 400 times.
✓ Branch 7 taken 56 times.
928 } while (gauge_ > leave_size);
590
591
1/2
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
64 if (max_acseq != -1) {
592
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 sqlite3_bind_int64(stmt_rm_batch_, 1, max_acseq);
593
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 result = (sqlite3_step(stmt_rm_batch_) == SQLITE_DONE);
594
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
64 assert(result);
595
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 sqlite3_reset(stmt_rm_batch_);
596
597
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 result = (sqlite3_step(stmt_unblock_) == SQLITE_DONE);
598
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 sqlite3_reset(stmt_unblock_);
599
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
64 assert(result);
600 }
601
602
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 64 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 64 times.
64 while (!lru_ordered_open.empty() and gauge_ > leave_size) {
603 // cleanup files in use
604 auto &candidate = lru_ordered_open[0];
605 trash.push_back(cache_dir_ + "/" + candidate.hash.MakePathWithoutSuffix());
606 gauge_ -= candidate.size;
607 max_acseq = candidate.acseq;
608 LogCvmfs(kLogQuota, kLogDebug, "lru cleanup %s, new gauge %" PRIu64,
609 candidate.hash.ToString().c_str(), gauge_);
610 lru_ordered_open.erase(lru_ordered_open.begin());
611 }
612
613
2/4
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 64 times.
64 if (!EmptyTrash(trash))
614 return false;
615
616
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 56 times.
64 if (gauge_ > leave_size) {
617
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
618 "request to clean until %" PRIu64 ", "
619 "but effective gauge is %" PRIu64,
620 leave_size, gauge_);
621 8 return false;
622 }
623 56 return true;
624 64 }
625
626 64 bool PosixQuotaManager::EmptyTrash(const std::vector<std::string> &trash) {
627
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 64 times.
64 if (trash.empty())
628 return true;
629
630
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 16 times.
64 if (async_delete_) {
631 // Double fork avoids zombie, forked removal process must not flush file
632 // buffers
633 pid_t pid;
634 int statloc;
635
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 48 times.
48 if ((pid = fork()) == 0) {
636 // TODO(jblomer): eviciting files in the cache should perhaps become a
637 // thread. This would also allow to block the chunks and prevent the
638 // race with re-insertion. Then again, a thread can block umount.
639 #ifndef DEBUGMSG
640 CloseAllFildes(std::set<int>());
641 #endif
642 if (fork() == 0) {
643 for (unsigned i = 0, iEnd = trash.size(); i < iEnd; ++i) {
644 LogCvmfs(kLogQuota, kLogDebug, "unlink %s", trash[i].c_str());
645 unlink(trash[i].c_str());
646 }
647 _exit(0);
648 }
649 _exit(0);
650 } else {
651
1/2
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
48 if (pid > 0)
652
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 waitpid(pid, &statloc, 0);
653 else
654 return false;
655 }
656 } else { // !async_delete_
657
2/2
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 16 times.
40 for (unsigned i = 0, iEnd = trash.size(); i < iEnd; ++i) {
658 24 LogCvmfs(kLogQuota, kLogDebug, "unlink %s", trash[i].c_str());
659 24 unlink(trash[i].c_str());
660 }
661 }
662 64 return true;
663 }
664
665
666 800524 void PosixQuotaManager::DoInsert(const shash::Any &hash,
667 const uint64_t size,
668 const string &description,
669 const CommandType command_type) {
670
1/2
✓ Branch 1 taken 800524 times.
✗ Branch 2 not taken.
800524 const string hash_str = hash.ToString();
671
1/2
✓ Branch 3 taken 800524 times.
✗ Branch 4 not taken.
800524 LogCvmfs(kLogQuota, kLogDebug, "insert into lru %s, path %s, method %d",
672 hash_str.c_str(), description.c_str(), command_type);
673 800524 const unsigned desc_length = (description.length() > kMaxDescription)
674 ? kMaxDescription
675
1/2
✓ Branch 0 taken 800524 times.
✗ Branch 1 not taken.
800524 : description.length();
676
677 LruCommand *cmd = reinterpret_cast<LruCommand *>(
678 800524 alloca(sizeof(LruCommand) + desc_length));
679 800524 new (cmd) LruCommand;
680 800524 cmd->command_type = command_type;
681 800524 cmd->SetSize(size);
682
1/2
✓ Branch 1 taken 800524 times.
✗ Branch 2 not taken.
800524 cmd->StoreHash(hash);
683 800524 cmd->desc_length = desc_length;
684 800524 memcpy(reinterpret_cast<char *>(cmd) + sizeof(LruCommand), &description[0],
685 desc_length);
686
1/2
✓ Branch 1 taken 800524 times.
✗ Branch 2 not taken.
800524 WritePipe(pipe_lru_[1], cmd, sizeof(LruCommand) + desc_length);
687 800524 }
688
689
690 280 vector<string> PosixQuotaManager::DoList(const CommandType list_command) {
691 280 vector<string> result;
692
693 int pipe_list[2];
694
1/2
✓ Branch 1 taken 280 times.
✗ Branch 2 not taken.
280 MakeReturnPipe(pipe_list);
695 char description_buffer[kMaxDescription];
696
697 280 LruCommand cmd;
698 280 cmd.command_type = list_command;
699 280 cmd.return_pipe = pipe_list[1];
700
1/2
✓ Branch 1 taken 280 times.
✗ Branch 2 not taken.
280 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
701
702 int length;
703 do {
704
1/2
✓ Branch 1 taken 800592 times.
✗ Branch 2 not taken.
800592 ManagedReadHalfPipe(pipe_list[0], &length, sizeof(length));
705
2/2
✓ Branch 0 taken 800312 times.
✓ Branch 1 taken 280 times.
800592 if (length > 0) {
706
1/2
✓ Branch 1 taken 800312 times.
✗ Branch 2 not taken.
800312 ReadPipe(pipe_list[0], description_buffer, length);
707
2/4
✓ Branch 2 taken 800312 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 800312 times.
✗ Branch 6 not taken.
800312 result.push_back(string(description_buffer, length));
708 }
709
2/2
✓ Branch 0 taken 800312 times.
✓ Branch 1 taken 280 times.
800592 } while (length >= 0);
710
711
1/2
✓ Branch 1 taken 280 times.
✗ Branch 2 not taken.
280 CloseReturnPipe(pipe_list);
712 560 return result;
713 }
714
715
716 741 uint64_t PosixQuotaManager::GetCapacity() {
717
1/2
✓ Branch 0 taken 741 times.
✗ Branch 1 not taken.
741 if (limit_ != (uint64_t)(-1))
718 741 return limit_;
719
720 // Unrestricted cache, look at free space on cache dir fs
721 struct statfs info;
722 if (statfs(".", &info) == 0) {
723 return info.f_bavail * info.f_bsize;
724 } else {
725 LogCvmfs(kLogQuota, kLogSyslogErr | kLogDebug,
726 "failed to query file system info of cache (%d)", errno);
727 return limit_;
728 }
729 }
730
731
732 void PosixQuotaManager::GetLimits(uint64_t *limit,
733 uint64_t *cleanup_threshold) {
734 int pipe_limits[2];
735 MakeReturnPipe(pipe_limits);
736
737 LruCommand cmd;
738 cmd.command_type = kLimits;
739 cmd.return_pipe = pipe_limits[1];
740 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
741 ManagedReadHalfPipe(pipe_limits[0], limit, sizeof(*limit));
742 ReadPipe(pipe_limits[0], cleanup_threshold, sizeof(*cleanup_threshold));
743 CloseReturnPipe(pipe_limits);
744 }
745
746
747 /**
748 * Since we only cleanup until cleanup_threshold, we can only add
749 * files smaller than limit-cleanup_threshold.
750 */
751 231 uint64_t PosixQuotaManager::GetMaxFileSize() {
752 231 return limit_ - cleanup_threshold_;
753 }
754
755
756 8 pid_t PosixQuotaManager::GetPid() {
757
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
8 if (!shared_ || !spawned_) {
758 8 return getpid();
759 }
760 if (cachemgr_pid_) {
761 return cachemgr_pid_;
762 }
763
764 pid_t result;
765 int pipe_pid[2];
766 MakeReturnPipe(pipe_pid);
767
768 LruCommand cmd;
769 cmd.command_type = kPid;
770 cmd.return_pipe = pipe_pid[1];
771 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
772 ReadHalfPipe(pipe_pid[0], &result, sizeof(result));
773 CloseReturnPipe(pipe_pid);
774 return result;
775 }
776
777
778 8 uint32_t PosixQuotaManager::GetProtocolRevision() {
779 int pipe_revision[2];
780
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 MakeReturnPipe(pipe_revision);
781
782 8 LruCommand cmd;
783 8 cmd.command_type = kGetProtocolRevision;
784 8 cmd.return_pipe = pipe_revision[1];
785
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
786
787 uint32_t revision;
788
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 ManagedReadHalfPipe(pipe_revision[0], &revision, sizeof(revision));
789
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 CloseReturnPipe(pipe_revision);
790 8 return revision;
791 }
792
793 733 void PosixQuotaManager::SetCleanupPolicy(bool cleanup_unused_first) {
794 733 LruCommand cmd;
795 733 cmd.command_type = kSetCleanupPolicy;
796
1/2
✓ Branch 1 taken 733 times.
✗ Branch 2 not taken.
733 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
797
798
1/2
✓ Branch 1 taken 733 times.
✗ Branch 2 not taken.
733 WritePipe(pipe_lru_[1], &cleanup_unused_first, sizeof(bool));
799 733 }
800
801 void PosixQuotaManager::RegisterMountpoint(const std::string &mountpoint) {
802 LruCommand cmd;
803 cmd.command_type = kRegisterMountpoint;
804 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
805
806 size_t mp_size = mountpoint.size();
807 WritePipe(pipe_lru_[1], &mp_size, sizeof(size_t));
808 WritePipe(pipe_lru_[1], mountpoint.data(), mp_size);
809 }
810
811 std::string PosixQuotaManager::GetMountpoints() {
812 int pipe_mp[2];
813 MakeReturnPipe(pipe_mp);
814
815 LruCommand cmd;
816 cmd.command_type = kGetMountpoints;
817 cmd.return_pipe = pipe_mp[1];
818 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
819 size_t mp_str_size = 0;
820 ManagedReadHalfPipe(pipe_mp[0], &mp_str_size, sizeof(size_t));
821 char *buf = (char *)malloc(mp_str_size * sizeof(char));
822 ManagedReadHalfPipe(pipe_mp[0], buf, mp_str_size);
823 return std::string(buf);
824 }
825
826 std::string PosixQuotaManager::GetGroupHashes() {
827 int pipe_gh[2];
828 MakeReturnPipe(pipe_gh);
829
830 LruCommand cmd;
831 cmd.command_type = kGetGroupHashes;
832 cmd.return_pipe = pipe_gh[1];
833 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
834 size_t mp_str_size = 0;
835 ManagedReadHalfPipe(pipe_gh[0], &mp_str_size, sizeof(size_t));
836 char *buf = (char *)malloc(mp_str_size * sizeof(char));
837 ManagedReadHalfPipe(pipe_gh[0], buf, mp_str_size);
838 return std::string(buf);
839 }
840
841 /**
842 * Queries the shared local hard disk quota manager.
843 */
844 144 void PosixQuotaManager::GetSharedStatus(uint64_t *gauge, uint64_t *pinned) {
845 int pipe_status[2];
846
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 MakeReturnPipe(pipe_status);
847
848 144 LruCommand cmd;
849 144 cmd.command_type = kStatus;
850 144 cmd.return_pipe = pipe_status[1];
851
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
852
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 ManagedReadHalfPipe(pipe_status[0], gauge, sizeof(*gauge));
853
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 ReadPipe(pipe_status[0], pinned, sizeof(*pinned));
854
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 CloseReturnPipe(pipe_status);
855 144 }
856
857 8 bool PosixQuotaManager::SetSharedLimit(uint64_t limit) {
858 int pipe_set_limit[2];
859 bool result;
860
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 MakeReturnPipe(pipe_set_limit);
861
862 8 LruCommand cmd;
863 8 cmd.command_type = kSetLimit;
864 8 cmd.size = limit;
865 8 cmd.return_pipe = pipe_set_limit[1];
866
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
867
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 ReadHalfPipe(pipe_set_limit[0], &result, sizeof(result));
868
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 CloseReturnPipe(pipe_set_limit);
869 8 return result;
870 }
871
872
873 8 bool PosixQuotaManager::SetLimit(uint64_t size) {
874
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (!spawned_) {
875 limit_ = size;
876 cleanup_threshold_ = size / 2;
877 LogCvmfs(kLogQuota, kLogDebug | kLogSyslog,
878 "Quota limit set to %lu / threshold %lu", limit_,
879 cleanup_threshold_);
880 return true;
881 }
882 8 return SetSharedLimit(size);
883 }
884
885 1594 uint64_t PosixQuotaManager::GetSize() {
886
2/2
✓ Branch 0 taken 1466 times.
✓ Branch 1 taken 128 times.
1594 if (!spawned_)
887 1466 return gauge_;
888 uint64_t gauge, size_pinned;
889
1/2
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
128 GetSharedStatus(&gauge, &size_pinned);
890 128 return gauge;
891 }
892
893
894 16 uint64_t PosixQuotaManager::GetSizePinned() {
895
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (!spawned_)
896 return pinned_;
897 uint64_t gauge, size_pinned;
898
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 GetSharedStatus(&gauge, &size_pinned);
899 16 return size_pinned;
900 }
901
902
903 32 uint64_t PosixQuotaManager::GetCleanupRate(uint64_t period_s) {
904
2/4
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 32 times.
32 if (!spawned_ || (protocol_revision_ < 2))
905 return 0;
906 uint64_t cleanup_rate;
907
908 int pipe_cleanup_rate[2];
909
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 MakeReturnPipe(pipe_cleanup_rate);
910 32 LruCommand cmd;
911 32 cmd.command_type = kCleanupRate;
912 32 cmd.size = period_s;
913 32 cmd.return_pipe = pipe_cleanup_rate[1];
914
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
915
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 ManagedReadHalfPipe(pipe_cleanup_rate[0], &cleanup_rate,
916 sizeof(cleanup_rate));
917
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 CloseReturnPipe(pipe_cleanup_rate);
918
919 32 return cleanup_rate;
920 }
921
922
923 1205 bool PosixQuotaManager::InitDatabase(const bool rebuild_database) {
924 1205 string sql;
925 sqlite3_stmt *stmt;
926
927
2/4
✓ Branch 1 taken 1205 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1205 times.
✗ Branch 5 not taken.
1205 fd_lock_cachedb_ = LockFile(workspace_dir_ + "/lock_cachedb");
928
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1197 times.
1205 if (fd_lock_cachedb_ < 0) {
929
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 LogCvmfs(kLogQuota, kLogDebug, "failed to create cachedb lock");
930 8 return false;
931 }
932
933 1197 bool retry = false;
934
1/2
✓ Branch 1 taken 1197 times.
✗ Branch 2 not taken.
1197 const string db_file = cache_dir_ + "/cachedb";
935
2/2
✓ Branch 0 taken 1144 times.
✓ Branch 1 taken 53 times.
1197 if (rebuild_database) {
936
1/2
✓ Branch 2 taken 53 times.
✗ Branch 3 not taken.
53 LogCvmfs(kLogQuota, kLogDebug, "rebuild database, unlinking existing (%s)",
937 db_file.c_str());
938 53 unlink(db_file.c_str());
939
1/2
✓ Branch 1 taken 53 times.
✗ Branch 2 not taken.
53 unlink((db_file + "-journal").c_str());
940 }
941
942 1144 init_recover:
943
1/2
✓ Branch 2 taken 1197 times.
✗ Branch 3 not taken.
1197 int err = sqlite3_open(db_file.c_str(), &database_);
944
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1197 times.
1197 if (err != SQLITE_OK) {
945 LogCvmfs(kLogQuota, kLogDebug, "could not open cache database (%d)", err);
946 goto init_database_fail;
947 }
948 // TODO(reneme): make this a `QuotaDatabase : public sqlite::Database`
949 sql = "PRAGMA synchronous=0; PRAGMA locking_mode=EXCLUSIVE; "
950 "PRAGMA auto_vacuum=1; "
951 "CREATE TABLE IF NOT EXISTS cache_catalog (sha1 TEXT, size INTEGER, "
952 " acseq INTEGER, path TEXT, type INTEGER, pinned INTEGER, "
953 "CONSTRAINT pk_cache_catalog PRIMARY KEY (sha1)); "
954 "CREATE UNIQUE INDEX IF NOT EXISTS idx_cache_catalog_acseq "
955 " ON cache_catalog (acseq); "
956 "CREATE TEMP TABLE fscache (sha1 TEXT, size INTEGER, actime INTEGER, "
957 "CONSTRAINT pk_fscache PRIMARY KEY (sha1)); "
958 "CREATE INDEX idx_fscache_actime ON fscache (actime); "
959 "CREATE TABLE IF NOT EXISTS properties (key TEXT, value TEXT, "
960
1/2
✓ Branch 1 taken 1197 times.
✗ Branch 2 not taken.
1197 " CONSTRAINT pk_properties PRIMARY KEY(key));";
961
1/2
✓ Branch 2 taken 1197 times.
✗ Branch 3 not taken.
1197 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
962
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1197 times.
1197 if (err != SQLITE_OK) {
963 if (!retry) {
964 retry = true;
965 sqlite3_close(database_);
966 unlink(db_file.c_str());
967 unlink((db_file + "-journal").c_str());
968 LogCvmfs(kLogQuota, kLogSyslogWarn,
969 "LRU database corrupted, re-building");
970 goto init_recover;
971 }
972 LogCvmfs(kLogQuota, kLogDebug, "could not init cache database (failed: %s)",
973 sql.c_str());
974 goto init_database_fail;
975 }
976
977 // If this an old cache catalog,
978 // add and initialize new columns to cache_catalog
979 sql = "ALTER TABLE cache_catalog ADD type INTEGER; "
980
1/2
✓ Branch 1 taken 1197 times.
✗ Branch 2 not taken.
1197 "ALTER TABLE cache_catalog ADD pinned INTEGER";
981
1/2
✓ Branch 2 taken 1197 times.
✗ Branch 3 not taken.
1197 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
982
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1197 times.
1197 if (err == SQLITE_OK) {
983 sql = "UPDATE cache_catalog SET type=" + StringifyInt(kFileRegular) + ";";
984 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
985 if (err != SQLITE_OK) {
986 LogCvmfs(kLogQuota, kLogDebug,
987 "could not init cache database (failed: %s)", sql.c_str());
988 goto init_database_fail;
989 }
990 }
991
992 // Set pinned back
993
1/2
✓ Branch 1 taken 1197 times.
✗ Branch 2 not taken.
1197 sql = "UPDATE cache_catalog SET pinned=0;";
994
1/2
✓ Branch 2 taken 1197 times.
✗ Branch 3 not taken.
1197 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
995
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1197 times.
1197 if (err != SQLITE_OK) {
996 LogCvmfs(kLogQuota, kLogDebug, "could not init cache database (failed: %s)",
997 sql.c_str());
998 goto init_database_fail;
999 }
1000
1001 // Set schema version
1002 sql = "INSERT OR REPLACE INTO properties (key, value) "
1003
1/2
✓ Branch 1 taken 1197 times.
✗ Branch 2 not taken.
1197 "VALUES ('schema', '1.0')";
1004
1/2
✓ Branch 2 taken 1197 times.
✗ Branch 3 not taken.
1197 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
1005
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1197 times.
1197 if (err != SQLITE_OK) {
1006 LogCvmfs(kLogQuota, kLogDebug, "could not init cache database (failed: %s)",
1007 sql.c_str());
1008 goto init_database_fail;
1009 }
1010
1011 // If cache catalog is empty, recreate from file system
1012
1/2
✓ Branch 1 taken 1197 times.
✗ Branch 2 not taken.
1197 sql = "SELECT count(*) FROM cache_catalog;";
1013
1/2
✓ Branch 2 taken 1197 times.
✗ Branch 3 not taken.
1197 sqlite3_prepare_v2(database_, sql.c_str(), -1, &stmt, NULL);
1014
2/4
✓ Branch 1 taken 1197 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1197 times.
✗ Branch 4 not taken.
1197 if (sqlite3_step(stmt) == SQLITE_ROW) {
1015
6/8
✓ Branch 1 taken 1197 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 32 times.
✓ Branch 4 taken 1165 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 32 times.
✓ Branch 7 taken 1165 times.
✓ Branch 8 taken 32 times.
1197 if ((sqlite3_column_int64(stmt, 0)) == 0 || rebuild_database) {
1016
1/2
✓ Branch 1 taken 1165 times.
✗ Branch 2 not taken.
1165 LogCvmfs(kLogCvmfs, kLogDebug,
1017 "CernVM-FS: building lru cache database...");
1018
3/4
✓ Branch 1 taken 1165 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 24 times.
✓ Branch 4 taken 1141 times.
1165 if (!RebuildDatabase()) {
1019
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 LogCvmfs(kLogQuota, kLogDebug,
1020 "could not build cache database from file system");
1021
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 sqlite3_finalize(stmt);
1022 24 goto init_database_fail;
1023 }
1024 }
1025
1/2
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
1173 sqlite3_finalize(stmt);
1026 } else {
1027 LogCvmfs(kLogQuota, kLogDebug, "could not select on cache catalog");
1028 sqlite3_finalize(stmt);
1029 goto init_database_fail;
1030 }
1031
1032 // How many bytes do we already have in cache?
1033
1/2
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
1173 sql = "SELECT sum(size) FROM cache_catalog;";
1034
1/2
✓ Branch 2 taken 1173 times.
✗ Branch 3 not taken.
1173 sqlite3_prepare_v2(database_, sql.c_str(), -1, &stmt, NULL);
1035
2/4
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1173 times.
✗ Branch 4 not taken.
1173 if (sqlite3_step(stmt) == SQLITE_ROW) {
1036
1/2
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
1173 gauge_ = sqlite3_column_int64(stmt, 0);
1037 } else {
1038 LogCvmfs(kLogQuota, kLogDebug, "could not determine cache size");
1039 sqlite3_finalize(stmt);
1040 goto init_database_fail;
1041 }
1042
1/2
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
1173 sqlite3_finalize(stmt);
1043
1044 // Highest seq-no?
1045
1/2
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
1173 sql = "SELECT coalesce(max(acseq & (~(1<<63))), 0) FROM cache_catalog;";
1046
1/2
✓ Branch 2 taken 1173 times.
✗ Branch 3 not taken.
1173 sqlite3_prepare_v2(database_, sql.c_str(), -1, &stmt, NULL);
1047
2/4
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1173 times.
✗ Branch 4 not taken.
1173 if (sqlite3_step(stmt) == SQLITE_ROW) {
1048
1/2
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
1173 seq_ = sqlite3_column_int64(stmt, 0) + 1;
1049 } else {
1050 LogCvmfs(kLogQuota, kLogDebug, "could not determine highest seq-no");
1051 sqlite3_finalize(stmt);
1052 goto init_database_fail;
1053 }
1054
1/2
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
1173 sqlite3_finalize(stmt);
1055
1056 // Prepare touch, new, remove statements
1057
1/2
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
1173 sqlite3_prepare_v2(database_,
1058 "UPDATE cache_catalog SET acseq=:seq | (acseq&(1<<63)) "
1059 "WHERE sha1=:sha1;",
1060 -1, &stmt_touch_, NULL);
1061
1/2
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
1173 sqlite3_prepare_v2(database_,
1062 "UPDATE cache_catalog SET pinned=0 "
1063 "WHERE sha1=:sha1;",
1064 -1, &stmt_unpin_, NULL);
1065
1/2
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
1173 sqlite3_prepare_v2(database_,
1066 "UPDATE cache_catalog SET pinned=2 "
1067 "WHERE sha1=:sha1;",
1068 -1, &stmt_block_, NULL);
1069
1/2
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
1173 sqlite3_prepare_v2(database_,
1070 "UPDATE cache_catalog SET pinned=1 "
1071 "WHERE pinned=2;",
1072 -1, &stmt_unblock_, NULL);
1073
1/2
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
1173 sqlite3_prepare_v2(database_,
1074 "INSERT OR REPLACE INTO cache_catalog "
1075 "(sha1, size, acseq, path, type, pinned) "
1076 "VALUES (:sha1, :s, :seq, :p, :t, :pin);",
1077 -1, &stmt_new_, NULL);
1078
1/2
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
1173 sqlite3_prepare_v2(database_,
1079 "SELECT size, pinned FROM cache_catalog WHERE sha1=:sha1;",
1080 -1, &stmt_size_, NULL);
1081
1/2
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
1173 sqlite3_prepare_v2(database_, "DELETE FROM cache_catalog WHERE sha1=:sha1;",
1082 -1, &stmt_rm_, NULL);
1083
1/2
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
1173 sqlite3_prepare_v2(database_,
1084 "DELETE FROM cache_catalog WHERE acseq<=:a AND pinned<>2;",
1085 -1, &stmt_rm_batch_, NULL);
1086
1/2
✓ Branch 2 taken 1173 times.
✗ Branch 3 not taken.
1173 sqlite3_prepare_v2(database_,
1087
1/2
✓ Branch 2 taken 1173 times.
✗ Branch 3 not taken.
2346 (std::string("SELECT sha1, size, acseq FROM cache_catalog "
1088 "WHERE pinned<>2 AND acseq>=:a "
1089 "ORDER BY acseq ASC "
1090 "LIMIT ")
1091
3/6
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1173 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1173 times.
✗ Branch 8 not taken.
4692 + StringifyInt(kEvictBatchSize) + ";")
1092 .c_str(),
1093 -1, &stmt_lru_, NULL);
1094
1/2
✓ Branch 2 taken 1173 times.
✗ Branch 3 not taken.
1173 sqlite3_prepare_v2(database_,
1095 ("SELECT path FROM cache_catalog WHERE type="
1096
3/6
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1173 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1173 times.
✗ Branch 8 not taken.
2346 + StringifyInt(kFileRegular) + ";")
1097 .c_str(),
1098 -1, &stmt_list_, NULL);
1099
1/2
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
1173 sqlite3_prepare_v2(database_,
1100 "SELECT path FROM cache_catalog WHERE pinned<>0;", -1,
1101 &stmt_list_pinned_, NULL);
1102
1/2
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
1173 sqlite3_prepare_v2(database_,
1103 "SELECT path FROM cache_catalog WHERE acseq < 0;", -1,
1104 &stmt_list_volatile_, NULL);
1105
1/2
✓ Branch 2 taken 1173 times.
✗ Branch 3 not taken.
1173 sqlite3_prepare_v2(database_,
1106 ("SELECT path FROM cache_catalog WHERE type="
1107
3/6
✓ Branch 1 taken 1173 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1173 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1173 times.
✗ Branch 8 not taken.
2346 + StringifyInt(kFileCatalog) + ";")
1108 .c_str(),
1109 -1, &stmt_list_catalogs_, NULL);
1110 1173 return true;
1111
1112 24 init_database_fail:
1113
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 sqlite3_close(database_);
1114 24 database_ = NULL;
1115
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 UnlockFile(fd_lock_cachedb_);
1116 24 return false;
1117 1205 }
1118
1119
1120 /**
1121 * Inserts a new file into cache catalog. This file gets a new,
1122 * highest sequence number. Does cache cleanup if necessary.
1123 */
1124 800388 void PosixQuotaManager::Insert(const shash::Any &any_hash,
1125 const uint64_t size,
1126 const string &description) {
1127 800388 DoInsert(any_hash, size, description, kInsert);
1128 800388 }
1129
1130
1131 /**
1132 * Inserts a new file into cache catalog. This file is marked as volatile
1133 * and gets a new highest sequence number with the first bit set. Cache cleanup
1134 * treats these files with priority.
1135 */
1136 32 void PosixQuotaManager::InsertVolatile(const shash::Any &any_hash,
1137 const uint64_t size,
1138 const string &description) {
1139 32 DoInsert(any_hash, size, description, kInsertVolatile);
1140 32 }
1141
1142
1143 /**
1144 * Lists all path names from the cache db.
1145 */
1146 168 vector<string> PosixQuotaManager::List() { return DoList(kList); }
1147
1148
1149 /**
1150 * Lists all pinned files from the cache db.
1151 */
1152 64 vector<string> PosixQuotaManager::ListPinned() { return DoList(kListPinned); }
1153
1154
1155 /**
1156 * Lists all sqlite catalog files from the cache db.
1157 */
1158 24 vector<string> PosixQuotaManager::ListCatalogs() {
1159 24 return DoList(kListCatalogs);
1160 }
1161
1162
1163 /**
1164 * Lists only files flagged as volatile (priority removal)
1165 */
1166 24 vector<string> PosixQuotaManager::ListVolatile() {
1167 24 return DoList(kListVolatile);
1168 }
1169
1170
1171 /**
1172 * Entry point for the shared cache manager process
1173 */
1174 int PosixQuotaManager::MainCacheManager(int argc, char **argv) {
1175 LogCvmfs(kLogQuota, kLogDebug, "starting quota manager");
1176 int retval;
1177
1178 PosixQuotaManager shared_manager(0, 0, "");
1179 shared_manager.shared_ = true;
1180 shared_manager.spawned_ = true;
1181 shared_manager.pinned_ = 0;
1182
1183 // Process command line arguments
1184 ParseDirectories(string(argv[2]),
1185 &shared_manager.cache_dir_,
1186 &shared_manager.workspace_dir_);
1187 const int pipe_boot = String2Int64(argv[3]);
1188 const int pipe_handshake = String2Int64(argv[4]);
1189 shared_manager.limit_ = String2Int64(argv[5]);
1190 shared_manager.cleanup_threshold_ = String2Int64(argv[6]);
1191 const int foreground = String2Int64(argv[7]);
1192 const int syslog_level = String2Int64(argv[8]);
1193 const int syslog_facility = String2Int64(argv[9]);
1194 vector<string> logfiles = SplitString(argv[10], ':');
1195
1196 SetLogSyslogLevel(syslog_level);
1197 SetLogSyslogFacility(syslog_facility);
1198 if ((logfiles.size() > 0) && (logfiles[0] != ""))
1199 SetLogDebugFile(logfiles[0] + ".cachemgr");
1200 if (logfiles.size() > 1)
1201 SetLogMicroSyslog(logfiles[1]);
1202
1203 if (!foreground)
1204 Daemonize();
1205
1206 const UniquePtr<Watchdog> watchdog(Watchdog::Create(NULL));
1207 assert(watchdog.IsValid());
1208 watchdog->Spawn("./stacktrace.cachemgr");
1209
1210 // Initialize pipe, open non-blocking as cvmfs is not yet connected
1211 const int fd_lockfile_fifo = LockFile(shared_manager.workspace_dir_
1212 + "/lock_cachemgr.fifo");
1213 if (fd_lockfile_fifo < 0) {
1214 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
1215 "could not open lock file "
1216 "%s (%d)",
1217 (shared_manager.workspace_dir_ + "/lock_cachemgr.fifo").c_str(),
1218 errno);
1219 return 1;
1220 }
1221 const string crash_guard = shared_manager.cache_dir_ + "/cachemgr.running";
1222 const bool rebuild = FileExists(crash_guard);
1223 retval = open(crash_guard.c_str(), O_RDONLY | O_CREAT, 0600);
1224 if (retval < 0) {
1225 LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr,
1226 "failed to create shared cache manager crash guard");
1227 UnlockFile(fd_lockfile_fifo);
1228 return 1;
1229 }
1230 close(retval);
1231
1232 // Redirect SQlite temp directory to cache (global variable)
1233 const string tmp_dir = shared_manager.workspace_dir_;
1234 sqlite3_temp_directory = static_cast<char *>(
1235 sqlite3_malloc(tmp_dir.length() + 1));
1236 snprintf(sqlite3_temp_directory, tmp_dir.length() + 1, "%s", tmp_dir.c_str());
1237
1238 // Cleanup leftover named pipes
1239 shared_manager.CleanupPipes();
1240
1241 if (!shared_manager.InitDatabase(rebuild)) {
1242 UnlockFile(fd_lockfile_fifo);
1243 return 1;
1244 }
1245 shared_manager.CheckFreeSpace();
1246
1247 // Save protocol revision to file. If the file is not found, it indicates
1248 // to the client that the cache manager is from times before the protocol
1249 // was versioned.
1250 const string protocol_revision_path = shared_manager.workspace_dir_
1251 + "/cachemgr.protocol";
1252 retval = open(protocol_revision_path.c_str(), O_WRONLY | O_CREAT, 0600);
1253 if (retval < 0) {
1254 LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr,
1255 "failed to open protocol revision file (%d)", errno);
1256 UnlockFile(fd_lockfile_fifo);
1257 return 1;
1258 }
1259 const string revision = StringifyInt(kProtocolRevision);
1260 const int written = write(retval, revision.data(), revision.length());
1261 close(retval);
1262 if ((written < 0) || static_cast<unsigned>(written) != revision.length()) {
1263 LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr,
1264 "failed to write protocol revision (%d)", errno);
1265 UnlockFile(fd_lockfile_fifo);
1266 return 1;
1267 }
1268
1269 const string fifo_path = shared_manager.workspace_dir_ + "/cachemgr";
1270 shared_manager.pipe_lru_[0] = open(fifo_path.c_str(), O_RDONLY | O_NONBLOCK);
1271 if (shared_manager.pipe_lru_[0] < 0) {
1272 LogCvmfs(kLogQuota, kLogDebug, "failed to listen on FIFO %s (%d)",
1273 fifo_path.c_str(), errno);
1274 UnlockFile(fd_lockfile_fifo);
1275 return 1;
1276 }
1277 Nonblock2Block(shared_manager.pipe_lru_[0]);
1278 LogCvmfs(kLogQuota, kLogDebug, "shared cache manager listening");
1279
1280 char buf = 'C';
1281 WritePipe(pipe_boot, &buf, 1);
1282 close(pipe_boot);
1283
1284 ReadPipe(pipe_handshake, &buf, 1);
1285 close(pipe_handshake);
1286 LogCvmfs(kLogQuota, kLogDebug, "shared cache manager handshake done");
1287
1288 // Ensure that broken pipes from clients do not kill the cache manager
1289 signal(SIGPIPE, SIG_IGN);
1290 // Don't let Ctrl-C ungracefully kill interactive session
1291 signal(SIGINT, SIG_IGN);
1292
1293 shared_manager.MainCommandServer(&shared_manager);
1294 unlink(fifo_path.c_str());
1295 unlink(protocol_revision_path.c_str());
1296 shared_manager.CloseDatabase();
1297 unlink(crash_guard.c_str());
1298 UnlockFile(fd_lockfile_fifo);
1299
1300 if (sqlite3_temp_directory) {
1301 sqlite3_free(sqlite3_temp_directory);
1302 sqlite3_temp_directory = NULL;
1303 }
1304
1305 return 0;
1306 }
1307
1308
1309 248 void *PosixQuotaManager::MainCommandServer(void *data) {
1310 248 PosixQuotaManager *quota_mgr = static_cast<PosixQuotaManager *>(data);
1311
1312
1/2
✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
248 LogCvmfs(kLogQuota, kLogDebug, "starting quota manager");
1313
1/2
✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
248 sqlite3_soft_heap_limit(quota_mgr->kSqliteMemPerThread);
1314
1315
2/2
✓ Branch 1 taken 7936 times.
✓ Branch 2 taken 248 times.
8184 LruCommand command_buffer[kCommandBufferSize];
1316 char description_buffer[kCommandBufferSize * kMaxDescription];
1317 248 unsigned num_commands = 0;
1318
1319
1/2
✓ Branch 1 taken 1201288 times.
✗ Branch 2 not taken.
1201288 while (read(quota_mgr->pipe_lru_[0], &command_buffer[num_commands],
1320 sizeof(command_buffer[0]))
1321
2/2
✓ Branch 0 taken 1201040 times.
✓ Branch 1 taken 248 times.
1201288 == sizeof(command_buffer[0])) {
1322 1201040 const CommandType command_type = command_buffer[num_commands].command_type;
1323
1/2
✓ Branch 1 taken 1201040 times.
✗ Branch 2 not taken.
1201040 LogCvmfs(kLogQuota, kLogDebug, "received command %d", command_type);
1324 1201040 const uint64_t size = command_buffer[num_commands].GetSize();
1325
1326 // Inserts and pins come with a description (usually a path)
1327
4/4
✓ Branch 0 taken 400904 times.
✓ Branch 1 taken 800136 times.
✓ Branch 2 taken 400872 times.
✓ Branch 3 taken 32 times.
1201040 if ((command_type == kInsert) || (command_type == kInsertVolatile)
1328
4/4
✓ Branch 0 taken 400856 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 88 times.
✓ Branch 3 taken 400768 times.
400872 || (command_type == kPin) || (command_type == kPinRegular)) {
1329 800272 const int desc_length = command_buffer[num_commands].desc_length;
1330 800272 ReadPipe(quota_mgr->pipe_lru_[0],
1331
1/2
✓ Branch 1 taken 800272 times.
✗ Branch 2 not taken.
800272 &description_buffer[kMaxDescription * num_commands],
1332 desc_length);
1333 }
1334
1335 // The protocol revision is returned immediately
1336
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1201032 times.
1201040 if (command_type == kGetProtocolRevision) {
1337
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 const int return_pipe = quota_mgr->BindReturnPipe(
1338 command_buffer[num_commands].return_pipe);
1339
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (return_pipe < 0)
1340 continue;
1341
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 WritePipe(return_pipe, &quota_mgr->kProtocolRevision,
1342 sizeof(quota_mgr->kProtocolRevision));
1343
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 quota_mgr->UnbindReturnPipe(return_pipe);
1344 8 continue;
1345 8 }
1346
1347 // Register a new mountpoint
1348
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1201032 times.
1201032 if (command_type == kRegisterMountpoint) {
1349 size_t mp_size = 0;
1350 ReadPipe(quota_mgr->pipe_lru_[0], &mp_size, sizeof(size_t));
1351 std::string mountpoint(mp_size, '\0');
1352 ReadPipe(quota_mgr->pipe_lru_[0], (void *)mountpoint.data(), mp_size);
1353 quota_mgr->mountpoints_.push_back(mountpoint);
1354 LogCvmfs(kLogQuota, kLogDebug | kLogSyslog,
1355 "Mountpoint %s registered in the group", mountpoint.c_str());
1356 continue;
1357 }
1358
1359 // Set Cleanup Policy
1360
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1201032 times.
1201032 if (command_type == kSetCleanupPolicy) {
1361 bool policy = false;
1362 ReadPipe(quota_mgr->pipe_lru_[0], &policy, sizeof(bool));
1363 quota_mgr->cleanup_unused_first_ = policy;
1364 continue;
1365 }
1366 // Mountpoints are returned immediately
1367
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1201032 times.
1201032 if (command_type == kGetMountpoints) {
1368 const int return_pipe = quota_mgr->BindReturnPipe(
1369 command_buffer[num_commands].return_pipe);
1370 if (return_pipe < 0)
1371 continue;
1372
1373 std::string mps;
1374 for (auto it = quota_mgr->mountpoints_.begin();
1375 it != quota_mgr->mountpoints_.end();
1376 ++it) {
1377 mps += *it + "\n";
1378 }
1379 size_t mp_size = mps.size();
1380 WritePipe(return_pipe, &mp_size, sizeof(size_t));
1381 WritePipe(return_pipe, mps.c_str(), mp_size);
1382 quota_mgr->UnbindReturnPipe(return_pipe);
1383 continue;
1384 }
1385
1386 // Group hashes are returned immediately
1387
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1201032 times.
1201032 if (command_type == kGetGroupHashes) {
1388 const int return_pipe = quota_mgr->BindReturnPipe(
1389 command_buffer[num_commands].return_pipe);
1390 if (return_pipe < 0)
1391 continue;
1392
1393 std::vector<shash::Short> gh = quota_mgr->CollectAllOpenHashes();
1394 std::string result;
1395 for (auto it = gh.begin(); it != gh.end(); ++it) {
1396 result += (*it).ToString() + "\n";
1397 }
1398 size_t result_size = result.size();
1399 WritePipe(return_pipe, &result_size, sizeof(size_t));
1400 WritePipe(return_pipe, result.c_str(), result_size);
1401 quota_mgr->UnbindReturnPipe(return_pipe);
1402 continue;
1403 }
1404
1405 // The cleanup rate is returned immediately
1406
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 1201000 times.
1201032 if (command_type == kCleanupRate) {
1407
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 const int return_pipe = quota_mgr->BindReturnPipe(
1408 command_buffer[num_commands].return_pipe);
1409
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (return_pipe < 0)
1410 continue;
1411 const uint64_t
1412 32 period_s = size; // use the size field to transmit the period
1413
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 uint64_t rate = quota_mgr->cleanup_recorder_.GetNoTicks(period_s);
1414
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 WritePipe(return_pipe, &rate, sizeof(rate));
1415
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 quota_mgr->UnbindReturnPipe(return_pipe);
1416 32 continue;
1417 32 }
1418
1419
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1200992 times.
1201000 if (command_type == kSetLimit) {
1420
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 const int return_pipe = quota_mgr->BindReturnPipe(
1421 command_buffer[num_commands].return_pipe);
1422
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (return_pipe < 0)
1423 continue;
1424 8 quota_mgr->limit_ = size; // use the size field to transmit the size
1425 8 quota_mgr->cleanup_threshold_ = size / 2;
1426
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 LogCvmfs(kLogQuota, kLogDebug | kLogSyslog,
1427 "Quota limit set to %lu / threshold %lu", quota_mgr->limit_,
1428 quota_mgr->cleanup_threshold_);
1429 8 bool ret = true;
1430
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 WritePipe(return_pipe, &ret, sizeof(ret));
1431
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 quota_mgr->UnbindReturnPipe(return_pipe);
1432 8 continue;
1433 8 }
1434
1435 // Reservations are handled immediately and "out of band"
1436
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 1200880 times.
1200992 if (command_type == kReserve) {
1437 112 bool success = true;
1438
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 const int return_pipe = quota_mgr->BindReturnPipe(
1439 command_buffer[num_commands].return_pipe);
1440
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 112 times.
112 if (return_pipe < 0)
1441 continue;
1442
1443
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 const shash::Any hash = command_buffer[num_commands].RetrieveHash();
1444
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 const string hash_str(hash.ToString());
1445
1/2
✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
112 LogCvmfs(kLogQuota, kLogDebug, "reserve %lu bytes for %s", size,
1446 hash_str.c_str());
1447
1448
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 if (quota_mgr->pinned_chunks_.find(hash)
1449
2/2
✓ Branch 2 taken 96 times.
✓ Branch 3 taken 16 times.
224 == quota_mgr->pinned_chunks_.end()) {
1450
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 88 times.
96 if ((quota_mgr->pinned_ + size) > quota_mgr->cleanup_threshold_) {
1451
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 LogCvmfs(kLogQuota, kLogDebug,
1452 "failed to insert %s (pinned), no space", hash_str.c_str());
1453 8 success = false;
1454 } else {
1455
1/2
✓ Branch 1 taken 88 times.
✗ Branch 2 not taken.
88 quota_mgr->pinned_chunks_[hash] = size;
1456 88 quota_mgr->pinned_ += size;
1457
1/2
✓ Branch 1 taken 88 times.
✗ Branch 2 not taken.
88 quota_mgr->CheckHighPinWatermark();
1458 }
1459 }
1460
1461
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 WritePipe(return_pipe, &success, sizeof(success));
1462
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 quota_mgr->UnbindReturnPipe(return_pipe);
1463 112 continue;
1464 112 }
1465
1466 // Back channels are also handled out of band
1467
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 1200848 times.
1200880 if (command_type == kRegisterBackChannel) {
1468
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 const int return_pipe = quota_mgr->BindReturnPipe(
1469 command_buffer[num_commands].return_pipe);
1470
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (return_pipe < 0)
1471 continue;
1472
1473
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 quota_mgr->UnlinkReturnPipe(command_buffer[num_commands].return_pipe);
1474
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 Block2Nonblock(return_pipe); // back channels are opportunistic
1475
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 shash::Md5 hash;
1476 32 memcpy(hash.digest, command_buffer[num_commands].digest,
1477 32 shash::kDigestSizes[shash::kMd5]);
1478
1479 32 quota_mgr->LockBackChannels();
1480 const map<shash::Md5, int>::const_iterator
1481
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 iter = quota_mgr->back_channels_.find(hash);
1482
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 32 times.
32 if (iter != quota_mgr->back_channels_.end()) {
1483 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
1484 "closing left-over back channel %s", hash.ToString().c_str());
1485 close(iter->second);
1486 }
1487
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 quota_mgr->back_channels_[hash] = return_pipe;
1488 32 quota_mgr->UnlockBackChannels();
1489
1490 32 char success = 'S';
1491
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 WritePipe(return_pipe, &success, sizeof(success));
1492
1/2
✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
32 LogCvmfs(kLogQuota, kLogDebug, "register back channel %s on fd %d",
1493
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
64 hash.ToString().c_str(), return_pipe);
1494
1495 32 continue;
1496 32 }
1497
1498
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1200832 times.
1200848 if (command_type == kUnregisterBackChannel) {
1499
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 shash::Md5 hash;
1500 16 memcpy(hash.digest, command_buffer[num_commands].digest,
1501 16 shash::kDigestSizes[shash::kMd5]);
1502
1503 16 quota_mgr->LockBackChannels();
1504 const map<shash::Md5, int>::iterator iter = quota_mgr->back_channels_
1505
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 .find(hash);
1506
1/2
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 if (iter != quota_mgr->back_channels_.end()) {
1507
1/2
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 LogCvmfs(kLogQuota, kLogDebug, "closing back channel %s",
1508
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
32 hash.ToString().c_str());
1509
1/2
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 close(iter->second);
1510
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 quota_mgr->back_channels_.erase(iter);
1511 } else {
1512 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
1513 "did not find back channel %s", hash.ToString().c_str());
1514 }
1515 16 quota_mgr->UnlockBackChannels();
1516
1517 16 continue;
1518 16 }
1519
1520 // Unpinnings are also handled immediately with respect to the pinned gauge
1521
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1200816 times.
1200832 if (command_type == kUnpin) {
1522
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 const shash::Any hash = command_buffer[num_commands].RetrieveHash();
1523
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 const string hash_str(hash.ToString());
1524
1525 const map<shash::Any, uint64_t>::iterator iter = quota_mgr->pinned_chunks_
1526
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 .find(hash);
1527
1/2
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 if (iter != quota_mgr->pinned_chunks_.end()) {
1528 16 quota_mgr->pinned_ -= iter->second;
1529
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 quota_mgr->pinned_chunks_.erase(iter);
1530 // It can happen that files get pinned that were removed from the cache
1531 // (see cache.cc). We fix this at this point, where we remove such
1532 // entries from the cache database.
1533
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
32 if (!FileExists(quota_mgr->cache_dir_ + "/"
1534
4/6
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 8 times.
✓ Branch 10 taken 8 times.
48 + hash.MakePathWithoutSuffix())) {
1535
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 LogCvmfs(kLogQuota, kLogDebug,
1536 "remove orphaned pinned hash %s from cache database",
1537 hash_str.c_str());
1538
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 sqlite3_bind_text(quota_mgr->stmt_size_, 1, &hash_str[0],
1539 8 hash_str.length(), SQLITE_STATIC);
1540 int retval;
1541
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 if ((retval = sqlite3_step(quota_mgr->stmt_size_)) == SQLITE_ROW) {
1542
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 const uint64_t size = sqlite3_column_int64(quota_mgr->stmt_size_,
1543 8 0);
1544
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 sqlite3_bind_text(quota_mgr->stmt_rm_, 1, &(hash_str[0]),
1545 8 hash_str.length(), SQLITE_STATIC);
1546
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 retval = sqlite3_step(quota_mgr->stmt_rm_);
1547
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
8 if ((retval == SQLITE_DONE) || (retval == SQLITE_OK)) {
1548 8 quota_mgr->gauge_ -= size;
1549 } else {
1550 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
1551 "failed to delete %s (%d)", hash_str.c_str(), retval);
1552 }
1553
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 sqlite3_reset(quota_mgr->stmt_rm_);
1554 }
1555
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 sqlite3_reset(quota_mgr->stmt_size_);
1556 }
1557 } else {
1558 LogCvmfs(kLogQuota, kLogDebug, "this chunk was not pinned");
1559 }
1560 16 }
1561
1562 // Immediate commands trigger flushing of the buffer
1563 1200832 const bool immediate_command = (command_type == kCleanup)
1564
2/2
✓ Branch 0 taken 1200592 times.
✓ Branch 1 taken 168 times.
1200760 || (command_type == kList)
1565
2/2
✓ Branch 0 taken 1200528 times.
✓ Branch 1 taken 64 times.
1200592 || (command_type == kListPinned)
1566
2/2
✓ Branch 0 taken 1200504 times.
✓ Branch 1 taken 24 times.
1200528 || (command_type == kListCatalogs)
1567
2/2
✓ Branch 0 taken 1200480 times.
✓ Branch 1 taken 24 times.
1200504 || (command_type == kListVolatile)
1568
2/2
✓ Branch 0 taken 1200456 times.
✓ Branch 1 taken 24 times.
1200480 || (command_type == kRemove)
1569
2/2
✓ Branch 0 taken 1200312 times.
✓ Branch 1 taken 144 times.
1200456 || (command_type == kStatus)
1570
1/2
✓ Branch 0 taken 1200312 times.
✗ Branch 1 not taken.
1200312 || (command_type == kLimits)
1571
3/4
✓ Branch 0 taken 1200760 times.
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1200312 times.
2401592 || (command_type == kPid);
1572
2/2
✓ Branch 0 taken 1200312 times.
✓ Branch 1 taken 520 times.
1200832 if (!immediate_command)
1573 1200312 num_commands++;
1574
1575
4/4
✓ Branch 0 taken 1163336 times.
✓ Branch 1 taken 37496 times.
✓ Branch 2 taken 520 times.
✓ Branch 3 taken 1162816 times.
1200832 if ((num_commands == kCommandBufferSize) || immediate_command) {
1576
1/2
✓ Branch 1 taken 38016 times.
✗ Branch 2 not taken.
38016 quota_mgr->ProcessCommandBunch(num_commands, command_buffer,
1577 description_buffer);
1578
2/2
✓ Branch 0 taken 37496 times.
✓ Branch 1 taken 520 times.
38016 if (!immediate_command)
1579 37496 num_commands = 0;
1580 }
1581
1582
2/2
✓ Branch 0 taken 520 times.
✓ Branch 1 taken 1200312 times.
1200832 if (immediate_command) {
1583 // Process cleanup, listings
1584
1/2
✓ Branch 1 taken 520 times.
✗ Branch 2 not taken.
520 const int return_pipe = quota_mgr->BindReturnPipe(
1585 command_buffer[num_commands].return_pipe);
1586
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 520 times.
520 if (return_pipe < 0) {
1587 num_commands = 0;
1588 continue;
1589 }
1590
1591 int retval;
1592 520 sqlite3_stmt *this_stmt_list = NULL;
1593
7/10
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 72 times.
✓ Branch 2 taken 168 times.
✓ Branch 3 taken 64 times.
✓ Branch 4 taken 24 times.
✓ Branch 5 taken 24 times.
✓ Branch 6 taken 144 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
520 switch (command_type) {
1594 24 case kRemove: {
1595
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 const shash::Any hash = command_buffer[num_commands].RetrieveHash();
1596
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 const string hash_str = hash.ToString();
1597
1/2
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
24 LogCvmfs(kLogQuota, kLogDebug, "manually removing %s",
1598 hash_str.c_str());
1599 24 bool success = false;
1600
1601
1/2
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
24 sqlite3_bind_text(quota_mgr->stmt_size_, 1, &hash_str[0],
1602 24 hash_str.length(), SQLITE_STATIC);
1603 int retval;
1604
3/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
✓ Branch 4 taken 8 times.
24 if ((retval = sqlite3_step(quota_mgr->stmt_size_)) == SQLITE_ROW) {
1605
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 const uint64_t size = sqlite3_column_int64(quota_mgr->stmt_size_,
1606 16 0);
1607
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 const uint64_t is_pinned = sqlite3_column_int64(
1608 16 quota_mgr->stmt_size_, 1);
1609
1610
1/2
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 sqlite3_bind_text(quota_mgr->stmt_rm_, 1, &(hash_str[0]),
1611 16 hash_str.length(), SQLITE_STATIC);
1612
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 retval = sqlite3_step(quota_mgr->stmt_rm_);
1613
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
16 if ((retval == SQLITE_DONE) || (retval == SQLITE_OK)) {
1614 16 success = true;
1615 16 quota_mgr->gauge_ -= size;
1616
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 if (is_pinned) {
1617
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 quota_mgr->pinned_chunks_.erase(hash);
1618 8 quota_mgr->pinned_ -= size;
1619 }
1620 } else {
1621 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
1622 "failed to delete %s (%d)", hash_str.c_str(), retval);
1623 }
1624
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 sqlite3_reset(quota_mgr->stmt_rm_);
1625 } else {
1626 // File does not exist
1627 8 success = true;
1628 }
1629
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 sqlite3_reset(quota_mgr->stmt_size_);
1630
1631
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 WritePipe(return_pipe, &success, sizeof(success));
1632 24 break;
1633 24 }
1634 72 case kCleanup:
1635
1/2
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
72 retval = quota_mgr->DoCleanup(size);
1636
1/2
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
72 WritePipe(return_pipe, &retval, sizeof(retval));
1637 72 break;
1638 168 case kList:
1639
1/2
✓ Branch 0 taken 168 times.
✗ Branch 1 not taken.
168 if (!this_stmt_list)
1640 168 this_stmt_list = quota_mgr->stmt_list_;
1641 case kListPinned:
1642
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 168 times.
232 if (!this_stmt_list)
1643 64 this_stmt_list = quota_mgr->stmt_list_pinned_;
1644 case kListCatalogs:
1645
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 232 times.
256 if (!this_stmt_list)
1646 24 this_stmt_list = quota_mgr->stmt_list_catalogs_;
1647 case kListVolatile:
1648
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 256 times.
280 if (!this_stmt_list)
1649 24 this_stmt_list = quota_mgr->stmt_list_volatile_;
1650
1651 // Pipe back the list, one by one
1652 int length;
1653
3/4
✓ Branch 1 taken 800592 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 800312 times.
✓ Branch 4 taken 280 times.
800592 while (sqlite3_step(this_stmt_list) == SQLITE_ROW) {
1654
1/2
✓ Branch 2 taken 800312 times.
✗ Branch 3 not taken.
800312 string path = "(NULL)";
1655
2/4
✓ Branch 1 taken 800312 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 800312 times.
✗ Branch 4 not taken.
800312 if (sqlite3_column_type(this_stmt_list, 0) != SQLITE_NULL) {
1656 1600624 path = string(reinterpret_cast<const char *>(
1657
2/4
✓ Branch 1 taken 800312 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 800312 times.
✗ Branch 5 not taken.
800312 sqlite3_column_text(this_stmt_list, 0)));
1658 }
1659 800312 length = path.length();
1660
1/2
✓ Branch 1 taken 800312 times.
✗ Branch 2 not taken.
800312 WritePipe(return_pipe, &length, sizeof(length));
1661
1/2
✓ Branch 0 taken 800312 times.
✗ Branch 1 not taken.
800312 if (length > 0)
1662
2/4
✓ Branch 1 taken 800312 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 800312 times.
✗ Branch 5 not taken.
800312 WritePipe(return_pipe, &path[0], length);
1663 800312 }
1664 280 length = -1;
1665
1/2
✓ Branch 1 taken 280 times.
✗ Branch 2 not taken.
280 WritePipe(return_pipe, &length, sizeof(length));
1666
1/2
✓ Branch 1 taken 280 times.
✗ Branch 2 not taken.
280 sqlite3_reset(this_stmt_list);
1667 280 break;
1668 144 case kStatus:
1669
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 WritePipe(return_pipe, &quota_mgr->gauge_, sizeof(quota_mgr->gauge_));
1670
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 WritePipe(return_pipe, &quota_mgr->pinned_,
1671 sizeof(quota_mgr->pinned_));
1672 144 break;
1673 case kLimits:
1674 WritePipe(return_pipe, &quota_mgr->limit_, sizeof(quota_mgr->limit_));
1675 WritePipe(return_pipe, &quota_mgr->cleanup_threshold_,
1676 sizeof(quota_mgr->cleanup_threshold_));
1677 break;
1678 case kPid: {
1679 pid_t pid = getpid();
1680 WritePipe(return_pipe, &pid, sizeof(pid));
1681 break;
1682 }
1683 default:
1684 PANIC(NULL); // other types are handled by the bunch processor
1685 }
1686
1/2
✓ Branch 1 taken 520 times.
✗ Branch 2 not taken.
520 quota_mgr->UnbindReturnPipe(return_pipe);
1687 520 num_commands = 0;
1688 }
1689 }
1690
1691
1/2
✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
248 LogCvmfs(kLogQuota, kLogDebug, "stopping cache manager (%d)", errno);
1692
1/2
✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
248 close(quota_mgr->pipe_lru_[0]);
1693
1/2
✓ Branch 1 taken 248 times.
✗ Branch 2 not taken.
248 quota_mgr->ProcessCommandBunch(num_commands, command_buffer,
1694 description_buffer);
1695
1696 // Unpin
1697 248 command_buffer[0].command_type = kTouch;
1698 248 for (map<shash::Any, uint64_t>::const_iterator
1699 248 i = quota_mgr->pinned_chunks_.begin(),
1700 248 iEnd = quota_mgr->pinned_chunks_.end();
1701
2/2
✓ Branch 1 taken 80 times.
✓ Branch 2 taken 248 times.
328 i != iEnd;
1702 80 ++i) {
1703
1/2
✓ Branch 2 taken 80 times.
✗ Branch 3 not taken.
80 command_buffer[0].StoreHash(i->first);
1704
1/2
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
80 quota_mgr->ProcessCommandBunch(1, command_buffer, description_buffer);
1705 }
1706
1707 248 return NULL;
1708 }
1709
1710
1711 736 void PosixQuotaManager::MakeReturnPipe(int pipe[2]) {
1712
2/2
✓ Branch 0 taken 712 times.
✓ Branch 1 taken 24 times.
736 if (!shared_) {
1713 712 MakePipe(pipe);
1714 712 return;
1715 }
1716
1717 // Create FIFO in cache directory, store path name (number) in pipe write end
1718 24 int i = 0;
1719 int retval;
1720 do {
1721
2/4
✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 32 times.
✗ Branch 6 not taken.
32 retval = mkfifo((workspace_dir_ + "/pipe" + StringifyInt(i)).c_str(), 0600);
1722 32 pipe[1] = i;
1723 32 i++;
1724
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
32 } while ((retval == -1) && (errno == EEXIST));
1725
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 assert(retval == 0);
1726
1727 // Connect reader's end
1728
3/6
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 24 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 24 times.
✗ Branch 10 not taken.
24 pipe[0] = open((workspace_dir_ + "/pipe" + StringifyInt(pipe[1])).c_str(),
1729 O_RDONLY | O_NONBLOCK);
1730
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 assert(pipe[0] >= 0);
1731 24 Nonblock2Block(pipe[0]);
1732 }
1733
1734
1735 1197 void PosixQuotaManager::ParseDirectories(const std::string cache_workspace,
1736 std::string *cache_dir,
1737 std::string *workspace_dir) {
1738
1/2
✓ Branch 1 taken 1197 times.
✗ Branch 2 not taken.
1197 vector<string> dir_tokens(SplitString(cache_workspace, ':'));
1739
2/3
✓ Branch 1 taken 1181 times.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
1197 switch (dir_tokens.size()) {
1740 1181 case 1:
1741
2/4
✓ Branch 2 taken 1181 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1181 times.
✗ Branch 6 not taken.
1181 *cache_dir = *workspace_dir = dir_tokens[0];
1742 1181 break;
1743 16 case 2:
1744
1/2
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 *cache_dir = dir_tokens[0];
1745
1/2
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 *workspace_dir = dir_tokens[1];
1746 16 break;
1747 default:
1748 PANIC(NULL);
1749 }
1750 1197 }
1751
1752 8 void PosixQuotaManager::SkipEviction(const EvictCandidate &candidate) {
1753 8 bool res = true;
1754
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 std::string hash_str = candidate.hash.ToString();
1755
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 LogCvmfs(kLogQuota, kLogDebug, "Exclude %s from eviction", hash_str.c_str());
1756
2/4
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
8 sqlite3_bind_text(stmt_block_, 1, &hash_str[0], hash_str.length(),
1757 SQLITE_STATIC);
1758
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 res = (sqlite3_step(stmt_block_) == SQLITE_DONE);
1759
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 sqlite3_reset(stmt_block_);
1760
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 assert(res);
1761 8 }
1762
1763 /**
1764 * Immediately inserts a new pinned catalog. Does cache cleanup if necessary.
1765 *
1766 * \return True on success, false otherwise
1767 */
1768 936 bool PosixQuotaManager::Pin(const shash::Any &hash,
1769 const uint64_t size,
1770 const string &description,
1771 const bool is_catalog) {
1772
3/4
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 920 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
936 assert((size > 0) || !is_catalog);
1773
1774
1/2
✓ Branch 1 taken 936 times.
✗ Branch 2 not taken.
936 const string hash_str = hash.ToString();
1775
1/2
✓ Branch 3 taken 936 times.
✗ Branch 4 not taken.
936 LogCvmfs(kLogQuota, kLogDebug, "pin into lru %s, path %s", hash_str.c_str(),
1776 description.c_str());
1777
1778 // Has to run when not yet spawned (cvmfs initialization)
1779
2/2
✓ Branch 0 taken 824 times.
✓ Branch 1 taken 112 times.
936 if (!spawned_) {
1780 // Code duplication here
1781
3/4
✓ Branch 2 taken 824 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 445 times.
✓ Branch 6 taken 379 times.
824 if (pinned_chunks_.find(hash) == pinned_chunks_.end()) {
1782
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 429 times.
445 if (pinned_ + size > cleanup_threshold_) {
1783
1/2
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 LogCvmfs(kLogQuota, kLogDebug, "failed to insert %s (pinned), no space",
1784 hash_str.c_str());
1785 16 return false;
1786 } else {
1787
1/2
✓ Branch 1 taken 429 times.
✗ Branch 2 not taken.
429 pinned_chunks_[hash] = size;
1788 429 pinned_ += size;
1789
1/2
✓ Branch 1 taken 429 times.
✗ Branch 2 not taken.
429 CheckHighPinWatermark();
1790 }
1791 }
1792
1/2
✓ Branch 1 taken 808 times.
✗ Branch 2 not taken.
808 const bool exists = Contains(hash_str);
1793
4/4
✓ Branch 0 taken 429 times.
✓ Branch 1 taken 379 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 421 times.
808 if (!exists && (gauge_ + size > limit_)) {
1794
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 LogCvmfs(kLogQuota, kLogDebug, "over limit, gauge %lu, file size %lu",
1795 gauge_, size);
1796
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 const int retval = DoCleanup(cleanup_threshold_);
1797
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 assert(retval != 0);
1798 }
1799
1/2
✓ Branch 3 taken 808 times.
✗ Branch 4 not taken.
808 sqlite3_bind_text(stmt_new_, 1, &hash_str[0], hash_str.length(),
1800 SQLITE_STATIC);
1801
1/2
✓ Branch 1 taken 808 times.
✗ Branch 2 not taken.
808 sqlite3_bind_int64(stmt_new_, 2, size);
1802
1/2
✓ Branch 1 taken 808 times.
✗ Branch 2 not taken.
808 sqlite3_bind_int64(stmt_new_, 3, seq_++);
1803
1/2
✓ Branch 3 taken 808 times.
✗ Branch 4 not taken.
808 sqlite3_bind_text(stmt_new_, 4, &description[0], description.length(),
1804 SQLITE_STATIC);
1805
3/4
✓ Branch 0 taken 784 times.
✓ Branch 1 taken 24 times.
✓ Branch 3 taken 808 times.
✗ Branch 4 not taken.
808 sqlite3_bind_int64(stmt_new_, 5, is_catalog ? kFileCatalog : kFileRegular);
1806
1/2
✓ Branch 1 taken 808 times.
✗ Branch 2 not taken.
808 sqlite3_bind_int64(stmt_new_, 6, 1);
1807
1/2
✓ Branch 1 taken 808 times.
✗ Branch 2 not taken.
808 const int retval = sqlite3_step(stmt_new_);
1808
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 808 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
808 assert((retval == SQLITE_DONE) || (retval == SQLITE_OK));
1809
1/2
✓ Branch 1 taken 808 times.
✗ Branch 2 not taken.
808 sqlite3_reset(stmt_new_);
1810
2/2
✓ Branch 0 taken 429 times.
✓ Branch 1 taken 379 times.
808 if (!exists)
1811 429 gauge_ += size;
1812 808 return true;
1813 }
1814
1815 int pipe_reserve[2];
1816
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 MakeReturnPipe(pipe_reserve);
1817
1818 112 LruCommand cmd;
1819 112 cmd.command_type = kReserve;
1820 112 cmd.SetSize(size);
1821
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 cmd.StoreHash(hash);
1822 112 cmd.return_pipe = pipe_reserve[1];
1823
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
1824 bool result;
1825
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 ManagedReadHalfPipe(pipe_reserve[0], &result, sizeof(result));
1826
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 CloseReturnPipe(pipe_reserve);
1827
1828
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 104 times.
112 if (!result)
1829 8 return false;
1830
3/4
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 88 times.
✓ Branch 3 taken 104 times.
✗ Branch 4 not taken.
104 DoInsert(hash, size, description, is_catalog ? kPin : kPinRegular);
1831
1832 104 return true;
1833 936 }
1834
1835
1836 1181 PosixQuotaManager::PosixQuotaManager(const uint64_t limit,
1837 const uint64_t cleanup_threshold,
1838 1181 const string &cache_workspace)
1839 1181 : shared_(false)
1840 1181 , spawned_(false)
1841 1181 , limit_(limit)
1842 1181 , cleanup_threshold_(cleanup_threshold)
1843 1181 , gauge_(0)
1844 1181 , pinned_(0)
1845 1181 , seq_(0)
1846 1181 , cache_dir_() // initialized in body
1847 1181 , workspace_dir_() // initialized in body
1848 1181 , fd_lock_cachedb_(-1)
1849 1181 , async_delete_(true)
1850 1181 , cachemgr_pid_(0)
1851 1181 , database_(NULL)
1852 1181 , stmt_touch_(NULL)
1853 1181 , stmt_unpin_(NULL)
1854 1181 , stmt_block_(NULL)
1855 1181 , stmt_unblock_(NULL)
1856 1181 , stmt_new_(NULL)
1857 1181 , stmt_lru_(NULL)
1858 1181 , stmt_size_(NULL)
1859 1181 , stmt_rm_(NULL)
1860 1181 , stmt_rm_batch_(NULL)
1861 1181 , stmt_list_(NULL)
1862 1181 , stmt_list_pinned_(NULL)
1863 1181 , stmt_list_catalogs_(NULL)
1864 1181 , stmt_list_volatile_(NULL)
1865 1181 , initialized_(false)
1866 2362 , cleanup_unused_first_(false) {
1867
2/4
✓ Branch 1 taken 1181 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1181 times.
✗ Branch 5 not taken.
1181 ParseDirectories(cache_workspace, &cache_dir_, &workspace_dir_);
1868 1181 pipe_lru_[0] = pipe_lru_[1] = -1;
1869
1/2
✓ Branch 1 taken 1181 times.
✗ Branch 2 not taken.
1181 cleanup_recorder_.AddRecorder(1, 90); // last 1.5 min with second resolution
1870 // last 1.5 h with minute resolution
1871
1/2
✓ Branch 1 taken 1181 times.
✗ Branch 2 not taken.
1181 cleanup_recorder_.AddRecorder(60, 90 * 60);
1872 // last 18 hours with 20 min resolution
1873
1/2
✓ Branch 1 taken 1181 times.
✗ Branch 2 not taken.
1181 cleanup_recorder_.AddRecorder(20 * 60, 60 * 60 * 18);
1874 // last 4 days with hour resolution
1875
1/2
✓ Branch 1 taken 1181 times.
✗ Branch 2 not taken.
1181 cleanup_recorder_.AddRecorder(60 * 60, 60 * 60 * 24 * 4);
1876
1877 1181 lock_open_files_ = reinterpret_cast<pthread_mutex_t *>(
1878 1181 smalloc(sizeof(pthread_mutex_t)));
1879 1181 }
1880
1881
1882 4720 PosixQuotaManager::~PosixQuotaManager() {
1883 2360 free(lock_open_files_);
1884
1885
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 1156 times.
2360 if (!initialized_)
1886 48 return;
1887
1888
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1156 times.
2312 if (shared_) {
1889 // Most of cleanup is done elsewhen by shared cache manager
1890 close(pipe_lru_[1]);
1891 return;
1892 }
1893
1894
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 908 times.
2312 if (spawned_) {
1895 496 char fin = 0;
1896 496 WritePipe(pipe_lru_[1], &fin, 1);
1897 496 close(pipe_lru_[1]);
1898 496 pthread_join(thread_lru_, NULL);
1899 } else {
1900 1816 ClosePipe(pipe_lru_);
1901 }
1902
1903 2312 CloseDatabase();
1904
14/14
✓ Branch 1 taken 1156 times.
✓ Branch 2 taken 24 times.
✓ Branch 4 taken 1156 times.
✓ Branch 5 taken 24 times.
✓ Branch 7 taken 1156 times.
✓ Branch 8 taken 24 times.
✓ Branch 10 taken 1156 times.
✓ Branch 11 taken 24 times.
✓ Branch 13 taken 1156 times.
✓ Branch 14 taken 24 times.
✓ Branch 16 taken 1156 times.
✓ Branch 17 taken 24 times.
✓ Branch 19 taken 1156 times.
✓ Branch 20 taken 24 times.
5008 }
1905
1906
1907 38344 void PosixQuotaManager::ProcessCommandBunch(const unsigned num,
1908 const LruCommand *commands,
1909 const char *descriptions) {
1910 38344 int retval = sqlite3_exec(database_, "BEGIN", NULL, NULL, NULL);
1911
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38344 times.
38344 assert(retval == SQLITE_OK);
1912
1913
2/2
✓ Branch 0 taken 1200392 times.
✓ Branch 1 taken 38344 times.
1238736 for (unsigned i = 0; i < num; ++i) {
1914
1/2
✓ Branch 1 taken 1200392 times.
✗ Branch 2 not taken.
1200392 const shash::Any hash = commands[i].RetrieveHash();
1915
1/2
✓ Branch 1 taken 1200392 times.
✗ Branch 2 not taken.
1200392 const string hash_str = hash.ToString();
1916 1200392 const unsigned size = commands[i].GetSize();
1917
1/2
✓ Branch 1 taken 1200392 times.
✗ Branch 2 not taken.
1200392 LogCvmfs(kLogQuota, kLogDebug, "processing %s (%d)", hash_str.c_str(),
1918 1200392 commands[i].command_type);
1919
1920 bool exists;
1921
3/4
✓ Branch 0 taken 400104 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 800272 times.
✗ Branch 3 not taken.
1200392 switch (commands[i].command_type) {
1922 400104 case kTouch:
1923
1/2
✓ Branch 1 taken 400104 times.
✗ Branch 2 not taken.
400104 sqlite3_bind_int64(stmt_touch_, 1, seq_++);
1924
1/2
✓ Branch 3 taken 400104 times.
✗ Branch 4 not taken.
400104 sqlite3_bind_text(stmt_touch_, 2, &hash_str[0], hash_str.length(),
1925 SQLITE_STATIC);
1926
1/2
✓ Branch 1 taken 400104 times.
✗ Branch 2 not taken.
400104 retval = sqlite3_step(stmt_touch_);
1927
1/2
✓ Branch 1 taken 400104 times.
✗ Branch 2 not taken.
400104 LogCvmfs(kLogQuota, kLogDebug, "touching %s (%ld): %d",
1928 400104 hash_str.c_str(), seq_ - 1, retval);
1929
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 400104 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
400104 if ((retval != SQLITE_DONE) && (retval != SQLITE_OK)) {
1930 PANIC(kLogSyslogErr, "failed to update %s in cachedb, error %d",
1931 hash_str.c_str(), retval);
1932 }
1933
1/2
✓ Branch 1 taken 400104 times.
✗ Branch 2 not taken.
400104 sqlite3_reset(stmt_touch_);
1934 400104 break;
1935 16 case kUnpin:
1936
1/2
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
16 sqlite3_bind_text(stmt_unpin_, 1, &hash_str[0], hash_str.length(),
1937 SQLITE_STATIC);
1938
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 retval = sqlite3_step(stmt_unpin_);
1939
1/2
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 LogCvmfs(kLogQuota, kLogDebug, "unpinning %s: %d", hash_str.c_str(),
1940 retval);
1941
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
16 if ((retval != SQLITE_DONE) && (retval != SQLITE_OK)) {
1942 PANIC(kLogSyslogErr, "failed to unpin %s in cachedb, error %d",
1943 hash_str.c_str(), retval);
1944 }
1945
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 sqlite3_reset(stmt_unpin_);
1946 16 break;
1947 800272 case kPin:
1948 case kPinRegular:
1949 case kInsert:
1950 case kInsertVolatile:
1951 // It could already be in, check
1952
1/2
✓ Branch 1 taken 800272 times.
✗ Branch 2 not taken.
800272 exists = Contains(hash_str);
1953
1954 // Cleanup, move to trash and unlink
1955
3/4
✓ Branch 0 taken 800232 times.
✓ Branch 1 taken 40 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 800232 times.
800272 if (!exists && (gauge_ + size > limit_)) {
1956 LogCvmfs(kLogQuota, kLogDebug, "over limit, gauge %lu, file size %u",
1957 gauge_, size);
1958 retval = DoCleanup(cleanup_threshold_);
1959 assert(retval != 0);
1960 }
1961
1962 // Insert or replace
1963
1/2
✓ Branch 3 taken 800272 times.
✗ Branch 4 not taken.
800272 sqlite3_bind_text(stmt_new_, 1, &hash_str[0], hash_str.length(),
1964 SQLITE_STATIC);
1965
1/2
✓ Branch 1 taken 800272 times.
✗ Branch 2 not taken.
800272 sqlite3_bind_int64(stmt_new_, 2, size);
1966
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 800240 times.
800272 if (commands[i].command_type == kInsertVolatile) {
1967
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 sqlite3_bind_int64(stmt_new_, 3, (seq_++) | kVolatileFlag);
1968 } else {
1969
1/2
✓ Branch 1 taken 800240 times.
✗ Branch 2 not taken.
800240 sqlite3_bind_int64(stmt_new_, 3, seq_++);
1970 }
1971 800272 sqlite3_bind_text(stmt_new_, 4, &descriptions[i * kMaxDescription],
1972
1/2
✓ Branch 1 taken 800272 times.
✗ Branch 2 not taken.
800272 commands[i].desc_length, SQLITE_STATIC);
1973
1/2
✓ Branch 1 taken 800272 times.
✗ Branch 2 not taken.
800272 sqlite3_bind_int64(
1974 stmt_new_, 5,
1975
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 800256 times.
800272 (commands[i].command_type == kPin) ? kFileCatalog : kFileRegular);
1976
1/2
✓ Branch 1 taken 800272 times.
✗ Branch 2 not taken.
800272 sqlite3_bind_int64(stmt_new_, 6,
1977
2/2
✓ Branch 0 taken 800256 times.
✓ Branch 1 taken 16 times.
800272 ((commands[i].command_type == kPin)
1978
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 800168 times.
800256 || (commands[i].command_type == kPinRegular))
1979 ? 1
1980 : 0);
1981
1/2
✓ Branch 1 taken 800272 times.
✗ Branch 2 not taken.
800272 retval = sqlite3_step(stmt_new_);
1982
1/2
✓ Branch 1 taken 800272 times.
✗ Branch 2 not taken.
800272 LogCvmfs(kLogQuota, kLogDebug, "insert or replace %s, method %d: %d",
1983 800272 hash_str.c_str(), commands[i].command_type, retval);
1984
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 800272 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
800272 if ((retval != SQLITE_DONE) && (retval != SQLITE_OK)) {
1985 PANIC(kLogSyslogErr, "failed to insert %s in cachedb, error %d",
1986 hash_str.c_str(), retval);
1987 }
1988
1/2
✓ Branch 1 taken 800272 times.
✗ Branch 2 not taken.
800272 sqlite3_reset(stmt_new_);
1989
1990
2/2
✓ Branch 0 taken 800232 times.
✓ Branch 1 taken 40 times.
800272 if (!exists)
1991 800232 gauge_ += size;
1992 800272 break;
1993 default:
1994 // other types should have been taken care of by event loop
1995 PANIC(NULL);
1996 }
1997 1200392 }
1998
1999 38344 retval = sqlite3_exec(database_, "COMMIT", NULL, NULL, NULL);
2000
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38344 times.
38344 if (retval != SQLITE_OK) {
2001 PANIC(kLogSyslogErr, "failed to commit to cachedb, error %d", retval);
2002 }
2003 38344 }
2004
2005
2006 1165 bool PosixQuotaManager::RebuildDatabase() {
2007 1165 bool result = false;
2008 1165 string sql;
2009 1165 sqlite3_stmt *stmt_select = NULL;
2010 1165 sqlite3_stmt *stmt_insert = NULL;
2011 int sqlerr;
2012 1165 int seq = 0;
2013 char hex[4];
2014 struct stat info;
2015 platform_dirent64 *d;
2016 1165 DIR *dirp = NULL;
2017 1165 string path;
2018
2019
1/2
✓ Branch 1 taken 1165 times.
✗ Branch 2 not taken.
1165 LogCvmfs(kLogQuota, kLogSyslog | kLogDebug, "re-building cache database");
2020
2021 // Empty cache catalog and fscache
2022
1/2
✓ Branch 1 taken 1165 times.
✗ Branch 2 not taken.
1165 sql = "DELETE FROM cache_catalog; DELETE FROM fscache;";
2023
1/2
✓ Branch 2 taken 1165 times.
✗ Branch 3 not taken.
1165 sqlerr = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
2024
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1165 times.
1165 if (sqlerr != SQLITE_OK) {
2025 LogCvmfs(kLogQuota, kLogDebug, "could not clear cache database");
2026 goto build_return;
2027 }
2028
2029 1165 gauge_ = 0;
2030
2031 // Insert files from cache sub-directories 00 - ff
2032 // TODO(jblomer): fs_traversal
2033
1/2
✓ Branch 1 taken 1165 times.
✗ Branch 2 not taken.
1165 sqlite3_prepare_v2(database_,
2034 "INSERT INTO fscache (sha1, size, actime) "
2035 "VALUES (:sha1, :s, :t);",
2036 -1, &stmt_insert, NULL);
2037
2038
2/2
✓ Branch 0 taken 292120 times.
✓ Branch 1 taken 1141 times.
293261 for (int i = 0; i <= 0xff; i++) {
2039 292120 snprintf(hex, sizeof(hex), "%02x", i);
2040
3/6
✓ Branch 2 taken 292120 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 292120 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 292120 times.
✗ Branch 9 not taken.
292120 path = cache_dir_ + "/" + string(hex);
2041
3/4
✓ Branch 2 taken 292120 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
✓ Branch 5 taken 292096 times.
292120 if ((dirp = opendir(path.c_str())) == NULL) {
2042
1/2
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
24 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
2043 "failed to open directory %s (tmpwatch interfering?)",
2044 path.c_str());
2045 24 goto build_return;
2046 }
2047
3/4
✓ Branch 1 taken 876304 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 584208 times.
✓ Branch 4 taken 292096 times.
876304 while ((d = platform_readdir(dirp)) != NULL) {
2048
3/6
✓ Branch 2 taken 584208 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 584208 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 584208 times.
✗ Branch 9 not taken.
1168416 const string file_path = path + "/" + string(d->d_name);
2049
1/2
✓ Branch 2 taken 584208 times.
✗ Branch 3 not taken.
584208 if (stat(file_path.c_str(), &info) == 0) {
2050
2/2
✓ Branch 0 taken 584192 times.
✓ Branch 1 taken 16 times.
584208 if (!S_ISREG(info.st_mode))
2051 584200 continue;
2052
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 if (info.st_size == 0) {
2053
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 LogCvmfs(kLogQuota, kLogSyslog | kLogDebug,
2054 "removing empty file %s during automatic cache db rebuild",
2055 file_path.c_str());
2056 8 unlink(file_path.c_str());
2057 8 continue;
2058 }
2059
2060
3/6
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 8 times.
✗ Branch 10 not taken.
16 string hash = string(hex) + string(d->d_name);
2061
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 sqlite3_bind_text(stmt_insert, 1, hash.data(), hash.length(),
2062 SQLITE_STATIC);
2063
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 sqlite3_bind_int64(stmt_insert, 2, info.st_size);
2064
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 sqlite3_bind_int64(stmt_insert, 3, info.st_atime);
2065
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
8 if (sqlite3_step(stmt_insert) != SQLITE_DONE) {
2066 LogCvmfs(kLogQuota, kLogDebug, "could not insert into temp table");
2067 goto build_return;
2068 }
2069
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 sqlite3_reset(stmt_insert);
2070
2071 8 gauge_ += info.st_size;
2072
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 } else {
2073 LogCvmfs(kLogQuota, kLogDebug, "could not stat %s", file_path.c_str());
2074 }
2075
2/3
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 584200 times.
✗ Branch 3 not taken.
584208 }
2076
1/2
✓ Branch 1 taken 292096 times.
✗ Branch 2 not taken.
292096 closedir(dirp);
2077 292096 dirp = NULL;
2078 }
2079
1/2
✓ Branch 1 taken 1141 times.
✗ Branch 2 not taken.
1141 sqlite3_finalize(stmt_insert);
2080 1141 stmt_insert = NULL;
2081
2082 // Transfer from temp table in cache catalog
2083
1/2
✓ Branch 1 taken 1141 times.
✗ Branch 2 not taken.
1141 sqlite3_prepare_v2(database_,
2084 "SELECT sha1, size FROM fscache ORDER BY actime;", -1,
2085 &stmt_select, NULL);
2086
1/2
✓ Branch 1 taken 1141 times.
✗ Branch 2 not taken.
1141 sqlite3_prepare_v2(
2087 database_,
2088 "INSERT INTO cache_catalog (sha1, size, acseq, path, type, pinned) "
2089 "VALUES (:sha1, :s, :seq, 'unknown (automatic rebuild)', :t, 0);",
2090 -1, &stmt_insert, NULL);
2091
3/4
✓ Branch 1 taken 1149 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 1141 times.
1149 while (sqlite3_step(stmt_select) == SQLITE_ROW) {
2092 const string hash = string(
2093
2/4
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
8 reinterpret_cast<const char *>(sqlite3_column_text(stmt_select, 0)));
2094
1/2
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 sqlite3_bind_text(stmt_insert, 1, &hash[0], hash.length(), SQLITE_STATIC);
2095
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 sqlite3_bind_int64(stmt_insert, 2, sqlite3_column_int64(stmt_select, 1));
2096
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 sqlite3_bind_int64(stmt_insert, 3, seq++);
2097 // Might also be a catalog (information is lost)
2098
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 sqlite3_bind_int64(stmt_insert, 4, kFileRegular);
2099
2100
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 const int retval = sqlite3_step(stmt_insert);
2101
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (retval != SQLITE_DONE) {
2102 // If the file system hosting the cache is full, we'll likely notice here
2103 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
2104 "could not insert into cache catalog (%d - %s)", retval,
2105 sqlite3_errstr(retval));
2106 goto build_return;
2107 }
2108
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 sqlite3_reset(stmt_insert);
2109
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 }
2110
2111 // Delete temporary table
2112
1/2
✓ Branch 1 taken 1141 times.
✗ Branch 2 not taken.
1141 sql = "DELETE FROM fscache;";
2113
1/2
✓ Branch 2 taken 1141 times.
✗ Branch 3 not taken.
1141 sqlerr = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
2114
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1141 times.
1141 if (sqlerr != SQLITE_OK) {
2115 LogCvmfs(kLogQuota, kLogDebug, "could not clear temporary table (%d)",
2116 sqlerr);
2117 goto build_return;
2118 }
2119
2120 1141 seq_ = seq;
2121 1141 result = true;
2122
1/2
✓ Branch 1 taken 1141 times.
✗ Branch 2 not taken.
1141 LogCvmfs(kLogQuota, kLogDebug,
2123 "rebuilding finished, sequence %" PRIu64 ", gauge %" PRIu64, seq_,
2124 gauge_);
2125
2126 1165 build_return:
2127
1/2
✓ Branch 0 taken 1165 times.
✗ Branch 1 not taken.
1165 if (stmt_insert)
2128
1/2
✓ Branch 1 taken 1165 times.
✗ Branch 2 not taken.
1165 sqlite3_finalize(stmt_insert);
2129
2/2
✓ Branch 0 taken 1141 times.
✓ Branch 1 taken 24 times.
1165 if (stmt_select)
2130
1/2
✓ Branch 1 taken 1141 times.
✗ Branch 2 not taken.
1141 sqlite3_finalize(stmt_select);
2131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1165 times.
1165 if (dirp)
2132 closedir(dirp);
2133 1165 return result;
2134 1165 }
2135
2136
2137 /**
2138 * Register a channel that allows the cache manager to trigger action to its
2139 * clients. Currently used for releasing pinned catalogs.
2140 */
2141 32 void PosixQuotaManager::RegisterBackChannel(int back_channel[2],
2142 const string &channel_id) {
2143
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 if (protocol_revision_ >= 1) {
2144
1/2
✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
32 shash::Md5 hash = shash::Md5(shash::AsciiPtr(channel_id));
2145
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 MakeReturnPipe(back_channel);
2146
2147 32 LruCommand cmd;
2148 32 cmd.command_type = kRegisterBackChannel;
2149 32 cmd.return_pipe = back_channel[1];
2150 // Not StoreHash(). This is an MD5 hash.
2151 32 memcpy(cmd.digest, hash.digest, hash.GetDigestSize());
2152
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2153
2154 char success;
2155
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 ManagedReadHalfPipe(back_channel[0], &success, sizeof(success));
2156 // At this point, the named FIFO is unlinked, so don't use CloseReturnPipe
2157
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (success != 'S') {
2158 PANIC(kLogDebug | kLogSyslogErr,
2159 "failed to register quota back channel (%c)", success);
2160 }
2161 } else {
2162 // Dummy pipe to return valid file descriptors
2163 MakePipe(back_channel);
2164 }
2165 32 }
2166
2167
2168 /**
2169 * Removes a chunk from cache, if it exists.
2170 */
2171 24 void PosixQuotaManager::Remove(const shash::Any &hash) {
2172
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 const string hash_str = hash.ToString();
2173
2174 int pipe_remove[2];
2175
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 MakeReturnPipe(pipe_remove);
2176
2177 24 LruCommand cmd;
2178 24 cmd.command_type = kRemove;
2179 24 cmd.return_pipe = pipe_remove[1];
2180
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 cmd.StoreHash(hash);
2181
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2182
2183 bool success;
2184
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 ManagedReadHalfPipe(pipe_remove[0], &success, sizeof(success));
2185
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 CloseReturnPipe(pipe_remove);
2186
2187
3/6
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 24 times.
✗ Branch 8 not taken.
24 unlink((cache_dir_ + "/" + hash.MakePathWithoutSuffix()).c_str());
2188 24 }
2189
2190
2191 264 void PosixQuotaManager::Spawn() {
2192
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 248 times.
264 if (spawned_)
2193 16 return;
2194
2195 248 if (pthread_create(&thread_lru_, NULL, MainCommandServer,
2196 static_cast<void *>(this))
2197
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 248 times.
248 != 0) {
2198 PANIC(kLogDebug, "could not create lru thread");
2199 }
2200
2201 248 spawned_ = true;
2202 }
2203
2204
2205 /**
2206 * Updates the sequence number of the file specified by the hash.
2207 */
2208 400458 void PosixQuotaManager::Touch(const shash::Any &hash) {
2209 400458 LruCommand cmd;
2210 400458 cmd.command_type = kTouch;
2211
1/2
✓ Branch 1 taken 400458 times.
✗ Branch 2 not taken.
400458 cmd.StoreHash(hash);
2212
1/2
✓ Branch 1 taken 400458 times.
✗ Branch 2 not taken.
400458 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2213 400458 }
2214
2215
2216 688 void PosixQuotaManager::UnbindReturnPipe(int pipe_wronly) {
2217
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 680 times.
688 if (shared_)
2218 8 close(pipe_wronly);
2219 688 }
2220
2221
2222 56 void PosixQuotaManager::UnlinkReturnPipe(int pipe_wronly) {
2223
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 32 times.
56 if (shared_)
2224
2/4
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 24 times.
✗ Branch 6 not taken.
24 unlink((workspace_dir_ + "/pipe" + StringifyInt(pipe_wronly)).c_str());
2225 56 }
2226
2227
2228 457 void PosixQuotaManager::Unpin(const shash::Any &hash) {
2229
2/4
✓ Branch 1 taken 457 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 457 times.
✗ Branch 6 not taken.
457 LogCvmfs(kLogQuota, kLogDebug, "Unpin %s", hash.ToString().c_str());
2230
2231 457 LruCommand cmd;
2232 457 cmd.command_type = kUnpin;
2233
1/2
✓ Branch 1 taken 457 times.
✗ Branch 2 not taken.
457 cmd.StoreHash(hash);
2234
1/2
✓ Branch 1 taken 457 times.
✗ Branch 2 not taken.
457 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2235 457 }
2236
2237
2238 16 void PosixQuotaManager::UnregisterBackChannel(int back_channel[2],
2239 const string &channel_id) {
2240
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (protocol_revision_ >= 1) {
2241
1/2
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 shash::Md5 hash = shash::Md5(shash::AsciiPtr(channel_id));
2242
2243 16 LruCommand cmd;
2244 16 cmd.command_type = kUnregisterBackChannel;
2245 // Not StoreHash(). This is an MD5 hash.
2246 16 memcpy(cmd.digest, hash.digest, hash.GetDigestSize());
2247
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2248
2249 // Writer's end will be closed by cache manager, FIFO is already unlinked
2250
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 close(back_channel[0]);
2251 } else {
2252 ClosePipe(back_channel);
2253 }
2254 16 }
2255
2256 801016 void PosixQuotaManager::ManagedReadHalfPipe(int fd, void *buf, size_t nbyte) {
2257
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 801016 times.
801016 const unsigned timeout_ms = cachemgr_pid_ ? 1000 : 0;
2258 801016 bool result = false;
2259 do {
2260 801016 result = ReadHalfPipe(fd, buf, nbyte, timeout_ms);
2261 // try only as long as the cachemgr is still alive
2262
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 801016 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 801016 times.
801016 } while (!result && getpgid(cachemgr_pid_) >= 0);
2263
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 801016 times.
801016 if (!result) {
2264 PANIC(kLogStderr,
2265 "Error: quota manager could not read from cachemanager pipe");
2266 }
2267 801016 }
2268
2269 void *PosixQuotaManager::CollectMountpointsHashes(void *data) {
2270 #ifndef __APPLE__
2271 pthread_setname_np(pthread_self(), "hash_collector");
2272 auto *handler = static_cast<CollectorHandler *>(data);
2273
2274 const std::string mountpoint = handler->mp[handler->i];
2275 ssize_t n = getxattr(mountpoint.c_str(), "user.list_open_hashes", nullptr, 0);
2276 if (n < 0) {
2277 pthread_exit(nullptr);
2278 }
2279 std::vector<char> buf((size_t)n);
2280 n = getxattr(mountpoint.c_str(), "user.list_open_hashes", buf.data(),
2281 buf.size());
2282 if (n < 0) {
2283 pthread_exit(nullptr);
2284 }
2285
2286 std::vector<std::string> hash_strs;
2287 std::string hash_str;
2288 for (const char c : buf) {
2289 if (c == '\n') {
2290 hash_strs.push_back(hash_str);
2291 hash_str.clear();
2292 } else {
2293 hash_str += c;
2294 }
2295 }
2296 const MutexLockGuard lock_guard(handler->l);
2297 for (auto hash_str : hash_strs) {
2298 handler->of.push_back(shash::Short( shash::MkFromHexPtr(shash::HexPtr(hash_str)) ));
2299 }
2300 #endif
2301 pthread_exit(nullptr);
2302 }
2303
2304 std::vector<shash::Short> PosixQuotaManager::CollectAllOpenHashes() {
2305 std::vector<CollectorHandler *> handlers;
2306 std::vector<pthread_t *> threads;
2307 open_files_.clear();
2308 #ifndef __APPLE__
2309 auto &&a_after_b = [](const struct timespec a, const struct timespec b) {
2310 return (a.tv_sec > b.tv_sec) ? true : false;
2311 };
2312
2313 for (size_t i = 0; i < mountpoints_.size(); ++i) {
2314 handlers.push_back(
2315 new CollectorHandler{open_files_, mountpoints_, lock_open_files_, i});
2316 threads.push_back(new pthread_t);
2317 }
2318
2319 const int retval = pthread_mutex_init(lock_open_files_, NULL);
2320 assert(retval == 0);
2321
2322 for (size_t i = 0; i < mountpoints_.size(); ++i) {
2323 pthread_create(threads[i], nullptr, CollectMountpointsHashes, handlers[i]);
2324 }
2325
2326 std::vector<bool> joined(handlers.size(), false);
2327 struct timespec reference, current;
2328 clock_gettime(CLOCK_REALTIME, &reference);
2329 clock_gettime(CLOCK_REALTIME, &current);
2330 reference.tv_sec += 10; // Give 10sec for hash collection
2331 size_t i = 0;
2332 while (
2333 (not std::all_of(joined.begin(), joined.end(), [](bool b) { return b; }))
2334 and a_after_b(reference, current)) {
2335 // as long as there are still threads that haven't joined yet
2336 // and for 10 seconds
2337 if (not joined[i]) {
2338 const int s = pthread_tryjoin_np(*threads[i], NULL);
2339 if (s == 0) {
2340 joined[i] = true;
2341 }
2342 }
2343 ++i;
2344 i = i % handlers.size();
2345 clock_gettime(CLOCK_REALTIME, &current);
2346 }
2347
2348 for (size_t i = 0; i < handlers.size(); ++i) {
2349 delete handlers[i];
2350 }
2351
2352 pthread_mutex_destroy(lock_open_files_);
2353 #endif
2354 return open_files_;
2355 }
2356
2357