GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/talk.cc
Date: 2024-04-21 02:33:16
Exec Total Coverage
Lines: 0 553 0.0%
Branches: 0 1437 0.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * Implements a socket interface to cvmfs. This way commands can be send
5 * to cvmfs. When cvmfs is running, the socket
6 * /var/cache/cvmfs2/$INSTANCE/cvmfs_io
7 * is available for command input and reply messages, resp.
8 *
9 * Cvmfs comes with the cvmfs_talk script, that handles writing and reading the
10 * socket.
11 *
12 * The talk module runs in a separate thread.
13 */
14
15 #ifndef __STDC_FORMAT_MACROS
16 #define __STDC_FORMAT_MACROS
17 #endif
18
19 #include "cvmfs_config.h"
20 #include "talk.h"
21
22 #include <errno.h>
23 #include <inttypes.h>
24 #include <pthread.h>
25 #include <stdint.h>
26 #include <sys/socket.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <sys/uio.h>
30 #include <sys/un.h>
31 #include <unistd.h>
32
33 #include <cassert>
34 #include <cstdlib>
35 #include <cstring>
36 #include <string>
37 #include <vector>
38
39 #include "cache.h"
40 #include "cache_posix.h"
41 #include "catalog_mgr_client.h"
42 #include "cvmfs.h"
43 #include "duplex_sqlite3.h"
44 #include "fuse_remount.h"
45 #include "glue_buffer.h"
46 #include "loader.h"
47 #include "monitor.h"
48 #include "mountpoint.h"
49 #include "network/download.h"
50 #include "nfs_maps.h"
51 #include "options.h"
52 #include "quota.h"
53 #include "shortstring.h"
54 #include "statistics.h"
55 #include "tracer.h"
56 #include "util/logging.h"
57 #include "util/platform.h"
58 #include "util/pointer.h"
59 #include "wpad.h"
60
61 using namespace std; // NOLINT
62
63
64 void TalkManager::Answer(int con_fd, const string &msg) {
65 (void)send(con_fd, &msg[0], msg.length(), MSG_NOSIGNAL);
66 }
67
68
69 void TalkManager::AnswerStringList(int con_fd, const vector<string> &list) {
70 string list_str;
71 for (unsigned i = 0; i < list.size(); ++i) {
72 list_str += list[i] + "\n";
73 }
74 Answer(con_fd, list_str);
75 }
76
77
78 TalkManager *TalkManager::Create(
79 const string &socket_path,
80 MountPoint *mount_point,
81 FuseRemounter *remounter)
82 {
83 UniquePtr<TalkManager>
84 talk_manager(new TalkManager(socket_path, mount_point, remounter));
85
86 talk_manager->socket_fd_ = MakeSocket(socket_path, 0660);
87 if (talk_manager->socket_fd_ == -1)
88 return NULL;
89 if (listen(talk_manager->socket_fd_, 1) == -1)
90 return NULL;
91
92 LogCvmfs(kLogTalk, kLogDebug, "socket created at %s (fd %d)",
93 socket_path.c_str(), talk_manager->socket_fd_);
94
95 return talk_manager.Release();
96 }
97
98
99 string TalkManager::FormatHostInfo(download::DownloadManager *download_mgr) {
100 vector<string> host_chain;
101 vector<int> rtt;
102 unsigned active_host;
103
104 download_mgr->GetHostInfo(&host_chain, &rtt, &active_host);
105 if (host_chain.size() == 0)
106 return "No hosts defined\n";
107
108 string host_str;
109 for (unsigned i = 0; i < host_chain.size(); ++i) {
110 host_str += " [" + StringifyInt(i) + "] " + host_chain[i] + " (";
111 if (rtt[i] == download::DownloadManager::kProbeUnprobed)
112 host_str += "unprobed";
113 else if (rtt[i] == download::DownloadManager::kProbeDown)
114 host_str += "host down";
115 else if (rtt[i] == download::DownloadManager::kProbeGeo)
116 host_str += "geographically ordered";
117 else
118 host_str += StringifyInt(rtt[i]) + " ms";
119 host_str += ")\n";
120 }
121 host_str += "Active host " + StringifyInt(active_host) + ": " +
122 host_chain[active_host] + "\n";
123 return host_str;
124 }
125
126 string TalkManager::FormatProxyInfo(download::DownloadManager *download_mgr) {
127 vector< vector<download::DownloadManager::ProxyInfo> > proxy_chain;
128 unsigned active_group;
129 unsigned fallback_group;
130
131 download_mgr->GetProxyInfo(&proxy_chain, &active_group, &fallback_group);
132 string proxy_str;
133 if (proxy_chain.size()) {
134 proxy_str += "Load-balance groups:\n";
135 for (unsigned i = 0; i < proxy_chain.size(); ++i) {
136 vector<string> urls;
137 for (unsigned j = 0; j < proxy_chain[i].size(); ++j) {
138 urls.push_back(proxy_chain[i][j].Print());
139 }
140 proxy_str +=
141 "[" + StringifyInt(i) + "] " + JoinStrings(urls, ", ") + "\n";
142 }
143 proxy_str += "Active proxy: [" + StringifyInt(active_group) + "] " +
144 proxy_chain[active_group][0].url + "\n";
145 if (fallback_group < proxy_chain.size())
146 proxy_str += "First fallback group: [" +
147 StringifyInt(fallback_group) + "]\n";
148 } else {
149 proxy_str = "No proxies defined\n";
150 }
151 return proxy_str;
152 }
153
154
155 /**
156 * Listener thread on the socket.
157 * TODO(jblomer): create Format... helpers to shorten this method
158 */
159 void *TalkManager::MainResponder(void *data) {
160 TalkManager *talk_mgr = reinterpret_cast<TalkManager *>(data);
161 MountPoint *mount_point = talk_mgr->mount_point_;
162 FileSystem *file_system = mount_point->file_system();
163 FuseRemounter *remounter = talk_mgr->remounter_;
164 LogCvmfs(kLogTalk, kLogDebug, "talk thread started");
165
166 struct sockaddr_un remote;
167 socklen_t socket_size = sizeof(remote);
168 int con_fd = -1;
169 while (true) {
170 if (con_fd >= 0) {
171 shutdown(con_fd, SHUT_RDWR);
172 close(con_fd);
173 }
174 LogCvmfs(kLogTalk, kLogDebug, "accepting connections on socketfd %d",
175 talk_mgr->socket_fd_);
176 if ((con_fd = accept(talk_mgr->socket_fd_,
177 (struct sockaddr *)&remote,
178 &socket_size)) < 0)
179 {
180 LogCvmfs(kLogTalk, kLogDebug, "terminating talk thread (fd %d, errno %d)",
181 con_fd, errno);
182 break;
183 }
184
185 char buf[kMaxCommandSize];
186 int bytes_read;
187 if ((bytes_read = recv(con_fd, buf, sizeof(buf), 0)) <= 0)
188 continue;
189
190 if (buf[bytes_read-1] == '\0')
191 bytes_read--;
192 const string line = string(buf, bytes_read);
193 LogCvmfs(kLogTalk, kLogDebug, "received %s (length %lu)",
194 line.c_str(), line.length());
195
196 if (line == "tracebuffer flush") {
197 mount_point->tracer()->Flush();
198 talk_mgr->Answer(con_fd, "OK\n");
199 } else if (line == "cache size") {
200 QuotaManager *quota_mgr = file_system->cache_mgr()->quota_mgr();
201 if (!quota_mgr->HasCapability(QuotaManager::kCapIntrospectSize)) {
202 talk_mgr->Answer(con_fd, "Cache cannot report its size\n");
203 } else {
204 uint64_t size_unpinned = quota_mgr->GetSize();
205 uint64_t size_pinned = quota_mgr->GetSizePinned();
206 const string size_str = "Current cache size is " +
207 StringifyInt(size_unpinned / (1024*1024)) + "MB (" +
208 StringifyInt(size_unpinned) + " Bytes), pinned: " +
209 StringifyInt(size_pinned / (1024*1024)) + "MB (" +
210 StringifyInt(size_pinned) + " Bytes)\n";
211 talk_mgr->Answer(con_fd, size_str);
212 }
213 } else if (line == "cache instance") {
214 talk_mgr->Answer(con_fd, file_system->cache_mgr()->Describe());
215 } else if (line == "cache list") {
216 QuotaManager *quota_mgr = file_system->cache_mgr()->quota_mgr();
217 if (!quota_mgr->HasCapability(QuotaManager::kCapList)) {
218 talk_mgr->Answer(con_fd, "Cache cannot list its entries\n");
219 } else {
220 vector<string> ls = quota_mgr->List();
221 talk_mgr->AnswerStringList(con_fd, ls);
222 }
223 } else if (line == "cache list pinned") {
224 QuotaManager *quota_mgr = file_system->cache_mgr()->quota_mgr();
225 if (!quota_mgr->HasCapability(QuotaManager::kCapList)) {
226 talk_mgr->Answer(con_fd, "Cache cannot list its entries\n");
227 } else {
228 vector<string> ls_pinned = quota_mgr->ListPinned();
229 talk_mgr->AnswerStringList(con_fd, ls_pinned);
230 }
231 } else if (line == "cache list catalogs") {
232 QuotaManager *quota_mgr = file_system->cache_mgr()->quota_mgr();
233 if (!quota_mgr->HasCapability(QuotaManager::kCapList)) {
234 talk_mgr->Answer(con_fd, "Cache cannot list its entries\n");
235 } else {
236 vector<string> ls_catalogs = quota_mgr->ListCatalogs();
237 talk_mgr->AnswerStringList(con_fd, ls_catalogs);
238 }
239 } else if (line.substr(0, 12) == "cleanup rate") {
240 QuotaManager *quota_mgr = file_system->cache_mgr()->quota_mgr();
241 if (!quota_mgr->HasCapability(QuotaManager::kCapIntrospectCleanupRate)) {
242 talk_mgr->Answer(con_fd, "Unsupported by this cache\n");
243 } else {
244 if (line.length() < 14) {
245 talk_mgr->Answer(con_fd, "Usage: cleanup rate <period in mn>\n");
246 } else {
247 const uint64_t period_s = String2Uint64(line.substr(13)) * 60;
248 const uint64_t rate = quota_mgr->GetCleanupRate(period_s);
249 talk_mgr->Answer(con_fd, StringifyInt(rate) + "\n");
250 }
251 }
252 } else if (line.substr(0, 7) == "cleanup") {
253 QuotaManager *quota_mgr = file_system->cache_mgr()->quota_mgr();
254 if (!quota_mgr->HasCapability(QuotaManager::kCapShrink)) {
255 talk_mgr->Answer(con_fd, "Cache cannot trigger eviction\n");
256 } else {
257 if (line.length() < 9) {
258 talk_mgr->Answer(con_fd, "Usage: cleanup <MB>\n");
259 } else {
260 const uint64_t size = String2Uint64(line.substr(8))*1024*1024;
261 if (quota_mgr->Cleanup(size)) {
262 talk_mgr->Answer(con_fd, "OK\n");
263 } else {
264 talk_mgr->Answer(con_fd, "Not fully cleaned "
265 "(there might be pinned chunks)\n");
266 }
267 }
268 }
269 } else if (line.substr(0, 5) == "evict") {
270 assert(mount_point->file_system()->type() == FileSystem::kFsFuse);
271 if (line.length() < 7) {
272 talk_mgr->Answer(con_fd, "Usage: evict <path>\n");
273 } else {
274 const string path = line.substr(6);
275 const bool found_regular = cvmfs::Evict(path);
276 if (found_regular)
277 talk_mgr->Answer(con_fd, "OK\n");
278 else
279 talk_mgr->Answer(con_fd, "No such regular file\n");
280 }
281 } else if (line.substr(0, 3) == "pin") {
282 assert(mount_point->file_system()->type() == FileSystem::kFsFuse);
283 if (line.length() < 5) {
284 talk_mgr->Answer(con_fd, "Usage: pin <path>\n");
285 } else {
286 const string path = line.substr(4);
287 const bool found_regular = cvmfs::Pin(path);
288 if (found_regular)
289 talk_mgr->Answer(con_fd, "OK\n");
290 else
291 talk_mgr->Answer(con_fd, "No such regular file or pinning failed\n");
292 }
293 } else if (line == "mountpoint") {
294 talk_mgr->Answer(con_fd, cvmfs::loader_exports_->mount_point + "\n");
295 } else if (line == "device id") {
296 if (cvmfs::loader_exports_->version >= 5)
297 talk_mgr->Answer(con_fd, cvmfs::loader_exports_->device_id + "\n");
298 else
299 talk_mgr->Answer(con_fd, "0:0\n");
300 } else if (line.substr(0, 13) == "send mount fd") {
301 // Hidden command intended to be used only by the cvmfs mount helper
302 if (line.length() < 15) {
303 talk_mgr->Answer(con_fd, "EINVAL\n");
304 } else {
305 std::string socket_path = line.substr(14);
306 bool retval = cvmfs::SendFuseFd(socket_path);
307 talk_mgr->Answer(con_fd, retval ? "OK\n" : "Failed\n");
308 LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslog,
309 "Attempt to send fuse connection info to new mount (via %s)%s",
310 socket_path.c_str(), retval ? "" : " -- failed!");
311 }
312 } else if (line.substr(0, 7) == "remount") {
313 FuseRemounter::Status status;
314 if (line == "remount sync")
315 status = remounter->CheckSynchronously();
316 else
317 status = remounter->Check();
318 switch (status) {
319 case FuseRemounter::kStatusFailGeneral:
320 talk_mgr->Answer(con_fd, "Failed\n");
321 break;
322 case FuseRemounter::kStatusFailNoSpace:
323 talk_mgr->Answer(con_fd, "Failed (no space)\n");
324 break;
325 case FuseRemounter::kStatusUp2Date:
326 talk_mgr->Answer(con_fd, "Catalog up to date\n");
327 break;
328 case FuseRemounter::kStatusDraining:
329 talk_mgr->Answer(con_fd, "New revision applied\n");
330 break;
331 case FuseRemounter::kStatusMaintenance:
332 talk_mgr->Answer(con_fd, "In maintenance mode\n");
333 break;
334 default:
335 talk_mgr->Answer(con_fd, "internal error\n");
336 }
337 } else if (line.substr(0, 6) == "chroot") {
338 if (line.length() < 8) {
339 talk_mgr->Answer(con_fd, "Usage: chroot <hash>\n");
340 } else {
341 std::string root_hash = Trim(line.substr(7), true /* trim_newline */);
342 FuseRemounter::Status status = remounter->ChangeRoot(
343 MkFromHexPtr(shash::HexPtr(root_hash), shash::kSuffixCatalog));
344 switch (status) {
345 case FuseRemounter::kStatusUp2Date:
346 talk_mgr->Answer(con_fd, "OK\n");
347 break;
348 default:
349 talk_mgr->Answer(con_fd, "Failed\n");
350 break;
351 }
352 }
353 } else if (line == "detach nested catalogs") {
354 mount_point->catalog_mgr()->DetachNested();
355 talk_mgr->Answer(con_fd, "OK\n");
356 } else if (line == "revision") {
357 string revision = StringifyInt(mount_point->catalog_mgr()->GetRevision());
358 talk_mgr->Answer(con_fd, revision + "\n");
359 } else if (line == "max ttl info") {
360 const unsigned max_ttl = mount_point->GetMaxTtlMn();
361 if (max_ttl == 0) {
362 talk_mgr->Answer(con_fd, "unset\n");
363 } else {
364 const string max_ttl_str = StringifyInt(max_ttl) + " minutes\n";
365 talk_mgr->Answer(con_fd, max_ttl_str);
366 }
367 } else if (line.substr(0, 11) == "max ttl set") {
368 if (line.length() < 13) {
369 talk_mgr->Answer(con_fd, "Usage: max ttl set <minutes>\n");
370 } else {
371 const unsigned max_ttl = String2Uint64(line.substr(12));
372 mount_point->SetMaxTtlMn(max_ttl);
373 talk_mgr->Answer(con_fd, "OK\n");
374 }
375 } else if (line.substr(0, 14) == "nameserver get") {
376 const string dns_server = mount_point->download_mgr()->GetDnsServer();
377 const string reply = !dns_server.empty() ?
378 std::string("DNS server address: ") + dns_server + "\n":
379 std::string("DNS server not set.\n");
380 talk_mgr->Answer(con_fd, reply);
381 } else if (line.substr(0, 14) == "nameserver set") {
382 if (line.length() < 16) {
383 talk_mgr->Answer(con_fd, "Usage: nameserver set <host>\n");
384 } else {
385 const string host = line.substr(15);
386 mount_point->download_mgr()->SetDnsServer(host);
387 talk_mgr->Answer(con_fd, "OK\n");
388 }
389 } else if (line == "external host info") {
390 string external_host_info =
391 talk_mgr->FormatHostInfo(mount_point->external_download_mgr());
392 talk_mgr->Answer(con_fd, external_host_info);
393 } else if (line == "host info") {
394 string host_info = talk_mgr->FormatHostInfo(mount_point->download_mgr());
395 talk_mgr->Answer(con_fd, host_info);
396 } else if (line == "host probe") {
397 mount_point->download_mgr()->ProbeHosts();
398 talk_mgr->Answer(con_fd, "OK\n");
399 } else if (line == "host probe geo") {
400 bool retval = mount_point->download_mgr()->ProbeGeo();
401 if (retval)
402 talk_mgr->Answer(con_fd, "OK\n");
403 else
404 talk_mgr->Answer(con_fd, "Failed\n");
405 } else if (line == "external host switch") {
406 mount_point->external_download_mgr()->SwitchHost();
407 talk_mgr->Answer(con_fd, "OK\n");
408 } else if (line == "host switch") {
409 mount_point->download_mgr()->SwitchHost();
410 talk_mgr->Answer(con_fd, "OK\n");
411 } else if (line.substr(0, 17) == "external host set") {
412 if (line.length() < 19) {
413 talk_mgr->Answer(con_fd, "Usage: external host set <URL>\n");
414 } else {
415 const std::string host = line.substr(18);
416 mount_point->external_download_mgr()->SetHostChain(host);
417 talk_mgr->Answer(con_fd, "OK\n");
418 }
419 } else if (line.substr(0, 8) == "host set") {
420 if (line.length() < 10) {
421 talk_mgr->Answer(con_fd, "Usage: host set <host list>\n");
422 } else {
423 const string hosts = line.substr(9);
424 mount_point->download_mgr()->SetHostChain(hosts);
425 talk_mgr->Answer(con_fd, "OK\n");
426 }
427 } else if (line == "external proxy info") {
428 string external_proxy_info =
429 talk_mgr->FormatProxyInfo(mount_point->external_download_mgr());
430 talk_mgr->Answer(con_fd, external_proxy_info);
431 } else if (line == "proxy info") {
432 string proxy_info =
433 talk_mgr->FormatProxyInfo(mount_point->download_mgr());
434 talk_mgr->Answer(con_fd, proxy_info);
435 } else if (line == "proxy rebalance") {
436 mount_point->download_mgr()->RebalanceProxies();
437 talk_mgr->Answer(con_fd, "OK\n");
438 } else if (line == "proxy group switch") {
439 mount_point->download_mgr()->SwitchProxyGroup();
440 talk_mgr->Answer(con_fd, "OK\n");
441 } else if (line.substr(0, 18) == "external proxy set") {
442 if (line.length() < 20) {
443 talk_mgr->Answer(con_fd, "Usage: external proxy set <proxy list>\n");
444 } else {
445 string external_proxies = line.substr(19);
446 mount_point->external_download_mgr()->SetProxyChain(
447 external_proxies, "", download::DownloadManager::kSetProxyRegular);
448 talk_mgr->Answer(con_fd, "OK\n");
449 }
450 } else if (line.substr(0, 9) == "proxy set") {
451 if (line.length() < 11) {
452 talk_mgr->Answer(con_fd, "Usage: proxy set <proxy list>\n");
453 } else {
454 string proxies = line.substr(10);
455 proxies =
456 download::ResolveProxyDescription(proxies, "",
457 mount_point->download_mgr());
458 if (proxies == "") {
459 talk_mgr->Answer(con_fd, "Failed, no valid proxies\n");
460 } else {
461 mount_point->download_mgr()->SetProxyChain(
462 proxies, "", download::DownloadManager::kSetProxyRegular);
463 talk_mgr->Answer(con_fd, "OK\n");
464 }
465 }
466 } else if (line.substr(0, 14) == "proxy fallback") {
467 if (line.length() < 15) {
468 talk_mgr->Answer(con_fd, "Usage: proxy fallback <proxy list>\n");
469 } else {
470 string fallback_proxies = line.substr(15);
471 mount_point->download_mgr()->SetProxyChain(
472 "", fallback_proxies, download::DownloadManager::kSetProxyFallback);
473 talk_mgr->Answer(con_fd, "OK\n");
474 }
475 } else if (line == "timeout info") {
476 unsigned timeout;
477 unsigned timeout_direct;
478 mount_point->download_mgr()->GetTimeout(&timeout, &timeout_direct);
479 string timeout_str = "Timeout with proxy: ";
480 if (timeout)
481 timeout_str += StringifyInt(timeout) + "s\n";
482 else
483 timeout_str += "no timeout\n";
484 timeout_str += "Timeout without proxy: ";
485 if (timeout_direct)
486 timeout_str += StringifyInt(timeout_direct) + "s\n";
487 else
488 timeout_str += "no timeout\n";
489 talk_mgr->Answer(con_fd, timeout_str);
490 } else if (line.substr(0, 11) == "timeout set") {
491 if (line.length() < 13) {
492 talk_mgr->Answer(con_fd, "Usage: timeout set <proxy> <direct>\n");
493 } else {
494 uint64_t timeout;
495 uint64_t timeout_direct;
496 String2Uint64Pair(line.substr(12), &timeout, &timeout_direct);
497 mount_point->download_mgr()->SetTimeout(timeout, timeout_direct);
498 talk_mgr->Answer(con_fd, "OK\n");
499 }
500 } else if (line == "open catalogs") {
501 talk_mgr->Answer(con_fd, mount_point->catalog_mgr()->PrintHierarchy());
502 } else if (line == "internal affairs") {
503 int current;
504 int highwater;
505 string result;
506
507 result += "Inode Generation:\n " + cvmfs::PrintInodeGeneration();
508
509 // Manually setting the values of the ShortString counters
510 mount_point->statistics()->Lookup("pathstring.n_instances")->
511 Set(PathString::num_instances());
512 mount_point->statistics()->Lookup("pathstring.n_overflows")->
513 Set(PathString::num_overflows());
514 mount_point->statistics()->Lookup("namestring.n_instances")->
515 Set(NameString::num_instances());
516 mount_point->statistics()->Lookup("namestring.n_overflows")->
517 Set(NameString::num_overflows());
518 mount_point->statistics()->Lookup("linkstring.n_instances")->
519 Set(LinkString::num_instances());
520 mount_point->statistics()->Lookup("linkstring.n_overflows")->
521 Set(LinkString::num_overflows());
522
523 // Manually setting the inode tracker numbers
524 glue::InodeTracker::Statistics inode_stats =
525 mount_point->inode_tracker()->GetStatistics();
526 glue::DentryTracker::Statistics dentry_stats =
527 mount_point->dentry_tracker()->GetStatistics();
528 glue::PageCacheTracker::Statistics page_cache_stats =
529 mount_point->page_cache_tracker()->GetStatistics();
530 mount_point->statistics()->Lookup("inode_tracker.n_insert")->Set(
531 atomic_read64(&inode_stats.num_inserts));
532 mount_point->statistics()->Lookup("inode_tracker.n_remove")->Set(
533 atomic_read64(&inode_stats.num_removes));
534 mount_point->statistics()->Lookup("inode_tracker.no_reference")->Set(
535 atomic_read64(&inode_stats.num_references));
536 mount_point->statistics()->Lookup("inode_tracker.n_hit_inode")->Set(
537 atomic_read64(&inode_stats.num_hits_inode));
538 mount_point->statistics()->Lookup("inode_tracker.n_hit_path")->Set(
539 atomic_read64(&inode_stats.num_hits_path));
540 mount_point->statistics()->Lookup("inode_tracker.n_miss_path")->Set(
541 atomic_read64(&inode_stats.num_misses_path));
542 mount_point->statistics()->Lookup("dentry_tracker.n_insert")->Set(
543 dentry_stats.num_insert);
544 mount_point->statistics()->Lookup("dentry_tracker.n_remove")->Set(
545 dentry_stats.num_remove);
546 mount_point->statistics()->Lookup("dentry_tracker.n_prune")->Set(
547 dentry_stats.num_prune);
548 mount_point->statistics()->Lookup("page_cache_tracker.n_insert")->Set(
549 page_cache_stats.n_insert);
550 mount_point->statistics()->Lookup("page_cache_tracker.n_remove")->Set(
551 page_cache_stats.n_remove);
552 mount_point->statistics()->Lookup("page_cache_tracker.n_open_direct")->
553 Set(page_cache_stats.n_open_direct);
554 mount_point->statistics()->Lookup("page_cache_tracker.n_open_flush")->
555 Set(page_cache_stats.n_open_flush);
556 mount_point->statistics()->Lookup("page_cache_tracker.n_open_cached")->
557 Set(page_cache_stats.n_open_cached);
558
559 if (file_system->cache_mgr()->id() == kPosixCacheManager) {
560 PosixCacheManager *cache_mgr =
561 reinterpret_cast<PosixCacheManager *>(
562 file_system->cache_mgr());
563 result += "\nCache Mode: ";
564 switch (cache_mgr->cache_mode()) {
565 case PosixCacheManager::kCacheReadWrite:
566 result += "read-write";
567 break;
568 case PosixCacheManager::kCacheReadOnly:
569 result += "read-only";
570 break;
571 default:
572 result += "unknown";
573 }
574 }
575 bool drainout_mode;
576 bool maintenance_mode;
577 cvmfs::GetReloadStatus(&drainout_mode, &maintenance_mode);
578 result += "\nDrainout Mode: " + StringifyBool(drainout_mode) + "\n";
579 result += "Maintenance Mode: " + StringifyBool(maintenance_mode) + "\n";
580
581 if (file_system->IsNfsSource()) {
582 result += "\nNFS Map Statistics:\n";
583 result += file_system->nfs_maps()->GetStatistics();
584 }
585
586 result += "SQlite Statistics:\n";
587 sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &current, &highwater, 0);
588 result += " Number of allocations " + StringifyInt(current) + "\n";
589
590 sqlite3_status(SQLITE_STATUS_MEMORY_USED, &current, &highwater, 0);
591 result += " General purpose allocator " +StringifyInt(current/1024) +
592 " KB / " + StringifyInt(highwater/1024) + " KB\n";
593
594 sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &current, &highwater, 0);
595 result += " Largest malloc " + StringifyInt(highwater) + " Bytes\n";
596
597 sqlite3_status(SQLITE_STATUS_PAGECACHE_USED, &current, &highwater, 0);
598 result += " Page cache allocations " + StringifyInt(current) + " / " +
599 StringifyInt(highwater) + "\n";
600
601 sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW,
602 &current, &highwater, 0);
603 result += " Page cache overflows " + StringifyInt(current/1024) +
604 " KB / " + StringifyInt(highwater/1024) + " KB\n";
605
606 sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &current, &highwater, 0);
607 result += " Largest page cache allocation " + StringifyInt(highwater) +
608 " Bytes\n";
609
610 sqlite3_status(SQLITE_STATUS_SCRATCH_USED, &current, &highwater, 0);
611 result += " Scratch allocations " + StringifyInt(current) + " / " +
612 StringifyInt(highwater) + "\n";
613
614 sqlite3_status(SQLITE_STATUS_SCRATCH_OVERFLOW, &current, &highwater, 0);
615 result += " Scratch overflows " + StringifyInt(current) + " / " +
616 StringifyInt(highwater) + "\n";
617
618 sqlite3_status(SQLITE_STATUS_SCRATCH_SIZE, &current, &highwater, 0);
619 result += " Largest scratch allocation " + StringifyInt(highwater/1024)
620 + " KB\n";
621
622 result += "\nPer-Connection Memory Statistics:\n" +
623 mount_point->catalog_mgr()->PrintAllMemStatistics();
624
625 result += "\nLatency distribution of system calls:\n";
626
627 result += "Lookup\n" + file_system->hist_fs_lookup()->ToString();
628 result += "Forget\n" + file_system->hist_fs_forget()->ToString();
629 result += "Multi-Forget\n"
630 + file_system->hist_fs_forget_multi()->ToString();
631 result += "Getattr\n" + file_system->hist_fs_getattr()->ToString();
632 result += "Readlink\n" + file_system->hist_fs_readlink()->ToString();
633 result += "Opendir\n" + file_system->hist_fs_opendir()->ToString();
634 result += "Releasedir\n" + file_system->hist_fs_releasedir()->ToString();
635 result += "Readdir\n" + file_system->hist_fs_readdir()->ToString();
636 result += "Open\n" + file_system->hist_fs_open()->ToString();
637 result += "Read\n" + file_system->hist_fs_read()->ToString();
638 result += "Release\n" + file_system->hist_fs_release()->ToString();
639
640 result += "\nRaw Counters:\n" +
641 mount_point->statistics()->PrintList(perf::Statistics::kPrintHeader);
642
643 talk_mgr->Answer(con_fd, result);
644 } else if (line == "reset error counters") {
645 file_system->ResetErrorCounters();
646 talk_mgr->Answer(con_fd, "OK\n");
647 } else if (line == "pid") {
648 const string pid_str = StringifyInt(cvmfs::pid_) + "\n";
649 talk_mgr->Answer(con_fd, pid_str);
650 } else if (line == "pid cachemgr") {
651 const string pid_str =
652 StringifyInt(file_system->cache_mgr()->quota_mgr()->GetPid()) + "\n";
653 talk_mgr->Answer(con_fd, pid_str);
654 } else if (line == "pid watchdog") {
655 const string pid_str = StringifyInt(Watchdog::GetPid()) + "\n";
656 talk_mgr->Answer(con_fd, pid_str);
657 } else if (line == "parameters") {
658 talk_mgr->Answer(con_fd, file_system->options_mgr()->Dump());
659 } else if (line == "hotpatch history") {
660 string history_str =
661 StringifyTime(cvmfs::loader_exports_->boot_time, true) +
662 " (start of CernVM-FS loader " +
663 cvmfs::loader_exports_->loader_version + ")\n";
664 for (loader::EventList::const_iterator i =
665 cvmfs::loader_exports_->history.begin(),
666 iEnd = cvmfs::loader_exports_->history.end(); i != iEnd; ++i)
667 {
668 history_str += StringifyTime((*i)->timestamp, true) +
669 " (loaded CernVM-FS Fuse Module " +
670 (*i)->so_version + ")\n";
671 }
672 talk_mgr->Answer(con_fd, history_str);
673 } else if (line == "vfs inodes") {
674 string result;
675 glue::InodeTracker::Cursor cursor(
676 mount_point->inode_tracker()->BeginEnumerate());
677 uint64_t inode;
678 while (mount_point->inode_tracker()->NextInode(&cursor, &inode)) {
679 result += StringifyInt(inode) + "\n";
680 }
681 mount_point->inode_tracker()->EndEnumerate(&cursor);
682 talk_mgr->Answer(con_fd, result);
683 } else if (line == "vfs entries") {
684 string result;
685 glue::InodeTracker::Cursor cursor(
686 mount_point->inode_tracker()->BeginEnumerate());
687 uint64_t inode_parent;
688 NameString name;
689 while (mount_point->inode_tracker()->NextEntry(
690 &cursor, &inode_parent, &name))
691 {
692 result += "<" + StringifyInt(inode_parent) + ">/" + name.ToString() +
693 "\n";
694 }
695 mount_point->inode_tracker()->EndEnumerate(&cursor);
696 talk_mgr->Answer(con_fd, result);
697 } else if (line == "version") {
698 string version_str = string(VERSION) + " (CernVM-FS Fuse Module)\n" +
699 cvmfs::loader_exports_->loader_version + " (Loader)\n";
700 talk_mgr->Answer(con_fd, version_str);
701 } else if (line == "version patchlevel") {
702 talk_mgr->Answer(con_fd, string(CVMFS_PATCH_LEVEL) + "\n");
703 } else if (line == "tear down to read-only") {
704 if (file_system->cache_mgr()->id() != kPosixCacheManager) {
705 talk_mgr->Answer(con_fd, "not supported\n");
706 } else {
707 // hack
708 cvmfs::UnregisterQuotaListener();
709 file_system->TearDown2ReadOnly();
710 talk_mgr->Answer(con_fd, "In read-only mode\n");
711 }
712 } else if (line == "latency") {
713 string result = talk_mgr->FormatLatencies(*mount_point, file_system);
714 talk_mgr->Answer(con_fd, result);
715 } else {
716 talk_mgr->Answer(con_fd, "unknown command\n");
717 }
718 }
719
720 return NULL;
721 } // NOLINT(readability/fn_size)
722
723 string TalkManager::FormatLatencies(const MountPoint &mount_point,
724 FileSystem *file_system) {
725 string result;
726 const unsigned int bufSize = 300;
727 char buffer[bufSize];
728
729 vector<float> qs;
730 qs.push_back(.1);
731 qs.push_back(.2);
732 qs.push_back(.25);
733 qs.push_back(.3);
734 qs.push_back(.4);
735 qs.push_back(.5);
736 qs.push_back(.6);
737 qs.push_back(.7);
738 qs.push_back(.75);
739 qs.push_back(.8);
740 qs.push_back(.9);
741 qs.push_back(.95);
742 qs.push_back(.99);
743 qs.push_back(.999);
744 qs.push_back(.9999);
745
746 string repo(mount_point.fqrn());
747
748 unsigned int format_index =
749 snprintf(buffer, bufSize, "\"%s\",\"%s\",\"%s\",\"%s\"", "repository",
750 "action", "total_count", "time_unit");
751 for (unsigned int i = 0; i < qs.size(); i++) {
752 format_index += snprintf(buffer + format_index, bufSize - format_index,
753 ",%0.5f", qs[i]);
754 }
755 format_index += snprintf(buffer + format_index, bufSize - format_index, "\n");
756 assert(format_index < bufSize);
757
758 result += buffer;
759 memset(buffer, 0, sizeof(buffer));
760 format_index = 0;
761
762 vector<Log2Histogram *> hist;
763 vector<string> names;
764 hist.push_back(file_system->hist_fs_lookup());
765 names.push_back("lookup");
766 hist.push_back(file_system->hist_fs_forget());
767 names.push_back("forget");
768 hist.push_back(file_system->hist_fs_forget_multi());
769 names.push_back("forget_multi");
770 hist.push_back(file_system->hist_fs_getattr());
771 names.push_back("getattr");
772 hist.push_back(file_system->hist_fs_readlink());
773 names.push_back("readlink");
774 hist.push_back(file_system->hist_fs_opendir());
775 names.push_back("opendir");
776 hist.push_back(file_system->hist_fs_releasedir());
777 names.push_back("releasedir");
778 hist.push_back(file_system->hist_fs_readdir());
779 names.push_back("readdir");
780 hist.push_back(file_system->hist_fs_open());
781 names.push_back("open");
782 hist.push_back(file_system->hist_fs_read());
783 names.push_back("read");
784 hist.push_back(file_system->hist_fs_release());
785 names.push_back("release");
786
787 for (unsigned int j = 0; j < hist.size(); j++) {
788 Log2Histogram *h = hist[j];
789 unsigned int format_index =
790 snprintf(buffer, bufSize, "\"%s\",\"%s\",%" PRIu64 ",\"nanoseconds\"",
791 repo.c_str(), names[j].c_str(), h->N());
792 for (unsigned int i = 0; i < qs.size(); i++) {
793 format_index += snprintf(buffer + format_index, bufSize - format_index,
794 ",%u", h->GetQuantile(qs[i]));
795 }
796 format_index +=
797 snprintf(buffer + format_index, bufSize - format_index, "\n");
798 assert(format_index < bufSize);
799
800 result += buffer;
801 memset(buffer, 0, sizeof(buffer));
802 format_index = 0;
803 }
804 return result;
805 }
806
807 TalkManager::TalkManager(
808 const string &socket_path,
809 MountPoint *mount_point,
810 FuseRemounter *remounter)
811 : socket_path_(socket_path)
812 , socket_fd_(-1)
813 , mount_point_(mount_point)
814 , remounter_(remounter)
815 , spawned_(false)
816 {
817 memset(&thread_talk_, 0, sizeof(thread_talk_));
818 }
819
820
821 TalkManager::~TalkManager() {
822 if (!socket_path_.empty()) {
823 int retval = unlink(socket_path_.c_str());
824 if ((retval != 0) && (errno != ENOENT)) {
825 LogCvmfs(kLogTalk, kLogSyslogWarn,
826 "Could not remove cvmfs_io socket from cache directory (%d)",
827 errno);
828 }
829 }
830
831 if (socket_fd_ >= 0) {
832 shutdown(socket_fd_, SHUT_RDWR);
833 close(socket_fd_);
834 }
835
836 if (spawned_) {
837 pthread_join(thread_talk_, NULL);
838 LogCvmfs(kLogTalk, kLogDebug, "talk thread stopped");
839 }
840 }
841
842
843 void TalkManager::Spawn() {
844 int retval = pthread_create(&thread_talk_, NULL, MainResponder, this);
845 assert(retval == 0);
846 spawned_ = true;
847 }
848