GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/quota_posix.cc
Date: 2025-07-27 02:42:09
Exec Total Coverage
Lines: 926 1232 75.2%
Branches: 733 1732 42.3%

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