GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/quota_posix.cc
Date: 2025-06-29 02:35:41
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 3588 int PosixQuotaManager::BindReturnPipe(int pipe_wronly) {
65
2/2
✓ Branch 0 taken 3510 times.
✓ Branch 1 taken 78 times.
3588 if (!shared_)
66 3510 return pipe_wronly;
67
68 // Connect writer's end
69
1/2
✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
78 const int result = open(
70
2/4
✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 78 times.
✗ Branch 6 not taken.
156 (workspace_dir_ + "/pipe" + StringifyInt(pipe_wronly)).c_str(),
71 O_WRONLY | O_NONBLOCK);
72
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 39 times.
78 if (result >= 0) {
73 39 Nonblock2Block(result);
74 } else {
75 39 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
76 39 "failed to bind return pipe (%d)", errno);
77 }
78 78 return result;
79 }
80
81
82 680 void PosixQuotaManager::CheckHighPinWatermark() {
83 680 const uint64_t watermark = kHighPinWatermark * cleanup_threshold_ / 100;
84
3/4
✓ Branch 0 taken 680 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 117 times.
✓ Branch 3 taken 563 times.
680 if ((cleanup_threshold_ > 0) && (pinned_ > watermark)) {
85 117 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
86 "high watermark of pinned files (%" PRIu64 "M > %" PRIu64 "M)",
87 117 pinned_ / (1024 * 1024), watermark / (1024 * 1024));
88
2/4
✓ Branch 2 taken 117 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 117 times.
✗ Branch 6 not taken.
117 BroadcastBackchannels("R"); // clients: please release pinned catalogs
89 }
90 680 }
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 351 bool PosixQuotaManager::Cleanup(const uint64_t leave_size) {
126
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 351 times.
351 if (!spawned_)
127 return DoCleanup(leave_size);
128
129 bool result;
130 int pipe_cleanup[2];
131
1/2
✓ Branch 1 taken 351 times.
✗ Branch 2 not taken.
351 MakeReturnPipe(pipe_cleanup);
132
133 351 LruCommand cmd;
134 351 cmd.command_type = kCleanup;
135 351 cmd.size = leave_size;
136 351 cmd.return_pipe = pipe_cleanup[1];
137
138
1/2
✓ Branch 1 taken 351 times.
✗ Branch 2 not taken.
351 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
139
1/2
✓ Branch 1 taken 351 times.
✗ Branch 2 not taken.
351 ManagedReadHalfPipe(pipe_cleanup[0], &result, sizeof(result));
140
1/2
✓ Branch 1 taken 351 times.
✗ Branch 2 not taken.
351 CloseReturnPipe(pipe_cleanup);
141
142 351 return result;
143 }
144
145
146 2865 void PosixQuotaManager::CloseDatabase() {
147
1/2
✓ Branch 0 taken 2865 times.
✗ Branch 1 not taken.
2865 if (stmt_list_catalogs_)
148 2865 sqlite3_finalize(stmt_list_catalogs_);
149
1/2
✓ Branch 0 taken 2865 times.
✗ Branch 1 not taken.
2865 if (stmt_list_pinned_)
150 2865 sqlite3_finalize(stmt_list_pinned_);
151
1/2
✓ Branch 0 taken 2865 times.
✗ Branch 1 not taken.
2865 if (stmt_list_volatile_)
152 2865 sqlite3_finalize(stmt_list_volatile_);
153
1/2
✓ Branch 0 taken 2865 times.
✗ Branch 1 not taken.
2865 if (stmt_list_)
154 2865 sqlite3_finalize(stmt_list_);
155
1/2
✓ Branch 0 taken 2865 times.
✗ Branch 1 not taken.
2865 if (stmt_lru_)
156 2865 sqlite3_finalize(stmt_lru_);
157
1/2
✓ Branch 0 taken 2865 times.
✗ Branch 1 not taken.
2865 if (stmt_rm_)
158 2865 sqlite3_finalize(stmt_rm_);
159
1/2
✓ Branch 0 taken 2865 times.
✗ Branch 1 not taken.
2865 if (stmt_rm_batch_)
160 2865 sqlite3_finalize(stmt_rm_batch_);
161
1/2
✓ Branch 0 taken 2865 times.
✗ Branch 1 not taken.
2865 if (stmt_size_)
162 2865 sqlite3_finalize(stmt_size_);
163
1/2
✓ Branch 0 taken 2865 times.
✗ Branch 1 not taken.
2865 if (stmt_touch_)
164 2865 sqlite3_finalize(stmt_touch_);
165
1/2
✓ Branch 0 taken 2865 times.
✗ Branch 1 not taken.
2865 if (stmt_unpin_)
166 2865 sqlite3_finalize(stmt_unpin_);
167
1/2
✓ Branch 0 taken 2865 times.
✗ Branch 1 not taken.
2865 if (stmt_block_)
168 2865 sqlite3_finalize(stmt_block_);
169
1/2
✓ Branch 0 taken 2865 times.
✗ Branch 1 not taken.
2865 if (stmt_unblock_)
170 2865 sqlite3_finalize(stmt_unblock_);
171
1/2
✓ Branch 0 taken 2865 times.
✗ Branch 1 not taken.
2865 if (stmt_new_)
172 2865 sqlite3_finalize(stmt_new_);
173
1/2
✓ Branch 0 taken 2865 times.
✗ Branch 1 not taken.
2865 if (database_)
174 2865 sqlite3_close(database_);
175 2865 UnlockFile(fd_lock_cachedb_);
176
177 2865 stmt_list_catalogs_ = NULL;
178 2865 stmt_list_pinned_ = NULL;
179 2865 stmt_list_volatile_ = NULL;
180 2865 stmt_list_ = NULL;
181 2865 stmt_rm_ = NULL;
182 2865 stmt_rm_batch_ = NULL;
183 2865 stmt_size_ = NULL;
184 2865 stmt_touch_ = NULL;
185 2865 stmt_unpin_ = NULL;
186 2865 stmt_block_ = NULL;
187 2865 stmt_unblock_ = NULL;
188 2865 stmt_new_ = NULL;
189 2865 database_ = NULL;
190
191 2865 pinned_chunks_.clear();
192 2865 }
193
194
195 3393 void PosixQuotaManager::CloseReturnPipe(int pipe[2]) {
196
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 3315 times.
3393 if (shared_) {
197 78 close(pipe[0]);
198 78 UnlinkReturnPipe(pipe[1]);
199 } else {
200 3315 ClosePipe(pipe);
201 }
202 3393 }
203
204
205 3901864 bool PosixQuotaManager::Contains(const string &hash_str) {
206 3901864 bool result = false;
207
208 3901864 sqlite3_bind_text(stmt_size_, 1, &hash_str[0], hash_str.length(),
209 SQLITE_STATIC);
210
2/2
✓ Branch 1 taken 443 times.
✓ Branch 2 taken 3901421 times.
3901864 if (sqlite3_step(stmt_size_) == SQLITE_ROW)
211 443 result = true;
212 3901864 sqlite3_reset(stmt_size_);
213 3901864 LogCvmfs(kLogQuota, kLogDebug, "contains %s returns %d", hash_str.c_str(),
214 result);
215
216 3901864 return result;
217 }
218
219
220 2788 void PosixQuotaManager::CheckFreeSpace() {
221
3/4
✓ Branch 0 taken 2788 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39 times.
✓ Branch 3 taken 2749 times.
2788 if ((limit_ == 0) || (gauge_ >= limit_))
222 39 return;
223
224 struct statvfs vfs_info;
225
1/2
✓ Branch 1 taken 2749 times.
✗ Branch 2 not taken.
2749 const int retval = statvfs((cache_dir_ + "/cachedb").c_str(), &vfs_info);
226
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2749 times.
2749 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 2749 const int64_t free_space_byte = vfs_info.f_bavail * vfs_info.f_bsize;
233
1/2
✓ Branch 1 taken 2749 times.
✗ Branch 2 not taken.
2749 LogCvmfs(kLogQuota, kLogDebug, "free space: %" PRId64 " MB",
234 free_space_byte / (1024 * 1024));
235
236 2749 const int64_t required_byte = limit_ - gauge_;
237
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2749 times.
2749 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 2905 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 78 times.
✓ Branch 1 taken 2827 times.
2905 if (cleanup_threshold >= limit) {
251 78 LogCvmfs(kLogQuota, kLogDebug,
252 "invalid parameters: limit %" PRIu64 ", "
253 "cleanup_threshold %" PRIu64,
254 limit, cleanup_threshold);
255 78 return NULL;
256 }
257
258 PosixQuotaManager *quota_manager = new PosixQuotaManager(
259
1/2
✓ Branch 2 taken 2827 times.
✗ Branch 3 not taken.
2827 limit, cleanup_threshold, cache_workspace);
260
261 // Initialize cache catalog
262
2/2
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 2788 times.
2827 if (!quota_manager->InitDatabase(rebuild_database)) {
263
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 delete quota_manager;
264 39 return NULL;
265 }
266 2788 quota_manager->CheckFreeSpace();
267 2788 MakePipe(quota_manager->pipe_lru_);
268
269 2788 quota_manager->protocol_revision_ = kProtocolRevision;
270 2788 quota_manager->initialized_ = true;
271 2788 return quota_manager;
272 }
273
274
275 /**
276 * Connects to a running shared local quota manager. Creates one if necessary.
277 */
278 78 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 78 string cache_dir;
285 78 string workspace_dir;
286
2/4
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 78 times.
✗ Branch 5 not taken.
78 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 78 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 78 times.
✗ Branch 5 not taken.
78 const int fd_lockfile = LockFile(workspace_dir + "/lock_cachemgr");
292
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 39 times.
78 if (fd_lockfile < 0) {
293
1/2
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 LogCvmfs(kLogQuota, kLogDebug, "could not open lock file %s (%d)",
294
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
78 (workspace_dir + "/lock_cachemgr").c_str(), errno);
295 39 return NULL;
296 }
297
298 PosixQuotaManager *quota_mgr = new PosixQuotaManager(limit, cleanup_threshold,
299
2/4
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
39 cache_workspace);
300 39 quota_mgr->shared_ = true;
301 39 quota_mgr->spawned_ = true;
302
303 // Try to connect to pipe
304
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 const string fifo_path = workspace_dir + "/cachemgr";
305
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 LogCvmfs(kLogQuota, kLogDebug, "trying to connect to existing pipe");
306
1/2
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 quota_mgr->pipe_lru_[1] = open(fifo_path.c_str(), O_WRONLY | O_NONBLOCK);
307
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 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 39 const int connect_error = errno;
354
355 // Lock file: let existing cache manager finish first
356
2/4
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
39 const int fd_lockfile_fifo = LockFile(workspace_dir + "/lock_cachemgr.fifo");
357
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 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 39 times.
✗ Branch 2 not taken.
39 UnlockFile(fd_lockfile_fifo);
365
366
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 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 39 int retval = mkfifo(fifo_path.c_str(), 0600);
373
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 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 39 times.
✗ Branch 2 not taken.
39 MakePipe(pipe_boot);
385
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 MakePipe(pipe_handshake);
386
387 39 vector<string> command_line;
388
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 command_line.push_back(exe_path);
389
2/4
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 39 times.
✗ Branch 6 not taken.
39 command_line.push_back("__cachemgr__");
390
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 command_line.push_back(cache_workspace);
391
2/4
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
39 command_line.push_back(StringifyInt(pipe_boot[1]));
392
2/4
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
39 command_line.push_back(StringifyInt(pipe_handshake[0]));
393
2/4
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
39 command_line.push_back(StringifyInt(limit));
394
2/4
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
39 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 39 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
39 command_line.push_back(StringifyInt(true)); // foreground
398
3/6
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 39 times.
✗ Branch 8 not taken.
39 command_line.push_back(StringifyInt(GetLogSyslogLevel()));
399
3/6
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 39 times.
✗ Branch 8 not taken.
39 command_line.push_back(StringifyInt(GetLogSyslogFacility()));
400
5/14
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 39 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 39 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 39 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
39 command_line.push_back(GetLogDebugFile() + ":" + GetLogMicroSyslog());
401
402 39 set<int> preserve_filedes;
403
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 preserve_filedes.insert(0);
404
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 preserve_filedes.insert(1);
405
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 preserve_filedes.insert(2);
406
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 preserve_filedes.insert(pipe_boot[1]);
407
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 preserve_filedes.insert(pipe_handshake[0]);
408
409
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 if (foreground) {
410
1/2
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 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 39 times.
39 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 39 times.
✗ Branch 2 not taken.
39 LogCvmfs(kLogQuota, kLogDebug, "new cache manager pid: %d", new_cachemgr_pid);
426 39 quota_mgr->SetCacheMgrPid(new_cachemgr_pid);
427
2/4
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 39 times.
✗ Branch 6 not taken.
39 const int fd_lockfile_rw = open((workspace_dir + "/lock_cachemgr").c_str(),
428 O_RDWR | O_TRUNC, 0600);
429 39 const unsigned magic_number = PosixQuotaManager::kLockFileMagicNumber;
430
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 const bool result_mn = SafeWrite(fd_lockfile_rw, &magic_number,
431 sizeof(magic_number));
432
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 const bool result = SafeWrite(fd_lockfile_rw, &new_cachemgr_pid,
433 sizeof(new_cachemgr_pid));
434
2/4
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 39 times.
39 if (!result || !result_mn) {
435 PANIC(kLogSyslogErr, "could not write cache manager pid to lockfile");
436 }
437
438
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 close(fd_lockfile_rw);
439 // Wait for cache manager to be ready
440
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 close(pipe_boot[1]);
441
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 close(pipe_handshake[0]);
442 char buf;
443
2/4
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 39 times.
✗ Branch 4 not taken.
39 if (read(pipe_boot[0], &buf, 1) != 1) {
444
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 UnlockFile(fd_lockfile);
445
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 close(pipe_boot[0]);
446
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 close(pipe_handshake[1]);
447
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 delete quota_mgr;
448
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
449 "cache manager did not start");
450 39 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 78 }
490
491
492 390 bool PosixQuotaManager::DoCleanup(const uint64_t leave_size) {
493
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 312 times.
390 if (gauge_ <= leave_size)
494 78 return true;
495
496 // TODO(jblomer) transaction
497
1/2
✓ Branch 1 taken 312 times.
✗ Branch 2 not taken.
312 LogCvmfs(kLogQuota, kLogSyslog | kLogDebug,
498 "clean up cache until at most %lu KB is used", leave_size / 1024);
499
1/2
✓ Branch 1 taken 312 times.
✗ Branch 2 not taken.
312 LogCvmfs(kLogQuota, kLogDebug, "gauge %" PRIu64, gauge_);
500
1/2
✓ Branch 1 taken 312 times.
✗ Branch 2 not taken.
312 cleanup_recorder_.Tick();
501
502 bool result;
503 312 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 312 int64_t max_acseq = -1;
509 do {
510
1/2
✓ Branch 1 taken 2262 times.
✗ Branch 2 not taken.
2262 sqlite3_reset(stmt_lru_);
511
3/4
✓ Branch 0 taken 312 times.
✓ Branch 1 taken 1950 times.
✓ Branch 3 taken 2262 times.
✗ Branch 4 not taken.
2574 sqlite3_bind_int64(stmt_lru_, 1,
512 312 (max_acseq == -1) ? std::numeric_limits<int64_t>::min()
513 : (max_acseq + 1));
514
515 2262 std::vector<EvictCandidate> candidates;
516
1/2
✓ Branch 1 taken 2262 times.
✗ Branch 2 not taken.
2262 candidates.reserve(kEvictBatchSize);
517 2262 string hash_str;
518 2262 unsigned i = 0;
519
3/4
✓ Branch 1 taken 2030652 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2028390 times.
✓ Branch 4 taken 2262 times.
2030652 while (sqlite3_step(stmt_lru_) == SQLITE_ROW) {
520 hash_str = reinterpret_cast<const char *>(
521
2/4
✓ Branch 1 taken 2028390 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2028390 times.
✗ Branch 5 not taken.
2028390 sqlite3_column_text(stmt_lru_, 0));
522
1/2
✓ Branch 2 taken 2028390 times.
✗ Branch 3 not taken.
2028390 LogCvmfs(kLogQuota, kLogDebug, "add %s to candidates for eviction",
523 hash_str.c_str());
524
1/2
✓ Branch 1 taken 2028390 times.
✗ Branch 2 not taken.
2028390 candidates.push_back(
525
1/2
✓ Branch 1 taken 2028390 times.
✗ Branch 2 not taken.
2028390 EvictCandidate(shash::MkFromHexPtr(shash::HexPtr(hash_str)),
526 2028390 sqlite3_column_int64(stmt_lru_, 1),
527
2/4
✓ Branch 1 taken 2028390 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2028390 times.
✗ Branch 5 not taken.
2028390 sqlite3_column_int64(stmt_lru_, 2)));
528 2028390 i++;
529 }
530
2/2
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 2223 times.
2262 if (candidates.empty()) {
531
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 LogCvmfs(kLogQuota, kLogDebug, "no more entries to evict");
532 39 break;
533 }
534
535 2223 const unsigned N = candidates.size();
536
2/2
✓ Branch 0 taken 1950351 times.
✓ Branch 1 taken 1950 times.
1952301 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 1950351 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 39 times.
✓ Branch 7 taken 1950312 times.
1950351 if (pinned_chunks_.find(candidates[i].hash) != pinned_chunks_.end()) {
541
1/2
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 hash_str = candidates[i].hash.ToString();
542
1/2
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 LogCvmfs(kLogQuota, kLogDebug, "skip %s for eviction",
543 hash_str.c_str());
544
2/4
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 39 times.
✗ Branch 6 not taken.
39 sqlite3_bind_text(stmt_block_, 1, &hash_str[0], hash_str.length(),
545 SQLITE_STATIC);
546
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 result = (sqlite3_step(stmt_block_) == SQLITE_DONE);
547
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 sqlite3_reset(stmt_block_);
548
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 assert(result);
549 39 continue;
550 }
551
552
2/4
✓ Branch 1 taken 1950312 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1950312 times.
✗ Branch 5 not taken.
3900624 trash.push_back(cache_dir_ + "/"
553
2/4
✓ Branch 2 taken 1950312 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1950312 times.
✗ Branch 6 not taken.
5850936 + candidates[i].hash.MakePathWithoutSuffix());
554 1950312 gauge_ -= candidates[i].size;
555 1950312 max_acseq = candidates[i].acseq;
556
1/2
✓ Branch 2 taken 1950312 times.
✗ Branch 3 not taken.
1950312 LogCvmfs(kLogQuota, kLogDebug, "lru cleanup %s, new gauge %" PRIu64,
557
1/2
✓ Branch 2 taken 1950312 times.
✗ Branch 3 not taken.
3900624 candidates[i].hash.ToString().c_str(), gauge_);
558
559
2/2
✓ Branch 0 taken 273 times.
✓ Branch 1 taken 1950039 times.
1950312 if (gauge_ <= leave_size)
560 273 break;
561 }
562
6/6
✓ Branch 1 taken 2223 times.
✓ Branch 2 taken 39 times.
✓ Branch 4 taken 2223 times.
✓ Branch 5 taken 39 times.
✓ Branch 6 taken 1950 times.
✓ Branch 7 taken 273 times.
4524 } while (gauge_ > leave_size);
563
564
1/2
✓ Branch 0 taken 312 times.
✗ Branch 1 not taken.
312 if (max_acseq != -1) {
565
1/2
✓ Branch 1 taken 312 times.
✗ Branch 2 not taken.
312 sqlite3_bind_int64(stmt_rm_batch_, 1, max_acseq);
566
1/2
✓ Branch 1 taken 312 times.
✗ Branch 2 not taken.
312 result = (sqlite3_step(stmt_rm_batch_) == SQLITE_DONE);
567
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 312 times.
312 assert(result);
568
1/2
✓ Branch 1 taken 312 times.
✗ Branch 2 not taken.
312 sqlite3_reset(stmt_rm_batch_);
569
570
1/2
✓ Branch 1 taken 312 times.
✗ Branch 2 not taken.
312 result = (sqlite3_step(stmt_unblock_) == SQLITE_DONE);
571
1/2
✓ Branch 1 taken 312 times.
✗ Branch 2 not taken.
312 sqlite3_reset(stmt_unblock_);
572
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 312 times.
312 assert(result);
573 }
574
575
2/4
✓ Branch 1 taken 312 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 312 times.
312 if (!EmptyTrash(trash))
576 return false;
577
578
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 273 times.
312 if (gauge_ > leave_size) {
579
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogWarn,
580 "request to clean until %" PRIu64 ", "
581 "but effective gauge is %" PRIu64,
582 leave_size, gauge_);
583 39 return false;
584 }
585 273 return true;
586 312 }
587
588 312 bool PosixQuotaManager::EmptyTrash(const std::vector<std::string> &trash) {
589
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 312 times.
312 if (trash.empty())
590 return true;
591
592
2/2
✓ Branch 0 taken 234 times.
✓ Branch 1 taken 78 times.
312 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 234 times.
234 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 234 times.
✗ Branch 1 not taken.
234 if (pid > 0)
614
1/2
✓ Branch 1 taken 234 times.
✗ Branch 2 not taken.
234 waitpid(pid, &statloc, 0);
615 else
616 return false;
617 }
618 } else { // !async_delete_
619
2/2
✓ Branch 1 taken 117 times.
✓ Branch 2 taken 78 times.
195 for (unsigned i = 0, iEnd = trash.size(); i < iEnd; ++i) {
620 117 LogCvmfs(kLogQuota, kLogDebug, "unlink %s", trash[i].c_str());
621 117 unlink(trash[i].c_str());
622 }
623 }
624 312 return true;
625 }
626
627
628 3901434 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 3901434 times.
✗ Branch 2 not taken.
3901434 const string hash_str = hash.ToString();
633
1/2
✓ Branch 3 taken 3901434 times.
✗ Branch 4 not taken.
3901434 LogCvmfs(kLogQuota, kLogDebug, "insert into lru %s, path %s, method %d",
634 hash_str.c_str(), description.c_str(), command_type);
635 3901434 const unsigned desc_length = (description.length() > kMaxDescription)
636 ? kMaxDescription
637
1/2
✓ Branch 0 taken 3901434 times.
✗ Branch 1 not taken.
3901434 : description.length();
638
639 LruCommand *cmd = reinterpret_cast<LruCommand *>(
640 3901434 alloca(sizeof(LruCommand) + desc_length));
641 3901434 new (cmd) LruCommand;
642 3901434 cmd->command_type = command_type;
643 3901434 cmd->SetSize(size);
644
1/2
✓ Branch 1 taken 3901434 times.
✗ Branch 2 not taken.
3901434 cmd->StoreHash(hash);
645 3901434 cmd->desc_length = desc_length;
646 3901434 memcpy(reinterpret_cast<char *>(cmd) + sizeof(LruCommand), &description[0],
647 desc_length);
648
1/2
✓ Branch 1 taken 3901434 times.
✗ Branch 2 not taken.
3901434 WritePipe(pipe_lru_[1], cmd, sizeof(LruCommand) + desc_length);
649 3901434 }
650
651
652 1365 vector<string> PosixQuotaManager::DoList(const CommandType list_command) {
653 1365 vector<string> result;
654
655 int pipe_list[2];
656
1/2
✓ Branch 1 taken 1365 times.
✗ Branch 2 not taken.
1365 MakeReturnPipe(pipe_list);
657 char description_buffer[kMaxDescription];
658
659 1365 LruCommand cmd;
660 1365 cmd.command_type = list_command;
661 1365 cmd.return_pipe = pipe_list[1];
662
1/2
✓ Branch 1 taken 1365 times.
✗ Branch 2 not taken.
1365 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
663
664 int length;
665 do {
666
1/2
✓ Branch 1 taken 3902886 times.
✗ Branch 2 not taken.
3902886 ManagedReadHalfPipe(pipe_list[0], &length, sizeof(length));
667
2/2
✓ Branch 0 taken 3901521 times.
✓ Branch 1 taken 1365 times.
3902886 if (length > 0) {
668
1/2
✓ Branch 1 taken 3901521 times.
✗ Branch 2 not taken.
3901521 ReadPipe(pipe_list[0], description_buffer, length);
669
2/4
✓ Branch 2 taken 3901521 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3901521 times.
✗ Branch 6 not taken.
3901521 result.push_back(string(description_buffer, length));
670 }
671
2/2
✓ Branch 0 taken 3901521 times.
✓ Branch 1 taken 1365 times.
3902886 } while (length >= 0);
672
673
1/2
✓ Branch 1 taken 1365 times.
✗ Branch 2 not taken.
1365 CloseReturnPipe(pipe_list);
674 2730 return result;
675 }
676
677
678 760 uint64_t PosixQuotaManager::GetCapacity() {
679
1/2
✓ Branch 0 taken 760 times.
✗ Branch 1 not taken.
760 if (limit_ != (uint64_t)(-1))
680 760 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 87 uint64_t PosixQuotaManager::GetMaxFileSize() {
714 87 return limit_ - cleanup_threshold_;
715 }
716
717
718 39 pid_t PosixQuotaManager::GetPid() {
719
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
39 if (!shared_ || !spawned_) {
720 39 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 39 uint32_t PosixQuotaManager::GetProtocolRevision() {
741 int pipe_revision[2];
742
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 MakeReturnPipe(pipe_revision);
743
744 39 LruCommand cmd;
745 39 cmd.command_type = kGetProtocolRevision;
746 39 cmd.return_pipe = pipe_revision[1];
747
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
748
749 uint32_t revision;
750
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 ManagedReadHalfPipe(pipe_revision[0], &revision, sizeof(revision));
751
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 CloseReturnPipe(pipe_revision);
752 39 return revision;
753 }
754
755
756 /**
757 * Queries the shared local hard disk quota manager.
758 */
759 702 void PosixQuotaManager::GetSharedStatus(uint64_t *gauge, uint64_t *pinned) {
760 int pipe_status[2];
761
1/2
✓ Branch 1 taken 702 times.
✗ Branch 2 not taken.
702 MakeReturnPipe(pipe_status);
762
763 702 LruCommand cmd;
764 702 cmd.command_type = kStatus;
765 702 cmd.return_pipe = pipe_status[1];
766
1/2
✓ Branch 1 taken 702 times.
✗ Branch 2 not taken.
702 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
767
1/2
✓ Branch 1 taken 702 times.
✗ Branch 2 not taken.
702 ManagedReadHalfPipe(pipe_status[0], gauge, sizeof(*gauge));
768
1/2
✓ Branch 1 taken 702 times.
✗ Branch 2 not taken.
702 ReadPipe(pipe_status[0], pinned, sizeof(*pinned));
769
1/2
✓ Branch 1 taken 702 times.
✗ Branch 2 not taken.
702 CloseReturnPipe(pipe_status);
770 702 }
771
772 39 bool PosixQuotaManager::SetSharedLimit(uint64_t limit) {
773 int pipe_set_limit[2];
774 bool result;
775
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 MakeReturnPipe(pipe_set_limit);
776
777 39 LruCommand cmd;
778 39 cmd.command_type = kSetLimit;
779 39 cmd.size = limit;
780 39 cmd.return_pipe = pipe_set_limit[1];
781
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
782
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 ReadHalfPipe(pipe_set_limit[0], &result, sizeof(result));
783
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 CloseReturnPipe(pipe_set_limit);
784 39 return result;
785 }
786
787
788 39 bool PosixQuotaManager::SetLimit(uint64_t size) {
789
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 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 39 return SetSharedLimit(size);
798 }
799
800 2066 uint64_t PosixQuotaManager::GetSize() {
801
2/2
✓ Branch 0 taken 1442 times.
✓ Branch 1 taken 624 times.
2066 if (!spawned_)
802 1442 return gauge_;
803 uint64_t gauge, size_pinned;
804
1/2
✓ Branch 1 taken 624 times.
✗ Branch 2 not taken.
624 GetSharedStatus(&gauge, &size_pinned);
805 624 return gauge;
806 }
807
808
809 78 uint64_t PosixQuotaManager::GetSizePinned() {
810
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
78 if (!spawned_)
811 return pinned_;
812 uint64_t gauge, size_pinned;
813
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 GetSharedStatus(&gauge, &size_pinned);
814 78 return size_pinned;
815 }
816
817
818 156 uint64_t PosixQuotaManager::GetCleanupRate(uint64_t period_s) {
819
2/4
✓ Branch 0 taken 156 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 156 times.
156 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 156 times.
✗ Branch 2 not taken.
156 MakeReturnPipe(pipe_cleanup_rate);
825 156 LruCommand cmd;
826 156 cmd.command_type = kCleanupRate;
827 156 cmd.size = period_s;
828 156 cmd.return_pipe = pipe_cleanup_rate[1];
829
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
830
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 ManagedReadHalfPipe(pipe_cleanup_rate[0], &cleanup_rate,
831 sizeof(cleanup_rate));
832
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 CloseReturnPipe(pipe_cleanup_rate);
833
834 156 return cleanup_rate;
835 }
836
837
838 3022 bool PosixQuotaManager::InitDatabase(const bool rebuild_database) {
839 3022 string sql;
840 sqlite3_stmt *stmt;
841
842
2/4
✓ Branch 1 taken 3022 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3022 times.
✗ Branch 5 not taken.
3022 fd_lock_cachedb_ = LockFile(workspace_dir_ + "/lock_cachedb");
843
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 2983 times.
3022 if (fd_lock_cachedb_ < 0) {
844
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 LogCvmfs(kLogQuota, kLogDebug, "failed to create cachedb lock");
845 39 return false;
846 }
847
848 2983 bool retry = false;
849
1/2
✓ Branch 1 taken 2983 times.
✗ Branch 2 not taken.
2983 const string db_file = cache_dir_ + "/cachedb";
850
2/2
✓ Branch 0 taken 2806 times.
✓ Branch 1 taken 177 times.
2983 if (rebuild_database) {
851
1/2
✓ Branch 2 taken 177 times.
✗ Branch 3 not taken.
177 LogCvmfs(kLogQuota, kLogDebug, "rebuild database, unlinking existing (%s)",
852 db_file.c_str());
853 177 unlink(db_file.c_str());
854
1/2
✓ Branch 1 taken 177 times.
✗ Branch 2 not taken.
177 unlink((db_file + "-journal").c_str());
855 }
856
857 2806 init_recover:
858
1/2
✓ Branch 2 taken 2983 times.
✗ Branch 3 not taken.
2983 int err = sqlite3_open(db_file.c_str(), &database_);
859
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2983 times.
2983 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 2983 times.
✗ Branch 2 not taken.
2983 " CONSTRAINT pk_properties PRIMARY KEY(key));";
876
1/2
✓ Branch 2 taken 2983 times.
✗ Branch 3 not taken.
2983 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
877
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2983 times.
2983 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 2983 times.
✗ Branch 2 not taken.
2983 "ALTER TABLE cache_catalog ADD pinned INTEGER";
896
1/2
✓ Branch 2 taken 2983 times.
✗ Branch 3 not taken.
2983 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
897
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2983 times.
2983 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 2983 times.
✗ Branch 2 not taken.
2983 sql = "UPDATE cache_catalog SET pinned=0;";
909
1/2
✓ Branch 2 taken 2983 times.
✗ Branch 3 not taken.
2983 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
910
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2983 times.
2983 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 2983 times.
✗ Branch 2 not taken.
2983 "VALUES ('schema', '1.0')";
919
1/2
✓ Branch 2 taken 2983 times.
✗ Branch 3 not taken.
2983 err = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
920
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2983 times.
2983 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 2983 times.
✗ Branch 2 not taken.
2983 sql = "SELECT count(*) FROM cache_catalog;";
928
1/2
✓ Branch 2 taken 2983 times.
✗ Branch 3 not taken.
2983 sqlite3_prepare_v2(database_, sql.c_str(), -1, &stmt, NULL);
929
2/4
✓ Branch 1 taken 2983 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2983 times.
✗ Branch 4 not taken.
2983 if (sqlite3_step(stmt) == SQLITE_ROW) {
930
6/8
✓ Branch 1 taken 2983 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 156 times.
✓ Branch 4 taken 2827 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 156 times.
✓ Branch 7 taken 2827 times.
✓ Branch 8 taken 156 times.
2983 if ((sqlite3_column_int64(stmt, 0)) == 0 || rebuild_database) {
931
1/2
✓ Branch 1 taken 2827 times.
✗ Branch 2 not taken.
2827 LogCvmfs(kLogCvmfs, kLogDebug,
932 "CernVM-FS: building lru cache database...");
933
3/4
✓ Branch 1 taken 2827 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 117 times.
✓ Branch 4 taken 2710 times.
2827 if (!RebuildDatabase()) {
934
1/2
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
117 LogCvmfs(kLogQuota, kLogDebug,
935 "could not build cache database from file system");
936
1/2
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
117 sqlite3_finalize(stmt);
937 117 goto init_database_fail;
938 }
939 }
940
1/2
✓ Branch 1 taken 2866 times.
✗ Branch 2 not taken.
2866 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 2866 times.
✗ Branch 2 not taken.
2866 sql = "SELECT sum(size) FROM cache_catalog;";
949
1/2
✓ Branch 2 taken 2866 times.
✗ Branch 3 not taken.
2866 sqlite3_prepare_v2(database_, sql.c_str(), -1, &stmt, NULL);
950
2/4
✓ Branch 1 taken 2866 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2866 times.
✗ Branch 4 not taken.
2866 if (sqlite3_step(stmt) == SQLITE_ROW) {
951
1/2
✓ Branch 1 taken 2866 times.
✗ Branch 2 not taken.
2866 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 2866 times.
✗ Branch 2 not taken.
2866 sqlite3_finalize(stmt);
958
959 // Highest seq-no?
960
1/2
✓ Branch 1 taken 2866 times.
✗ Branch 2 not taken.
2866 sql = "SELECT coalesce(max(acseq & (~(1<<63))), 0) FROM cache_catalog;";
961
1/2
✓ Branch 2 taken 2866 times.
✗ Branch 3 not taken.
2866 sqlite3_prepare_v2(database_, sql.c_str(), -1, &stmt, NULL);
962
2/4
✓ Branch 1 taken 2866 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2866 times.
✗ Branch 4 not taken.
2866 if (sqlite3_step(stmt) == SQLITE_ROW) {
963
1/2
✓ Branch 1 taken 2866 times.
✗ Branch 2 not taken.
2866 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 2866 times.
✗ Branch 2 not taken.
2866 sqlite3_finalize(stmt);
970
971 // Prepare touch, new, remove statements
972
1/2
✓ Branch 1 taken 2866 times.
✗ Branch 2 not taken.
2866 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 2866 times.
✗ Branch 2 not taken.
2866 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 2866 times.
✗ Branch 2 not taken.
2866 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 2866 times.
✗ Branch 2 not taken.
2866 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 2866 times.
✗ Branch 2 not taken.
2866 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 2866 times.
✗ Branch 2 not taken.
2866 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 2866 times.
✗ Branch 2 not taken.
2866 sqlite3_prepare_v2(database_, "DELETE FROM cache_catalog WHERE sha1=:sha1;",
997 -1, &stmt_rm_, NULL);
998
1/2
✓ Branch 1 taken 2866 times.
✗ Branch 2 not taken.
2866 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 2866 times.
✗ Branch 3 not taken.
2866 sqlite3_prepare_v2(database_,
1002
1/2
✓ Branch 2 taken 2866 times.
✗ Branch 3 not taken.
5732 (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 2866 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2866 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2866 times.
✗ Branch 8 not taken.
11464 + StringifyInt(kEvictBatchSize) + ";")
1007 .c_str(),
1008 -1, &stmt_lru_, NULL);
1009
1/2
✓ Branch 2 taken 2866 times.
✗ Branch 3 not taken.
2866 sqlite3_prepare_v2(database_,
1010 ("SELECT path FROM cache_catalog WHERE type="
1011
3/6
✓ Branch 1 taken 2866 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2866 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2866 times.
✗ Branch 8 not taken.
5732 + StringifyInt(kFileRegular) + ";")
1012 .c_str(),
1013 -1, &stmt_list_, NULL);
1014
1/2
✓ Branch 1 taken 2866 times.
✗ Branch 2 not taken.
2866 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 2866 times.
✗ Branch 2 not taken.
2866 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 2866 times.
✗ Branch 3 not taken.
2866 sqlite3_prepare_v2(database_,
1021 ("SELECT path FROM cache_catalog WHERE type="
1022
3/6
✓ Branch 1 taken 2866 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2866 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2866 times.
✗ Branch 8 not taken.
5732 + StringifyInt(kFileCatalog) + ";")
1023 .c_str(),
1024 -1, &stmt_list_catalogs_, NULL);
1025 2866 return true;
1026
1027 117 init_database_fail:
1028
1/2
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
117 sqlite3_close(database_);
1029 117 database_ = NULL;
1030
1/2
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
117 UnlockFile(fd_lock_cachedb_);
1031 117 return false;
1032 3022 }
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 3900771 void PosixQuotaManager::Insert(const shash::Any &any_hash,
1040 const uint64_t size,
1041 const string &description) {
1042 3900771 DoInsert(any_hash, size, description, kInsert);
1043 3900771 }
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 156 void PosixQuotaManager::InsertVolatile(const shash::Any &any_hash,
1052 const uint64_t size,
1053 const string &description) {
1054 156 DoInsert(any_hash, size, description, kInsertVolatile);
1055 156 }
1056
1057
1058 /**
1059 * Lists all path names from the cache db.
1060 */
1061 819 vector<string> PosixQuotaManager::List() { return DoList(kList); }
1062
1063
1064 /**
1065 * Lists all pinned files from the cache db.
1066 */
1067 312 vector<string> PosixQuotaManager::ListPinned() { return DoList(kListPinned); }
1068
1069
1070 /**
1071 * Lists all sqlite catalog files from the cache db.
1072 */
1073 117 vector<string> PosixQuotaManager::ListCatalogs() {
1074 117 return DoList(kListCatalogs);
1075 }
1076
1077
1078 /**
1079 * Lists only files flagged as volatile (priority removal)
1080 */
1081 117 vector<string> PosixQuotaManager::ListVolatile() {
1082 117 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 1209 void *PosixQuotaManager::MainCommandServer(void *data) {
1225 1209 PosixQuotaManager *quota_mgr = static_cast<PosixQuotaManager *>(data);
1226
1227
1/2
✓ Branch 1 taken 1209 times.
✗ Branch 2 not taken.
1209 LogCvmfs(kLogQuota, kLogDebug, "starting quota manager");
1228
1/2
✓ Branch 1 taken 1209 times.
✗ Branch 2 not taken.
1209 sqlite3_soft_heap_limit(quota_mgr->kSqliteMemPerThread);
1229
1230
2/2
✓ Branch 1 taken 38688 times.
✓ Branch 2 taken 1209 times.
39897 LruCommand command_buffer[kCommandBufferSize];
1231 char description_buffer[kCommandBufferSize * kMaxDescription];
1232 1209 unsigned num_commands = 0;
1233
1234
1/2
✓ Branch 1 taken 5856279 times.
✗ Branch 2 not taken.
5856279 while (read(quota_mgr->pipe_lru_[0], &command_buffer[num_commands],
1235 sizeof(command_buffer[0]))
1236
2/2
✓ Branch 0 taken 5855070 times.
✓ Branch 1 taken 1209 times.
5856279 == sizeof(command_buffer[0])) {
1237 5855070 const CommandType command_type = command_buffer[num_commands].command_type;
1238
1/2
✓ Branch 1 taken 5855070 times.
✗ Branch 2 not taken.
5855070 LogCvmfs(kLogQuota, kLogDebug, "received command %d", command_type);
1239 5855070 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 1954407 times.
✓ Branch 1 taken 3900663 times.
✓ Branch 2 taken 1954251 times.
✓ Branch 3 taken 156 times.
5855070 if ((command_type == kInsert) || (command_type == kInsertVolatile)
1243
4/4
✓ Branch 0 taken 1954173 times.
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 429 times.
✓ Branch 3 taken 1953744 times.
1954251 || (command_type == kPin) || (command_type == kPinRegular)) {
1244 3901326 const int desc_length = command_buffer[num_commands].desc_length;
1245 3901326 ReadPipe(quota_mgr->pipe_lru_[0],
1246
1/2
✓ Branch 1 taken 3901326 times.
✗ Branch 2 not taken.
3901326 &description_buffer[kMaxDescription * num_commands],
1247 desc_length);
1248 }
1249
1250 // The protocol revision is returned immediately
1251
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 5855031 times.
5855070 if (command_type == kGetProtocolRevision) {
1252
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 const int return_pipe = quota_mgr->BindReturnPipe(
1253 command_buffer[num_commands].return_pipe);
1254
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if (return_pipe < 0)
1255 continue;
1256
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 WritePipe(return_pipe, &quota_mgr->kProtocolRevision,
1257 sizeof(quota_mgr->kProtocolRevision));
1258
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 quota_mgr->UnbindReturnPipe(return_pipe);
1259 39 continue;
1260 39 }
1261
1262 // The cleanup rate is returned immediately
1263
2/2
✓ Branch 0 taken 156 times.
✓ Branch 1 taken 5854875 times.
5855031 if (command_type == kCleanupRate) {
1264
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 const int return_pipe = quota_mgr->BindReturnPipe(
1265 command_buffer[num_commands].return_pipe);
1266
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 156 times.
156 if (return_pipe < 0)
1267 continue;
1268 const uint64_t
1269 156 period_s = size; // use the size field to transmit the period
1270
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 uint64_t rate = quota_mgr->cleanup_recorder_.GetNoTicks(period_s);
1271
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 WritePipe(return_pipe, &rate, sizeof(rate));
1272
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 quota_mgr->UnbindReturnPipe(return_pipe);
1273 156 continue;
1274 156 }
1275
1276
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 5854836 times.
5854875 if (command_type == kSetLimit) {
1277
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 const int return_pipe = quota_mgr->BindReturnPipe(
1278 command_buffer[num_commands].return_pipe);
1279
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if (return_pipe < 0)
1280 continue;
1281 39 quota_mgr->limit_ = size; // use the size field to transmit the size
1282 39 quota_mgr->cleanup_threshold_ = size / 2;
1283
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 LogCvmfs(kLogQuota, kLogDebug | kLogSyslog,
1284 "Quota limit set to %lu / threshold %lu", quota_mgr->limit_,
1285 quota_mgr->cleanup_threshold_);
1286 39 bool ret = true;
1287
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 WritePipe(return_pipe, &ret, sizeof(ret));
1288
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 quota_mgr->UnbindReturnPipe(return_pipe);
1289 39 continue;
1290 39 }
1291
1292 // Reservations are handled immediately and "out of band"
1293
2/2
✓ Branch 0 taken 546 times.
✓ Branch 1 taken 5854290 times.
5854836 if (command_type == kReserve) {
1294 546 bool success = true;
1295
1/2
✓ Branch 1 taken 546 times.
✗ Branch 2 not taken.
546 const int return_pipe = quota_mgr->BindReturnPipe(
1296 command_buffer[num_commands].return_pipe);
1297
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 546 times.
546 if (return_pipe < 0)
1298 continue;
1299
1300
1/2
✓ Branch 1 taken 546 times.
✗ Branch 2 not taken.
546 const shash::Any hash = command_buffer[num_commands].RetrieveHash();
1301
1/2
✓ Branch 1 taken 546 times.
✗ Branch 2 not taken.
546 const string hash_str(hash.ToString());
1302
1/2
✓ Branch 2 taken 546 times.
✗ Branch 3 not taken.
546 LogCvmfs(kLogQuota, kLogDebug, "reserve %lu bytes for %s", size,
1303 hash_str.c_str());
1304
1305
1/2
✓ Branch 1 taken 546 times.
✗ Branch 2 not taken.
546 if (quota_mgr->pinned_chunks_.find(hash)
1306
2/2
✓ Branch 2 taken 468 times.
✓ Branch 3 taken 78 times.
1092 == quota_mgr->pinned_chunks_.end()) {
1307
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 429 times.
468 if ((quota_mgr->pinned_ + size) > quota_mgr->cleanup_threshold_) {
1308
1/2
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 LogCvmfs(kLogQuota, kLogDebug,
1309 "failed to insert %s (pinned), no space", hash_str.c_str());
1310 39 success = false;
1311 } else {
1312
1/2
✓ Branch 1 taken 429 times.
✗ Branch 2 not taken.
429 quota_mgr->pinned_chunks_[hash] = size;
1313 429 quota_mgr->pinned_ += size;
1314
1/2
✓ Branch 1 taken 429 times.
✗ Branch 2 not taken.
429 quota_mgr->CheckHighPinWatermark();
1315 }
1316 }
1317
1318
1/2
✓ Branch 1 taken 546 times.
✗ Branch 2 not taken.
546 WritePipe(return_pipe, &success, sizeof(success));
1319
1/2
✓ Branch 1 taken 546 times.
✗ Branch 2 not taken.
546 quota_mgr->UnbindReturnPipe(return_pipe);
1320 546 continue;
1321 546 }
1322
1323 // Back channels are also handled out of band
1324
2/2
✓ Branch 0 taken 156 times.
✓ Branch 1 taken 5854134 times.
5854290 if (command_type == kRegisterBackChannel) {
1325
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 const int return_pipe = quota_mgr->BindReturnPipe(
1326 command_buffer[num_commands].return_pipe);
1327
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 156 times.
156 if (return_pipe < 0)
1328 continue;
1329
1330
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 quota_mgr->UnlinkReturnPipe(command_buffer[num_commands].return_pipe);
1331
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 Block2Nonblock(return_pipe); // back channels are opportunistic
1332
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 shash::Md5 hash;
1333 156 memcpy(hash.digest, command_buffer[num_commands].digest,
1334 156 shash::kDigestSizes[shash::kMd5]);
1335
1336 156 quota_mgr->LockBackChannels();
1337 const map<shash::Md5, int>::const_iterator
1338
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 iter = quota_mgr->back_channels_.find(hash);
1339
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 156 times.
156 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 156 times.
✗ Branch 2 not taken.
156 quota_mgr->back_channels_[hash] = return_pipe;
1345 156 quota_mgr->UnlockBackChannels();
1346
1347 156 char success = 'S';
1348
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 WritePipe(return_pipe, &success, sizeof(success));
1349
1/2
✓ Branch 2 taken 156 times.
✗ Branch 3 not taken.
156 LogCvmfs(kLogQuota, kLogDebug, "register back channel %s on fd %d",
1350
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
312 hash.ToString().c_str(), return_pipe);
1351
1352 156 continue;
1353 156 }
1354
1355
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 5854056 times.
5854134 if (command_type == kUnregisterBackChannel) {
1356
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 shash::Md5 hash;
1357 78 memcpy(hash.digest, command_buffer[num_commands].digest,
1358 78 shash::kDigestSizes[shash::kMd5]);
1359
1360 78 quota_mgr->LockBackChannels();
1361 const map<shash::Md5, int>::iterator iter = quota_mgr->back_channels_
1362
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 .find(hash);
1363
1/2
✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
78 if (iter != quota_mgr->back_channels_.end()) {
1364
1/2
✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
78 LogCvmfs(kLogQuota, kLogDebug, "closing back channel %s",
1365
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
156 hash.ToString().c_str());
1366
1/2
✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
78 close(iter->second);
1367
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 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 78 quota_mgr->UnlockBackChannels();
1373
1374 78 continue;
1375 78 }
1376
1377 // Unpinnings are also handled immediately with respect to the pinned gauge
1378
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 5853978 times.
5854056 if (command_type == kUnpin) {
1379
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 const shash::Any hash = command_buffer[num_commands].RetrieveHash();
1380
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 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 78 times.
✗ Branch 2 not taken.
78 .find(hash);
1384
1/2
✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
78 if (iter != quota_mgr->pinned_chunks_.end()) {
1385 78 quota_mgr->pinned_ -= iter->second;
1386
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 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 78 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 78 times.
✗ Branch 5 not taken.
156 if (!FileExists(quota_mgr->cache_dir_ + "/"
1391
4/6
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 78 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 39 times.
✓ Branch 10 taken 39 times.
234 + hash.MakePathWithoutSuffix())) {
1392
1/2
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 LogCvmfs(kLogQuota, kLogDebug,
1393 "remove orphaned pinned hash %s from cache database",
1394 hash_str.c_str());
1395
1/2
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 sqlite3_bind_text(quota_mgr->stmt_size_, 1, &hash_str[0],
1396 39 hash_str.length(), SQLITE_STATIC);
1397 int retval;
1398
2/4
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 39 times.
✗ Branch 4 not taken.
39 if ((retval = sqlite3_step(quota_mgr->stmt_size_)) == SQLITE_ROW) {
1399
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 const uint64_t size = sqlite3_column_int64(quota_mgr->stmt_size_,
1400 39 0);
1401
1/2
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 sqlite3_bind_text(quota_mgr->stmt_rm_, 1, &(hash_str[0]),
1402 39 hash_str.length(), SQLITE_STATIC);
1403
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 retval = sqlite3_step(quota_mgr->stmt_rm_);
1404
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
39 if ((retval == SQLITE_DONE) || (retval == SQLITE_OK)) {
1405 39 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 39 times.
✗ Branch 2 not taken.
39 sqlite3_reset(quota_mgr->stmt_rm_);
1411 }
1412
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 sqlite3_reset(quota_mgr->stmt_size_);
1413 }
1414 } else {
1415 LogCvmfs(kLogQuota, kLogDebug, "this chunk was not pinned");
1416 }
1417 78 }
1418
1419 // Immediate commands trigger flushing of the buffer
1420 5854056 const bool immediate_command = (command_type == kCleanup)
1421
2/2
✓ Branch 0 taken 5852886 times.
✓ Branch 1 taken 819 times.
5853705 || (command_type == kList)
1422
2/2
✓ Branch 0 taken 5852574 times.
✓ Branch 1 taken 312 times.
5852886 || (command_type == kListPinned)
1423
2/2
✓ Branch 0 taken 5852457 times.
✓ Branch 1 taken 117 times.
5852574 || (command_type == kListCatalogs)
1424
2/2
✓ Branch 0 taken 5852340 times.
✓ Branch 1 taken 117 times.
5852457 || (command_type == kListVolatile)
1425
2/2
✓ Branch 0 taken 5852223 times.
✓ Branch 1 taken 117 times.
5852340 || (command_type == kRemove)
1426
2/2
✓ Branch 0 taken 5851521 times.
✓ Branch 1 taken 702 times.
5852223 || (command_type == kStatus)
1427
1/2
✓ Branch 0 taken 5851521 times.
✗ Branch 1 not taken.
5851521 || (command_type == kLimits)
1428
3/4
✓ Branch 0 taken 5853705 times.
✓ Branch 1 taken 351 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5851521 times.
11707761 || (command_type == kPid);
1429
2/2
✓ Branch 0 taken 5851521 times.
✓ Branch 1 taken 2535 times.
5854056 if (!immediate_command)
1430 5851521 num_commands++;
1431
1432
4/4
✓ Branch 0 taken 5671263 times.
✓ Branch 1 taken 182793 times.
✓ Branch 2 taken 2535 times.
✓ Branch 3 taken 5668728 times.
5854056 if ((num_commands == kCommandBufferSize) || immediate_command) {
1433
1/2
✓ Branch 1 taken 185328 times.
✗ Branch 2 not taken.
185328 quota_mgr->ProcessCommandBunch(num_commands, command_buffer,
1434 description_buffer);
1435
2/2
✓ Branch 0 taken 182793 times.
✓ Branch 1 taken 2535 times.
185328 if (!immediate_command)
1436 182793 num_commands = 0;
1437 }
1438
1439
2/2
✓ Branch 0 taken 2535 times.
✓ Branch 1 taken 5851521 times.
5854056 if (immediate_command) {
1440 // Process cleanup, listings
1441
1/2
✓ Branch 1 taken 2535 times.
✗ Branch 2 not taken.
2535 const int return_pipe = quota_mgr->BindReturnPipe(
1442 command_buffer[num_commands].return_pipe);
1443
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2535 times.
2535 if (return_pipe < 0) {
1444 num_commands = 0;
1445 continue;
1446 }
1447
1448 int retval;
1449 2535 sqlite3_stmt *this_stmt_list = NULL;
1450
7/10
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 351 times.
✓ Branch 2 taken 819 times.
✓ Branch 3 taken 312 times.
✓ Branch 4 taken 117 times.
✓ Branch 5 taken 117 times.
✓ Branch 6 taken 702 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
2535 switch (command_type) {
1451 117 case kRemove: {
1452
1/2
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
117 const shash::Any hash = command_buffer[num_commands].RetrieveHash();
1453
1/2
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
117 const string hash_str = hash.ToString();
1454
1/2
✓ Branch 2 taken 117 times.
✗ Branch 3 not taken.
117 LogCvmfs(kLogQuota, kLogDebug, "manually removing %s",
1455 hash_str.c_str());
1456 117 bool success = false;
1457
1458
1/2
✓ Branch 2 taken 117 times.
✗ Branch 3 not taken.
117 sqlite3_bind_text(quota_mgr->stmt_size_, 1, &hash_str[0],
1459 117 hash_str.length(), SQLITE_STATIC);
1460 int retval;
1461
3/4
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 78 times.
✓ Branch 4 taken 39 times.
117 if ((retval = sqlite3_step(quota_mgr->stmt_size_)) == SQLITE_ROW) {
1462
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 const uint64_t size = sqlite3_column_int64(quota_mgr->stmt_size_,
1463 78 0);
1464
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 const uint64_t is_pinned = sqlite3_column_int64(
1465 78 quota_mgr->stmt_size_, 1);
1466
1467
1/2
✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
78 sqlite3_bind_text(quota_mgr->stmt_rm_, 1, &(hash_str[0]),
1468 78 hash_str.length(), SQLITE_STATIC);
1469
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 retval = sqlite3_step(quota_mgr->stmt_rm_);
1470
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
78 if ((retval == SQLITE_DONE) || (retval == SQLITE_OK)) {
1471 78 success = true;
1472 78 quota_mgr->gauge_ -= size;
1473
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 39 times.
78 if (is_pinned) {
1474
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 quota_mgr->pinned_chunks_.erase(hash);
1475 39 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 78 times.
✗ Branch 2 not taken.
78 sqlite3_reset(quota_mgr->stmt_rm_);
1482 } else {
1483 // File does not exist
1484 39 success = true;
1485 }
1486
1/2
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
117 sqlite3_reset(quota_mgr->stmt_size_);
1487
1488
1/2
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
117 WritePipe(return_pipe, &success, sizeof(success));
1489 117 break;
1490 117 }
1491 351 case kCleanup:
1492
1/2
✓ Branch 1 taken 351 times.
✗ Branch 2 not taken.
351 retval = quota_mgr->DoCleanup(size);
1493
1/2
✓ Branch 1 taken 351 times.
✗ Branch 2 not taken.
351 WritePipe(return_pipe, &retval, sizeof(retval));
1494 351 break;
1495 819 case kList:
1496
1/2
✓ Branch 0 taken 819 times.
✗ Branch 1 not taken.
819 if (!this_stmt_list)
1497 819 this_stmt_list = quota_mgr->stmt_list_;
1498 case kListPinned:
1499
2/2
✓ Branch 0 taken 312 times.
✓ Branch 1 taken 819 times.
1131 if (!this_stmt_list)
1500 312 this_stmt_list = quota_mgr->stmt_list_pinned_;
1501 case kListCatalogs:
1502
2/2
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 1131 times.
1248 if (!this_stmt_list)
1503 117 this_stmt_list = quota_mgr->stmt_list_catalogs_;
1504 case kListVolatile:
1505
2/2
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 1248 times.
1365 if (!this_stmt_list)
1506 117 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 3902886 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3901521 times.
✓ Branch 4 taken 1365 times.
3902886 while (sqlite3_step(this_stmt_list) == SQLITE_ROW) {
1511
1/2
✓ Branch 2 taken 3901521 times.
✗ Branch 3 not taken.
3901521 string path = "(NULL)";
1512
2/4
✓ Branch 1 taken 3901521 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3901521 times.
✗ Branch 4 not taken.
3901521 if (sqlite3_column_type(this_stmt_list, 0) != SQLITE_NULL) {
1513 7803042 path = string(reinterpret_cast<const char *>(
1514
2/4
✓ Branch 1 taken 3901521 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3901521 times.
✗ Branch 5 not taken.
3901521 sqlite3_column_text(this_stmt_list, 0)));
1515 }
1516 3901521 length = path.length();
1517
1/2
✓ Branch 1 taken 3901521 times.
✗ Branch 2 not taken.
3901521 WritePipe(return_pipe, &length, sizeof(length));
1518
1/2
✓ Branch 0 taken 3901521 times.
✗ Branch 1 not taken.
3901521 if (length > 0)
1519
2/4
✓ Branch 1 taken 3901521 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3901521 times.
✗ Branch 5 not taken.
3901521 WritePipe(return_pipe, &path[0], length);
1520 3901521 }
1521 1365 length = -1;
1522
1/2
✓ Branch 1 taken 1365 times.
✗ Branch 2 not taken.
1365 WritePipe(return_pipe, &length, sizeof(length));
1523
1/2
✓ Branch 1 taken 1365 times.
✗ Branch 2 not taken.
1365 sqlite3_reset(this_stmt_list);
1524 1365 break;
1525 702 case kStatus:
1526
1/2
✓ Branch 1 taken 702 times.
✗ Branch 2 not taken.
702 WritePipe(return_pipe, &quota_mgr->gauge_, sizeof(quota_mgr->gauge_));
1527
1/2
✓ Branch 1 taken 702 times.
✗ Branch 2 not taken.
702 WritePipe(return_pipe, &quota_mgr->pinned_,
1528 sizeof(quota_mgr->pinned_));
1529 702 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 2535 times.
✗ Branch 2 not taken.
2535 quota_mgr->UnbindReturnPipe(return_pipe);
1544 2535 num_commands = 0;
1545 }
1546 }
1547
1548
1/2
✓ Branch 1 taken 1209 times.
✗ Branch 2 not taken.
1209 LogCvmfs(kLogQuota, kLogDebug, "stopping cache manager (%d)", errno);
1549
1/2
✓ Branch 1 taken 1209 times.
✗ Branch 2 not taken.
1209 close(quota_mgr->pipe_lru_[0]);
1550
1/2
✓ Branch 1 taken 1209 times.
✗ Branch 2 not taken.
1209 quota_mgr->ProcessCommandBunch(num_commands, command_buffer,
1551 description_buffer);
1552
1553 // Unpin
1554 1209 command_buffer[0].command_type = kTouch;
1555 1209 for (map<shash::Any, uint64_t>::const_iterator
1556 1209 i = quota_mgr->pinned_chunks_.begin(),
1557 1209 iEnd = quota_mgr->pinned_chunks_.end();
1558
2/2
✓ Branch 1 taken 390 times.
✓ Branch 2 taken 1209 times.
1599 i != iEnd;
1559 390 ++i) {
1560
1/2
✓ Branch 2 taken 390 times.
✗ Branch 3 not taken.
390 command_buffer[0].StoreHash(i->first);
1561
1/2
✓ Branch 1 taken 390 times.
✗ Branch 2 not taken.
390 quota_mgr->ProcessCommandBunch(1, command_buffer, description_buffer);
1562 }
1563
1564 1209 return NULL;
1565 }
1566
1567
1568 3588 void PosixQuotaManager::MakeReturnPipe(int pipe[2]) {
1569
2/2
✓ Branch 0 taken 3471 times.
✓ Branch 1 taken 117 times.
3588 if (!shared_) {
1570 3471 MakePipe(pipe);
1571 3471 return;
1572 }
1573
1574 // Create FIFO in cache directory, store path name (number) in pipe write end
1575 117 int i = 0;
1576 int retval;
1577 do {
1578
2/4
✓ Branch 2 taken 156 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 156 times.
✗ Branch 6 not taken.
156 retval = mkfifo((workspace_dir_ + "/pipe" + StringifyInt(i)).c_str(), 0600);
1579 156 pipe[1] = i;
1580 156 i++;
1581
3/4
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 117 times.
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
156 } while ((retval == -1) && (errno == EEXIST));
1582
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 117 times.
117 assert(retval == 0);
1583
1584 // Connect reader's end
1585
3/6
✓ Branch 2 taken 117 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 117 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 117 times.
✗ Branch 10 not taken.
117 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 117 times.
117 assert(pipe[0] >= 0);
1588 117 Nonblock2Block(pipe[0]);
1589 }
1590
1591
1592 2983 void PosixQuotaManager::ParseDirectories(const std::string cache_workspace,
1593 std::string *cache_dir,
1594 std::string *workspace_dir) {
1595
1/2
✓ Branch 1 taken 2983 times.
✗ Branch 2 not taken.
2983 vector<string> dir_tokens(SplitString(cache_workspace, ':'));
1596
2/3
✓ Branch 1 taken 2905 times.
✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
2983 switch (dir_tokens.size()) {
1597 2905 case 1:
1598
2/4
✓ Branch 2 taken 2905 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2905 times.
✗ Branch 6 not taken.
2905 *cache_dir = *workspace_dir = dir_tokens[0];
1599 2905 break;
1600 78 case 2:
1601
1/2
✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
78 *cache_dir = dir_tokens[0];
1602
1/2
✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
78 *workspace_dir = dir_tokens[1];
1603 78 break;
1604 default:
1605 PANIC(NULL);
1606 }
1607 2983 }
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 1045 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 78 times.
✓ Branch 1 taken 967 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 78 times.
1045 assert((size > 0) || !is_catalog);
1620
1621
1/2
✓ Branch 1 taken 1045 times.
✗ Branch 2 not taken.
1045 const string hash_str = hash.ToString();
1622
1/2
✓ Branch 3 taken 1045 times.
✗ Branch 4 not taken.
1045 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 499 times.
✓ Branch 1 taken 546 times.
1045 if (!spawned_) {
1627 // Code duplication here
1628
3/4
✓ Branch 2 taken 499 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 329 times.
✓ Branch 6 taken 170 times.
499 if (pinned_chunks_.find(hash) == pinned_chunks_.end()) {
1629
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 251 times.
329 if (pinned_ + size > cleanup_threshold_) {
1630
1/2
✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
78 LogCvmfs(kLogQuota, kLogDebug, "failed to insert %s (pinned), no space",
1631 hash_str.c_str());
1632 78 return false;
1633 } else {
1634
1/2
✓ Branch 1 taken 251 times.
✗ Branch 2 not taken.
251 pinned_chunks_[hash] = size;
1635 251 pinned_ += size;
1636
1/2
✓ Branch 1 taken 251 times.
✗ Branch 2 not taken.
251 CheckHighPinWatermark();
1637 }
1638 }
1639
1/2
✓ Branch 1 taken 421 times.
✗ Branch 2 not taken.
421 const bool exists = Contains(hash_str);
1640
4/4
✓ Branch 0 taken 251 times.
✓ Branch 1 taken 170 times.
✓ Branch 2 taken 39 times.
✓ Branch 3 taken 212 times.
421 if (!exists && (gauge_ + size > limit_)) {
1641
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 LogCvmfs(kLogQuota, kLogDebug, "over limit, gauge %lu, file size %lu",
1642 gauge_, size);
1643
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 const int retval = DoCleanup(cleanup_threshold_);
1644
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 assert(retval != 0);
1645 }
1646
1/2
✓ Branch 3 taken 421 times.
✗ Branch 4 not taken.
421 sqlite3_bind_text(stmt_new_, 1, &hash_str[0], hash_str.length(),
1647 SQLITE_STATIC);
1648
1/2
✓ Branch 1 taken 421 times.
✗ Branch 2 not taken.
421 sqlite3_bind_int64(stmt_new_, 2, size);
1649
1/2
✓ Branch 1 taken 421 times.
✗ Branch 2 not taken.
421 sqlite3_bind_int64(stmt_new_, 3, seq_++);
1650
1/2
✓ Branch 3 taken 421 times.
✗ Branch 4 not taken.
421 sqlite3_bind_text(stmt_new_, 4, &description[0], description.length(),
1651 SQLITE_STATIC);
1652
3/4
✓ Branch 0 taken 304 times.
✓ Branch 1 taken 117 times.
✓ Branch 3 taken 421 times.
✗ Branch 4 not taken.
421 sqlite3_bind_int64(stmt_new_, 5, is_catalog ? kFileCatalog : kFileRegular);
1653
1/2
✓ Branch 1 taken 421 times.
✗ Branch 2 not taken.
421 sqlite3_bind_int64(stmt_new_, 6, 1);
1654
1/2
✓ Branch 1 taken 421 times.
✗ Branch 2 not taken.
421 const int retval = sqlite3_step(stmt_new_);
1655
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 421 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
421 assert((retval == SQLITE_DONE) || (retval == SQLITE_OK));
1656
1/2
✓ Branch 1 taken 421 times.
✗ Branch 2 not taken.
421 sqlite3_reset(stmt_new_);
1657
2/2
✓ Branch 0 taken 251 times.
✓ Branch 1 taken 170 times.
421 if (!exists)
1658 251 gauge_ += size;
1659 421 return true;
1660 }
1661
1662 int pipe_reserve[2];
1663
1/2
✓ Branch 1 taken 546 times.
✗ Branch 2 not taken.
546 MakeReturnPipe(pipe_reserve);
1664
1665 546 LruCommand cmd;
1666 546 cmd.command_type = kReserve;
1667 546 cmd.SetSize(size);
1668
1/2
✓ Branch 1 taken 546 times.
✗ Branch 2 not taken.
546 cmd.StoreHash(hash);
1669 546 cmd.return_pipe = pipe_reserve[1];
1670
1/2
✓ Branch 1 taken 546 times.
✗ Branch 2 not taken.
546 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
1671 bool result;
1672
1/2
✓ Branch 1 taken 546 times.
✗ Branch 2 not taken.
546 ManagedReadHalfPipe(pipe_reserve[0], &result, sizeof(result));
1673
1/2
✓ Branch 1 taken 546 times.
✗ Branch 2 not taken.
546 CloseReturnPipe(pipe_reserve);
1674
1675
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 507 times.
546 if (!result)
1676 39 return false;
1677
3/4
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 429 times.
✓ Branch 3 taken 507 times.
✗ Branch 4 not taken.
507 DoInsert(hash, size, description, is_catalog ? kPin : kPinRegular);
1678
1679 507 return true;
1680 1045 }
1681
1682
1683 2905 PosixQuotaManager::PosixQuotaManager(const uint64_t limit,
1684 const uint64_t cleanup_threshold,
1685 2905 const string &cache_workspace)
1686 2905 : shared_(false)
1687 2905 , spawned_(false)
1688 2905 , limit_(limit)
1689 2905 , cleanup_threshold_(cleanup_threshold)
1690 2905 , gauge_(0)
1691 2905 , pinned_(0)
1692 2905 , seq_(0)
1693 2905 , cache_dir_() // initialized in body
1694 2905 , workspace_dir_() // initialized in body
1695 2905 , fd_lock_cachedb_(-1)
1696 2905 , async_delete_(true)
1697 2905 , cachemgr_pid_(0)
1698 2905 , database_(NULL)
1699 2905 , stmt_touch_(NULL)
1700 2905 , stmt_unpin_(NULL)
1701 2905 , stmt_block_(NULL)
1702 2905 , stmt_unblock_(NULL)
1703 2905 , stmt_new_(NULL)
1704 2905 , stmt_lru_(NULL)
1705 2905 , stmt_size_(NULL)
1706 2905 , stmt_rm_(NULL)
1707 2905 , stmt_rm_batch_(NULL)
1708 2905 , stmt_list_(NULL)
1709 2905 , stmt_list_pinned_(NULL)
1710 2905 , stmt_list_catalogs_(NULL)
1711 2905 , stmt_list_volatile_(NULL)
1712 5810 , initialized_(false) {
1713
2/4
✓ Branch 1 taken 2905 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2905 times.
✗ Branch 5 not taken.
2905 ParseDirectories(cache_workspace, &cache_dir_, &workspace_dir_);
1714 2905 pipe_lru_[0] = pipe_lru_[1] = -1;
1715
1/2
✓ Branch 1 taken 2905 times.
✗ Branch 2 not taken.
2905 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 2905 times.
✗ Branch 2 not taken.
2905 cleanup_recorder_.AddRecorder(60, 90 * 60);
1718 // last 18 hours with 20 min resolution
1719
1/2
✓ Branch 1 taken 2905 times.
✗ Branch 2 not taken.
2905 cleanup_recorder_.AddRecorder(20 * 60, 60 * 60 * 18);
1720 // last 4 days with hour resolution
1721
1/2
✓ Branch 1 taken 2905 times.
✗ Branch 2 not taken.
2905 cleanup_recorder_.AddRecorder(60 * 60, 60 * 60 * 24 * 4);
1722 2905 }
1723
1724
1725 11616 PosixQuotaManager::~PosixQuotaManager() {
1726
2/2
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 2787 times.
5808 if (!initialized_)
1727 234 return;
1728
1729
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2787 times.
5574 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 1209 times.
✓ Branch 1 taken 1578 times.
5574 if (spawned_) {
1736 2418 char fin = 0;
1737 2418 WritePipe(pipe_lru_[1], &fin, 1);
1738 2418 close(pipe_lru_[1]);
1739 2418 pthread_join(thread_lru_, NULL);
1740 } else {
1741 3156 ClosePipe(pipe_lru_);
1742 }
1743
1744 5574 CloseDatabase();
1745
10/10
✓ Branch 1 taken 2787 times.
✓ Branch 2 taken 117 times.
✓ Branch 4 taken 2787 times.
✓ Branch 5 taken 117 times.
✓ Branch 7 taken 2787 times.
✓ Branch 8 taken 117 times.
✓ Branch 10 taken 2787 times.
✓ Branch 11 taken 117 times.
✓ Branch 13 taken 2787 times.
✓ Branch 14 taken 117 times.
12552 }
1746
1747
1748 186927 void PosixQuotaManager::ProcessCommandBunch(const unsigned num,
1749 const LruCommand *commands,
1750 const char *descriptions) {
1751 186927 int retval = sqlite3_exec(database_, "BEGIN", NULL, NULL, NULL);
1752
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 186927 times.
186927 assert(retval == SQLITE_OK);
1753
1754
2/2
✓ Branch 0 taken 5851911 times.
✓ Branch 1 taken 186927 times.
6038838 for (unsigned i = 0; i < num; ++i) {
1755
1/2
✓ Branch 1 taken 5851911 times.
✗ Branch 2 not taken.
5851911 const shash::Any hash = commands[i].RetrieveHash();
1756
1/2
✓ Branch 1 taken 5851911 times.
✗ Branch 2 not taken.
5851911 const string hash_str = hash.ToString();
1757 5851911 const unsigned size = commands[i].GetSize();
1758
1/2
✓ Branch 1 taken 5851911 times.
✗ Branch 2 not taken.
5851911 LogCvmfs(kLogQuota, kLogDebug, "processing %s (%d)", hash_str.c_str(),
1759 5851911 commands[i].command_type);
1760
1761 bool exists;
1762
3/4
✓ Branch 0 taken 1950507 times.
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 3901326 times.
✗ Branch 3 not taken.
5851911 switch (commands[i].command_type) {
1763 1950507 case kTouch:
1764
1/2
✓ Branch 1 taken 1950507 times.
✗ Branch 2 not taken.
1950507 sqlite3_bind_int64(stmt_touch_, 1, seq_++);
1765
1/2
✓ Branch 3 taken 1950507 times.
✗ Branch 4 not taken.
1950507 sqlite3_bind_text(stmt_touch_, 2, &hash_str[0], hash_str.length(),
1766 SQLITE_STATIC);
1767
1/2
✓ Branch 1 taken 1950507 times.
✗ Branch 2 not taken.
1950507 retval = sqlite3_step(stmt_touch_);
1768
1/2
✓ Branch 1 taken 1950507 times.
✗ Branch 2 not taken.
1950507 LogCvmfs(kLogQuota, kLogDebug, "touching %s (%ld): %d",
1769 1950507 hash_str.c_str(), seq_ - 1, retval);
1770
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1950507 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1950507 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 1950507 times.
✗ Branch 2 not taken.
1950507 sqlite3_reset(stmt_touch_);
1775 1950507 break;
1776 78 case kUnpin:
1777
1/2
✓ Branch 3 taken 78 times.
✗ Branch 4 not taken.
78 sqlite3_bind_text(stmt_unpin_, 1, &hash_str[0], hash_str.length(),
1778 SQLITE_STATIC);
1779
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 retval = sqlite3_step(stmt_unpin_);
1780
1/2
✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
78 LogCvmfs(kLogQuota, kLogDebug, "unpinning %s: %d", hash_str.c_str(),
1781 retval);
1782
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
78 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 78 times.
✗ Branch 2 not taken.
78 sqlite3_reset(stmt_unpin_);
1787 78 break;
1788 3901326 case kPin:
1789 case kPinRegular:
1790 case kInsert:
1791 case kInsertVolatile:
1792 // It could already be in, check
1793
1/2
✓ Branch 1 taken 3901326 times.
✗ Branch 2 not taken.
3901326 exists = Contains(hash_str);
1794
1795 // Cleanup, move to trash and unlink
1796
3/4
✓ Branch 0 taken 3901131 times.
✓ Branch 1 taken 195 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3901131 times.
3901326 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 3901326 times.
✗ Branch 4 not taken.
3901326 sqlite3_bind_text(stmt_new_, 1, &hash_str[0], hash_str.length(),
1805 SQLITE_STATIC);
1806
1/2
✓ Branch 1 taken 3901326 times.
✗ Branch 2 not taken.
3901326 sqlite3_bind_int64(stmt_new_, 2, size);
1807
2/2
✓ Branch 0 taken 156 times.
✓ Branch 1 taken 3901170 times.
3901326 if (commands[i].command_type == kInsertVolatile) {
1808
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 sqlite3_bind_int64(stmt_new_, 3, (seq_++) | kVolatileFlag);
1809 } else {
1810
1/2
✓ Branch 1 taken 3901170 times.
✗ Branch 2 not taken.
3901170 sqlite3_bind_int64(stmt_new_, 3, seq_++);
1811 }
1812 3901326 sqlite3_bind_text(stmt_new_, 4, &descriptions[i * kMaxDescription],
1813
1/2
✓ Branch 1 taken 3901326 times.
✗ Branch 2 not taken.
3901326 commands[i].desc_length, SQLITE_STATIC);
1814
1/2
✓ Branch 1 taken 3901326 times.
✗ Branch 2 not taken.
3901326 sqlite3_bind_int64(
1815 stmt_new_, 5,
1816
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 3901248 times.
3901326 (commands[i].command_type == kPin) ? kFileCatalog : kFileRegular);
1817
1/2
✓ Branch 1 taken 3901326 times.
✗ Branch 2 not taken.
3901326 sqlite3_bind_int64(stmt_new_, 6,
1818
2/2
✓ Branch 0 taken 3901248 times.
✓ Branch 1 taken 78 times.
3901326 ((commands[i].command_type == kPin)
1819
2/2
✓ Branch 0 taken 429 times.
✓ Branch 1 taken 3900819 times.
3901248 || (commands[i].command_type == kPinRegular))
1820 ? 1
1821 : 0);
1822
1/2
✓ Branch 1 taken 3901326 times.
✗ Branch 2 not taken.
3901326 retval = sqlite3_step(stmt_new_);
1823
1/2
✓ Branch 1 taken 3901326 times.
✗ Branch 2 not taken.
3901326 LogCvmfs(kLogQuota, kLogDebug, "insert or replace %s, method %d: %d",
1824 3901326 hash_str.c_str(), commands[i].command_type, retval);
1825
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3901326 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3901326 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 3901326 times.
✗ Branch 2 not taken.
3901326 sqlite3_reset(stmt_new_);
1830
1831
2/2
✓ Branch 0 taken 3901131 times.
✓ Branch 1 taken 195 times.
3901326 if (!exists)
1832 3901131 gauge_ += size;
1833 3901326 break;
1834 default:
1835 // other types should have been taken care of by event loop
1836 PANIC(NULL);
1837 }
1838 5851911 }
1839
1840 186927 retval = sqlite3_exec(database_, "COMMIT", NULL, NULL, NULL);
1841
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 186927 times.
186927 if (retval != SQLITE_OK) {
1842 PANIC(kLogSyslogErr, "failed to commit to cachedb, error %d", retval);
1843 }
1844 186927 }
1845
1846
1847 2827 bool PosixQuotaManager::RebuildDatabase() {
1848 2827 bool result = false;
1849 2827 string sql;
1850 2827 sqlite3_stmt *stmt_select = NULL;
1851 2827 sqlite3_stmt *stmt_insert = NULL;
1852 int sqlerr;
1853 2827 int seq = 0;
1854 char hex[4];
1855 struct stat info;
1856 platform_dirent64 *d;
1857 2827 DIR *dirp = NULL;
1858 2827 string path;
1859
1860
1/2
✓ Branch 1 taken 2827 times.
✗ Branch 2 not taken.
2827 LogCvmfs(kLogQuota, kLogSyslog | kLogDebug, "re-building cache database");
1861
1862 // Empty cache catalog and fscache
1863
1/2
✓ Branch 1 taken 2827 times.
✗ Branch 2 not taken.
2827 sql = "DELETE FROM cache_catalog; DELETE FROM fscache;";
1864
1/2
✓ Branch 2 taken 2827 times.
✗ Branch 3 not taken.
2827 sqlerr = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
1865
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2827 times.
2827 if (sqlerr != SQLITE_OK) {
1866 LogCvmfs(kLogQuota, kLogDebug, "could not clear cache database");
1867 goto build_return;
1868 }
1869
1870 2827 gauge_ = 0;
1871
1872 // Insert files from cache sub-directories 00 - ff
1873 // TODO(jblomer): fs_traversal
1874
1/2
✓ Branch 1 taken 2827 times.
✗ Branch 2 not taken.
2827 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 693877 times.
✓ Branch 1 taken 2710 times.
696587 for (int i = 0; i <= 0xff; i++) {
1880 693877 snprintf(hex, sizeof(hex), "%02x", i);
1881
3/6
✓ Branch 2 taken 693877 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 693877 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 693877 times.
✗ Branch 9 not taken.
693877 path = cache_dir_ + "/" + string(hex);
1882
3/4
✓ Branch 2 taken 693877 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 117 times.
✓ Branch 5 taken 693760 times.
693877 if ((dirp = opendir(path.c_str())) == NULL) {
1883
1/2
✓ Branch 2 taken 117 times.
✗ Branch 3 not taken.
117 LogCvmfs(kLogQuota, kLogDebug | kLogSyslogErr,
1884 "failed to open directory %s (tmpwatch interfering?)",
1885 path.c_str());
1886 117 goto build_return;
1887 }
1888
3/4
✓ Branch 1 taken 2081358 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1387598 times.
✓ Branch 4 taken 693760 times.
2081358 while ((d = platform_readdir(dirp)) != NULL) {
1889
3/6
✓ Branch 2 taken 1387598 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1387598 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1387598 times.
✗ Branch 9 not taken.
2775196 const string file_path = path + "/" + string(d->d_name);
1890
1/2
✓ Branch 2 taken 1387598 times.
✗ Branch 3 not taken.
1387598 if (stat(file_path.c_str(), &info) == 0) {
1891
2/2
✓ Branch 0 taken 1387520 times.
✓ Branch 1 taken 78 times.
1387598 if (!S_ISREG(info.st_mode))
1892 1387559 continue;
1893
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 39 times.
78 if (info.st_size == 0) {
1894
1/2
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 LogCvmfs(kLogQuota, kLogSyslog | kLogDebug,
1895 "removing empty file %s during automatic cache db rebuild",
1896 file_path.c_str());
1897 39 unlink(file_path.c_str());
1898 39 continue;
1899 }
1900
1901
3/6
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 39 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 39 times.
✗ Branch 10 not taken.
78 string hash = string(hex) + string(d->d_name);
1902
1/2
✓ Branch 3 taken 39 times.
✗ Branch 4 not taken.
39 sqlite3_bind_text(stmt_insert, 1, hash.data(), hash.length(),
1903 SQLITE_STATIC);
1904
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 sqlite3_bind_int64(stmt_insert, 2, info.st_size);
1905
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 sqlite3_bind_int64(stmt_insert, 3, info.st_atime);
1906
2/4
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 39 times.
39 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 39 times.
✗ Branch 2 not taken.
39 sqlite3_reset(stmt_insert);
1911
1912 39 gauge_ += info.st_size;
1913
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 } else {
1914 LogCvmfs(kLogQuota, kLogDebug, "could not stat %s", file_path.c_str());
1915 }
1916
2/3
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 1387559 times.
✗ Branch 3 not taken.
1387598 }
1917
1/2
✓ Branch 1 taken 693760 times.
✗ Branch 2 not taken.
693760 closedir(dirp);
1918 693760 dirp = NULL;
1919 }
1920
1/2
✓ Branch 1 taken 2710 times.
✗ Branch 2 not taken.
2710 sqlite3_finalize(stmt_insert);
1921 2710 stmt_insert = NULL;
1922
1923 // Transfer from temp table in cache catalog
1924
1/2
✓ Branch 1 taken 2710 times.
✗ Branch 2 not taken.
2710 sqlite3_prepare_v2(database_,
1925 "SELECT sha1, size FROM fscache ORDER BY actime;", -1,
1926 &stmt_select, NULL);
1927
1/2
✓ Branch 1 taken 2710 times.
✗ Branch 2 not taken.
2710 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 2749 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 39 times.
✓ Branch 4 taken 2710 times.
2749 while (sqlite3_step(stmt_select) == SQLITE_ROW) {
1933 const string hash = string(
1934
2/4
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 39 times.
✗ Branch 6 not taken.
39 reinterpret_cast<const char *>(sqlite3_column_text(stmt_select, 0)));
1935
1/2
✓ Branch 3 taken 39 times.
✗ Branch 4 not taken.
39 sqlite3_bind_text(stmt_insert, 1, &hash[0], hash.length(), SQLITE_STATIC);
1936
2/4
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
39 sqlite3_bind_int64(stmt_insert, 2, sqlite3_column_int64(stmt_select, 1));
1937
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 sqlite3_bind_int64(stmt_insert, 3, seq++);
1938 // Might also be a catalog (information is lost)
1939
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 sqlite3_bind_int64(stmt_insert, 4, kFileRegular);
1940
1941
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 const int retval = sqlite3_step(stmt_insert);
1942
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 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 39 times.
✗ Branch 2 not taken.
39 sqlite3_reset(stmt_insert);
1950
1/2
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
39 }
1951
1952 // Delete temporary table
1953
1/2
✓ Branch 1 taken 2710 times.
✗ Branch 2 not taken.
2710 sql = "DELETE FROM fscache;";
1954
1/2
✓ Branch 2 taken 2710 times.
✗ Branch 3 not taken.
2710 sqlerr = sqlite3_exec(database_, sql.c_str(), NULL, NULL, NULL);
1955
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2710 times.
2710 if (sqlerr != SQLITE_OK) {
1956 LogCvmfs(kLogQuota, kLogDebug, "could not clear temporary table (%d)",
1957 sqlerr);
1958 goto build_return;
1959 }
1960
1961 2710 seq_ = seq;
1962 2710 result = true;
1963
1/2
✓ Branch 1 taken 2710 times.
✗ Branch 2 not taken.
2710 LogCvmfs(kLogQuota, kLogDebug,
1964 "rebuilding finished, sequence %" PRIu64 ", gauge %" PRIu64, seq_,
1965 gauge_);
1966
1967 2827 build_return:
1968
1/2
✓ Branch 0 taken 2827 times.
✗ Branch 1 not taken.
2827 if (stmt_insert)
1969
1/2
✓ Branch 1 taken 2827 times.
✗ Branch 2 not taken.
2827 sqlite3_finalize(stmt_insert);
1970
2/2
✓ Branch 0 taken 2710 times.
✓ Branch 1 taken 117 times.
2827 if (stmt_select)
1971
1/2
✓ Branch 1 taken 2710 times.
✗ Branch 2 not taken.
2710 sqlite3_finalize(stmt_select);
1972
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2827 times.
2827 if (dirp)
1973 closedir(dirp);
1974 2827 return result;
1975 2827 }
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 156 void PosixQuotaManager::RegisterBackChannel(int back_channel[2],
1983 const string &channel_id) {
1984
1/2
✓ Branch 0 taken 156 times.
✗ Branch 1 not taken.
156 if (protocol_revision_ >= 1) {
1985
1/2
✓ Branch 2 taken 156 times.
✗ Branch 3 not taken.
156 shash::Md5 hash = shash::Md5(shash::AsciiPtr(channel_id));
1986
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 MakeReturnPipe(back_channel);
1987
1988 156 LruCommand cmd;
1989 156 cmd.command_type = kRegisterBackChannel;
1990 156 cmd.return_pipe = back_channel[1];
1991 // Not StoreHash(). This is an MD5 hash.
1992 156 memcpy(cmd.digest, hash.digest, hash.GetDigestSize());
1993
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
1994
1995 char success;
1996
1/2
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
156 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 156 times.
156 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 156 }
2007
2008
2009 /**
2010 * Removes a chunk from cache, if it exists.
2011 */
2012 117 void PosixQuotaManager::Remove(const shash::Any &hash) {
2013
1/2
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
117 const string hash_str = hash.ToString();
2014
2015 int pipe_remove[2];
2016
1/2
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
117 MakeReturnPipe(pipe_remove);
2017
2018 117 LruCommand cmd;
2019 117 cmd.command_type = kRemove;
2020 117 cmd.return_pipe = pipe_remove[1];
2021
1/2
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
117 cmd.StoreHash(hash);
2022
1/2
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
117 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2023
2024 bool success;
2025
1/2
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
117 ManagedReadHalfPipe(pipe_remove[0], &success, sizeof(success));
2026
1/2
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
117 CloseReturnPipe(pipe_remove);
2027
2028
3/6
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 117 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 117 times.
✗ Branch 8 not taken.
117 unlink((cache_dir_ + "/" + hash.MakePathWithoutSuffix()).c_str());
2029 117 }
2030
2031
2032 1287 void PosixQuotaManager::Spawn() {
2033
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 1209 times.
1287 if (spawned_)
2034 78 return;
2035
2036 1209 if (pthread_create(&thread_lru_, NULL, MainCommandServer,
2037 static_cast<void *>(this))
2038
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1209 times.
1209 != 0) {
2039 PANIC(kLogDebug, "could not create lru thread");
2040 }
2041
2042 1209 spawned_ = true;
2043 }
2044
2045
2046 /**
2047 * Updates the sequence number of the file specified by the hash.
2048 */
2049 1950311 void PosixQuotaManager::Touch(const shash::Any &hash) {
2050 1950311 LruCommand cmd;
2051 1950311 cmd.command_type = kTouch;
2052
1/2
✓ Branch 1 taken 1950311 times.
✗ Branch 2 not taken.
1950311 cmd.StoreHash(hash);
2053
1/2
✓ Branch 1 taken 1950311 times.
✗ Branch 2 not taken.
1950311 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2054 1950311 }
2055
2056
2057 3354 void PosixQuotaManager::UnbindReturnPipe(int pipe_wronly) {
2058
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 3315 times.
3354 if (shared_)
2059 39 close(pipe_wronly);
2060 3354 }
2061
2062
2063 273 void PosixQuotaManager::UnlinkReturnPipe(int pipe_wronly) {
2064
2/2
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 156 times.
273 if (shared_)
2065
2/4
✓ Branch 2 taken 117 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 117 times.
✗ Branch 6 not taken.
117 unlink((workspace_dir_ + "/pipe" + StringifyInt(pipe_wronly)).c_str());
2066 273 }
2067
2068
2069 375 void PosixQuotaManager::Unpin(const shash::Any &hash) {
2070
2/4
✓ Branch 1 taken 375 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 375 times.
✗ Branch 6 not taken.
375 LogCvmfs(kLogQuota, kLogDebug, "Unpin %s", hash.ToString().c_str());
2071
2072 375 LruCommand cmd;
2073 375 cmd.command_type = kUnpin;
2074
1/2
✓ Branch 1 taken 375 times.
✗ Branch 2 not taken.
375 cmd.StoreHash(hash);
2075
1/2
✓ Branch 1 taken 375 times.
✗ Branch 2 not taken.
375 WritePipe(pipe_lru_[1], &cmd, sizeof(cmd));
2076 375 }
2077
2078
2079 78 void PosixQuotaManager::UnregisterBackChannel(int back_channel[2],
2080 const string &channel_id) {
2081
1/2
✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
78 if (protocol_revision_ >= 1) {
2082
1/2
✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
78 shash::Md5 hash = shash::Md5(shash::AsciiPtr(channel_id));
2083
2084 78 LruCommand cmd;
2085 78 cmd.command_type = kUnregisterBackChannel;
2086 // Not StoreHash(). This is an MD5 hash.
2087 78 memcpy(cmd.digest, hash.digest, hash.GetDigestSize());
2088
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 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 78 times.
✗ Branch 2 not taken.
78 close(back_channel[0]);
2092 } else {
2093 ClosePipe(back_channel);
2094 }
2095 78 }
2096
2097 3904953 void PosixQuotaManager::ManagedReadHalfPipe(int fd, void *buf, size_t nbyte) {
2098
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3904953 times.
3904953 const unsigned timeout_ms = cachemgr_pid_ ? 1000 : 0;
2099 3904953 bool result = false;
2100 do {
2101 3904953 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 3904953 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 3904953 times.
3904953 } while (!result && getpgid(cachemgr_pid_) >= 0);
2104
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3904953 times.
3904953 if (!result) {
2105 PANIC(kLogStderr,
2106 "Error: quota manager could not read from cachemanager pipe");
2107 }
2108 3904953 }
2109