GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/talk.cc Lines: 0 389 0.0 %
Date: 2019-02-03 02:48:13 Branches: 0 237 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
#include "cvmfs_config.h"
16
#include "talk.h"
17
18
#include <errno.h>
19
#include <pthread.h>
20
#include <sys/socket.h>
21
#include <sys/stat.h>
22
#include <sys/types.h>
23
#include <sys/uio.h>
24
#include <sys/un.h>
25
#include <unistd.h>
26
27
#include <cassert>
28
#include <cstdlib>
29
#include <cstring>
30
#include <string>
31
#include <vector>
32
33
#include "cache.h"
34
#include "cache_posix.h"
35
#include "catalog_mgr_client.h"
36
#include "cvmfs.h"
37
#include "download.h"
38
#include "duplex_sqlite3.h"
39
#include "fuse_remount.h"
40
#include "glue_buffer.h"
41
#include "loader.h"
42
#include "logging.h"
43
#include "monitor.h"
44
#include "mountpoint.h"
45
#include "nfs_maps.h"
46
#include "options.h"
47
#include "platform.h"
48
#include "quota.h"
49
#include "shortstring.h"
50
#include "statistics.h"
51
#include "tracer.h"
52
#include "util/pointer.h"
53
#include "wpad.h"
54
55
using namespace std;  // NOLINT
56
57
58
void TalkManager::Answer(int con_fd, const string &msg) {
59
  (void)send(con_fd, &msg[0], msg.length(), MSG_NOSIGNAL);
60
}
61
62
63
void TalkManager::AnswerStringList(int con_fd, const vector<string> &list) {
64
  string list_str;
65
  for (unsigned i = 0; i < list.size(); ++i) {
66
    list_str += list[i] + "\n";
67
  }
68
  Answer(con_fd, list_str);
69
}
70
71
72
TalkManager *TalkManager::Create(
73
  const string &socket_path,
74
  MountPoint *mount_point,
75
  FuseRemounter *remounter)
76
{
77
  UniquePtr<TalkManager>
78
    talk_manager(new TalkManager(socket_path, mount_point, remounter));
79
80
  talk_manager->socket_fd_ = MakeSocket(socket_path, 0660);
81
  if (talk_manager->socket_fd_ == -1)
82
    return NULL;
83
  if (listen(talk_manager->socket_fd_, 1) == -1)
84
    return NULL;
85
86
  LogCvmfs(kLogTalk, kLogDebug, "socket created at %s (fd %d)",
87
           socket_path.c_str(), talk_manager->socket_fd_);
88
89
  return talk_manager.Release();
90
}
91
92
93
string TalkManager::FormatHostInfo(download::DownloadManager *download_mgr) {
94
  vector<string> host_chain;
95
  vector<int> rtt;
96
  unsigned active_host;
97
98
  download_mgr->GetHostInfo(&host_chain, &rtt, &active_host);
99
  string host_str;
100
  for (unsigned i = 0; i < host_chain.size(); ++i) {
101
    host_str += "  [" + StringifyInt(i) + "] " + host_chain[i] + " (";
102
    if (rtt[i] == download::DownloadManager::kProbeUnprobed)
103
      host_str += "unprobed";
104
    else if (rtt[i] == download::DownloadManager::kProbeDown)
105
      host_str += "host down";
106
    else if (rtt[i] == download::DownloadManager::kProbeGeo)
107
      host_str += "geographically ordered";
108
    else
109
      host_str += StringifyInt(rtt[i]) + " ms";
110
    host_str += ")\n";
111
  }
112
  host_str += "Active host " + StringifyInt(active_host) + ": " +
113
              host_chain[active_host] + "\n";
114
  return host_str;
115
}
116
117
string TalkManager::FormatProxyInfo(download::DownloadManager *download_mgr) {
118
  vector< vector<download::DownloadManager::ProxyInfo> > proxy_chain;
119
  unsigned active_group;
120
  unsigned fallback_group;
121
122
  download_mgr->GetProxyInfo(&proxy_chain, &active_group, &fallback_group);
123
  string proxy_str;
124
  if (proxy_chain.size()) {
125
    proxy_str += "Load-balance groups:\n";
126
    for (unsigned i = 0; i < proxy_chain.size(); ++i) {
127
      vector<string> urls;
128
      for (unsigned j = 0; j < proxy_chain[i].size(); ++j) {
129
        urls.push_back(proxy_chain[i][j].Print());
130
      }
131
      proxy_str +=
132
        "[" + StringifyInt(i) + "] " + JoinStrings(urls, ", ") + "\n";
133
    }
134
    proxy_str += "Active proxy: [" + StringifyInt(active_group) + "] " +
135
                 proxy_chain[active_group][0].url + "\n";
136
    if (fallback_group < proxy_chain.size())
137
      proxy_str += "First fallback group: [" +
138
                   StringifyInt(fallback_group) + "]\n";
139
  } else {
140
    proxy_str = "No proxies defined\n";
141
  }
142
  return proxy_str;
143
}
144
145
146
/**
147
 * Listener thread on the socket.
148
 * TODO(jblomer): create Format... helpers to shorten this method
149
 */
150
void *TalkManager::MainResponder(void *data) {
151
  TalkManager *talk_mgr = reinterpret_cast<TalkManager *>(data);
152
  MountPoint *mount_point = talk_mgr->mount_point_;
153
  FileSystem *file_system = mount_point->file_system();
154
  FuseRemounter *remounter = talk_mgr->remounter_;
155
  LogCvmfs(kLogTalk, kLogDebug, "talk thread started");
156
157
  struct sockaddr_un remote;
158
  socklen_t socket_size = sizeof(remote);
159
  int con_fd = -1;
160
  while (true) {
161
    if (con_fd >= 0) {
162
      shutdown(con_fd, SHUT_RDWR);
163
      close(con_fd);
164
    }
165
    LogCvmfs(kLogTalk, kLogDebug, "accepting connections on socketfd %d",
166
             talk_mgr->socket_fd_);
167
    if ((con_fd = accept(talk_mgr->socket_fd_,
168
                         (struct sockaddr *)&remote,
169
                         &socket_size)) < 0)
170
    {
171
      LogCvmfs(kLogTalk, kLogDebug, "terminating talk thread (fd %d, errno %d)",
172
               con_fd, errno);
173
      break;
174
    }
175
176
    char buf[kMaxCommandSize];
177
    int bytes_read;
178
    if ((bytes_read = recv(con_fd, buf, sizeof(buf), 0)) <= 0)
179
      continue;
180
181
    if (buf[bytes_read-1] == '\0')
182
      bytes_read--;
183
    const string line = string(buf, bytes_read);
184
    LogCvmfs(kLogTalk, kLogDebug, "received %s (length %u)",
185
             line.c_str(), line.length());
186
187
    if (line == "tracebuffer flush") {
188
      mount_point->tracer()->Flush();
189
      talk_mgr->Answer(con_fd, "OK\n");
190
    } else if (line == "cache size") {
191
      QuotaManager *quota_mgr = file_system->cache_mgr()->quota_mgr();
192
      if (!quota_mgr->HasCapability(QuotaManager::kCapIntrospectSize)) {
193
        talk_mgr->Answer(con_fd, "Cache cannot report its size\n");
194
      } else {
195
        uint64_t size_unpinned = quota_mgr->GetSize();
196
        uint64_t size_pinned = quota_mgr->GetSizePinned();
197
        const string size_str = "Current cache size is " +
198
          StringifyInt(size_unpinned / (1024*1024)) + "MB (" +
199
          StringifyInt(size_unpinned) + " Bytes), pinned: " +
200
          StringifyInt(size_pinned / (1024*1024)) + "MB (" +
201
          StringifyInt(size_pinned) + " Bytes)\n";
202
        talk_mgr->Answer(con_fd, size_str);
203
      }
204
    } else if (line == "cache instance") {
205
      talk_mgr->Answer(con_fd, file_system->cache_mgr()->Describe());
206
    } else if (line == "cache list") {
207
      QuotaManager *quota_mgr = file_system->cache_mgr()->quota_mgr();
208
      if (!quota_mgr->HasCapability(QuotaManager::kCapList)) {
209
        talk_mgr->Answer(con_fd, "Cache cannot list its entries\n");
210
      } else {
211
        vector<string> ls = quota_mgr->List();
212
        talk_mgr->AnswerStringList(con_fd, ls);
213
      }
214
    } else if (line == "cache list pinned") {
215
      QuotaManager *quota_mgr = file_system->cache_mgr()->quota_mgr();
216
      if (!quota_mgr->HasCapability(QuotaManager::kCapList)) {
217
        talk_mgr->Answer(con_fd, "Cache cannot list its entries\n");
218
      } else {
219
        vector<string> ls_pinned = quota_mgr->ListPinned();
220
        talk_mgr->AnswerStringList(con_fd, ls_pinned);
221
      }
222
    } else if (line == "cache list catalogs") {
223
      QuotaManager *quota_mgr = file_system->cache_mgr()->quota_mgr();
224
      if (!quota_mgr->HasCapability(QuotaManager::kCapList)) {
225
        talk_mgr->Answer(con_fd, "Cache cannot list its entries\n");
226
      } else {
227
        vector<string> ls_catalogs = quota_mgr->ListCatalogs();
228
        talk_mgr->AnswerStringList(con_fd, ls_catalogs);
229
      }
230
    } else if (line.substr(0, 12) == "cleanup rate") {
231
      QuotaManager *quota_mgr = file_system->cache_mgr()->quota_mgr();
232
      if (!quota_mgr->HasCapability(QuotaManager::kCapIntrospectCleanupRate)) {
233
        talk_mgr->Answer(con_fd, "Unsupported by this cache\n");
234
      } else {
235
        if (line.length() < 14) {
236
          talk_mgr->Answer(con_fd, "Usage: cleanup rate <period in mn>\n");
237
        } else {
238
          const uint64_t period_s = String2Uint64(line.substr(13)) * 60;
239
          const uint64_t rate = quota_mgr->GetCleanupRate(period_s);
240
          talk_mgr->Answer(con_fd, StringifyInt(rate) + "\n");
241
        }
242
      }
243
    } else if (line.substr(0, 7) == "cleanup") {
244
      QuotaManager *quota_mgr = file_system->cache_mgr()->quota_mgr();
245
      if (!quota_mgr->HasCapability(QuotaManager::kCapShrink)) {
246
        talk_mgr->Answer(con_fd, "Cache cannot trigger eviction\n");
247
      } else {
248
        if (line.length() < 9) {
249
          talk_mgr->Answer(con_fd, "Usage: cleanup <MB>\n");
250
        } else {
251
          const uint64_t size = String2Uint64(line.substr(8))*1024*1024;
252
          if (quota_mgr->Cleanup(size)) {
253
            talk_mgr->Answer(con_fd, "OK\n");
254
          } else {
255
            talk_mgr->Answer(con_fd, "Not fully cleaned "
256
                                     "(there might be pinned chunks)\n");
257
          }
258
        }
259
      }
260
    } else if (line.substr(0, 5) == "evict") {
261
      assert(mount_point->file_system()->type() == FileSystem::kFsFuse);
262
      if (line.length() < 7) {
263
        talk_mgr->Answer(con_fd, "Usage: evict <path>\n");
264
      } else {
265
        const string path = line.substr(6);
266
        const bool found_regular = cvmfs::Evict(path);
267
        if (found_regular)
268
          talk_mgr->Answer(con_fd, "OK\n");
269
        else
270
          talk_mgr->Answer(con_fd, "No such regular file\n");
271
      }
272
    } else if (line.substr(0, 3) == "pin") {
273
      assert(mount_point->file_system()->type() == FileSystem::kFsFuse);
274
      if (line.length() < 5) {
275
        talk_mgr->Answer(con_fd, "Usage: pin <path>\n");
276
      } else {
277
        const string path = line.substr(4);
278
        const bool found_regular = cvmfs::Pin(path);
279
        if (found_regular)
280
          talk_mgr->Answer(con_fd, "OK\n");
281
        else
282
          talk_mgr->Answer(con_fd, "No such regular file or pinning failed\n");
283
      }
284
    } else if (line == "mountpoint") {
285
      talk_mgr->Answer(con_fd, cvmfs::loader_exports_->mount_point + "\n");
286
    } else if (line.substr(0, 7) == "remount") {
287
      FuseRemounter::Status status;
288
      if (line == "remount sync")
289
        status = remounter->CheckSynchronously();
290
      else
291
        status = remounter->Check();
292
      switch (status) {
293
        case FuseRemounter::kStatusFailGeneral:
294
          talk_mgr->Answer(con_fd, "Failed\n");
295
          break;
296
        case FuseRemounter::kStatusFailNoSpace:
297
          talk_mgr->Answer(con_fd, "Failed (no space)\n");
298
          break;
299
        case FuseRemounter::kStatusUp2Date:
300
          talk_mgr->Answer(con_fd, "Catalog up to date\n");
301
          break;
302
        case FuseRemounter::kStatusDraining:
303
          talk_mgr->Answer(con_fd, "New revision applied\n");
304
          break;
305
        case FuseRemounter::kStatusMaintenance:
306
          talk_mgr->Answer(con_fd, "In maintenance mode\n");
307
          break;
308
        default:
309
          talk_mgr->Answer(con_fd, "internal error\n");
310
      }
311
    } else if (line == "detach nested catalogs") {
312
      mount_point->catalog_mgr()->DetachNested();
313
      talk_mgr->Answer(con_fd, "OK\n");
314
    } else if (line == "revision") {
315
      string revision = StringifyInt(mount_point->catalog_mgr()->GetRevision());
316
      talk_mgr->Answer(con_fd, revision + "\n");
317
    } else if (line == "max ttl info") {
318
      const unsigned max_ttl = mount_point->GetMaxTtlMn();
319
      if (max_ttl == 0) {
320
        talk_mgr->Answer(con_fd, "unset\n");
321
      } else {
322
        const string max_ttl_str = StringifyInt(max_ttl) + " minutes\n";
323
        talk_mgr->Answer(con_fd, max_ttl_str);
324
      }
325
    } else if (line.substr(0, 11) == "max ttl set") {
326
      if (line.length() < 13) {
327
        talk_mgr->Answer(con_fd, "Usage: max ttl set <minutes>\n");
328
      } else {
329
        const unsigned max_ttl = String2Uint64(line.substr(12));
330
        mount_point->SetMaxTtlMn(max_ttl);
331
        talk_mgr->Answer(con_fd, "OK\n");
332
      }
333
    } else if (line.substr(0, 14) == "nameserver get") {
334
      const string dns_server = mount_point->download_mgr()->GetDnsServer();
335
      const string reply = !dns_server.empty() ?
336
        std::string("DNS server address: ") + dns_server + "\n":
337
        std::string("DNS server not set.\n");
338
      talk_mgr->Answer(con_fd, reply);
339
    } else if (line.substr(0, 14) == "nameserver set") {
340
      if (line.length() < 16) {
341
        talk_mgr->Answer(con_fd, "Usage: nameserver set <host>\n");
342
      } else {
343
        const string host = line.substr(15);
344
        mount_point->download_mgr()->SetDnsServer(host);
345
        talk_mgr->Answer(con_fd, "OK\n");
346
      }
347
    } else if (line == "external host info") {
348
      string external_host_info =
349
        talk_mgr->FormatHostInfo(mount_point->external_download_mgr());
350
      talk_mgr->Answer(con_fd, external_host_info);
351
    } else if (line == "host info") {
352
      string host_info = talk_mgr->FormatHostInfo(mount_point->download_mgr());
353
      talk_mgr->Answer(con_fd, host_info);
354
    } else if (line == "host probe") {
355
      mount_point->download_mgr()->ProbeHosts();
356
      talk_mgr->Answer(con_fd, "OK\n");
357
    } else if (line == "host probe geo") {
358
      bool retval = mount_point->download_mgr()->ProbeGeo();
359
      if (retval)
360
        talk_mgr->Answer(con_fd, "OK\n");
361
      else
362
        talk_mgr->Answer(con_fd, "Failed\n");
363
    } else if (line == "external host switch") {
364
      mount_point->external_download_mgr()->SwitchHost();
365
      talk_mgr->Answer(con_fd, "OK\n");
366
    } else if (line == "host switch") {
367
      mount_point->download_mgr()->SwitchHost();
368
      talk_mgr->Answer(con_fd, "OK\n");
369
    } else if (line.substr(0, 17) == "external host set") {
370
      if (line.length() < 19) {
371
        talk_mgr->Answer(con_fd, "Usage: external host set <URL>\n");
372
      } else {
373
        const std::string host = line.substr(18);
374
        mount_point->external_download_mgr()->SetHostChain(host);
375
        talk_mgr->Answer(con_fd, "OK\n");
376
      }
377
    } else if (line.substr(0, 8) == "host set") {
378
      if (line.length() < 10) {
379
        talk_mgr->Answer(con_fd, "Usage: host set <host list>\n");
380
      } else {
381
        const string hosts = line.substr(9);
382
        mount_point->download_mgr()->SetHostChain(hosts);
383
        talk_mgr->Answer(con_fd, "OK\n");
384
      }
385
    } else if (line == "external proxy info") {
386
      string external_proxy_info =
387
        talk_mgr->FormatProxyInfo(mount_point->external_download_mgr());
388
      talk_mgr->Answer(con_fd, external_proxy_info);
389
    } else if (line == "proxy info") {
390
      string proxy_info =
391
        talk_mgr->FormatProxyInfo(mount_point->download_mgr());
392
      talk_mgr->Answer(con_fd, proxy_info);
393
    } else if (line == "proxy rebalance") {
394
      mount_point->download_mgr()->RebalanceProxies();
395
      talk_mgr->Answer(con_fd, "OK\n");
396
    } else if (line == "proxy group switch") {
397
      mount_point->download_mgr()->SwitchProxyGroup();
398
      talk_mgr->Answer(con_fd, "OK\n");
399
    } else if (line.substr(0, 18) == "external proxy set") {
400
      if (line.length() < 20) {
401
        talk_mgr->Answer(con_fd, "Usage: external proxy set <proxy list>\n");
402
      } else {
403
        string external_proxies = line.substr(19);
404
        mount_point->external_download_mgr()->SetProxyChain(
405
          external_proxies, "", download::DownloadManager::kSetProxyRegular);
406
        talk_mgr->Answer(con_fd, "OK\n");
407
      }
408
    } else if (line.substr(0, 9) == "proxy set") {
409
      if (line.length() < 11) {
410
        talk_mgr->Answer(con_fd, "Usage: proxy set <proxy list>\n");
411
      } else {
412
        string proxies = line.substr(10);
413
        proxies =
414
          download::ResolveProxyDescription(proxies, "",
415
                                            mount_point->download_mgr());
416
        if (proxies == "") {
417
            talk_mgr->Answer(con_fd, "Failed, no valid proxies\n");
418
        } else {
419
          mount_point->download_mgr()->SetProxyChain(
420
            proxies, "", download::DownloadManager::kSetProxyRegular);
421
          talk_mgr->Answer(con_fd, "OK\n");
422
        }
423
      }
424
    } else if (line.substr(0, 14) == "proxy fallback") {
425
      if (line.length() < 15) {
426
        talk_mgr->Answer(con_fd, "Usage: proxy fallback <proxy list>\n");
427
      } else {
428
        string fallback_proxies = line.substr(15);
429
        mount_point->download_mgr()->SetProxyChain(
430
          "", fallback_proxies, download::DownloadManager::kSetProxyFallback);
431
        talk_mgr->Answer(con_fd, "OK\n");
432
      }
433
    } else if (line == "timeout info") {
434
      unsigned timeout;
435
      unsigned timeout_direct;
436
      mount_point->download_mgr()->GetTimeout(&timeout, &timeout_direct);
437
      string timeout_str =  "Timeout with proxy: ";
438
      if (timeout)
439
        timeout_str += StringifyInt(timeout) + "s\n";
440
      else
441
        timeout_str += "no timeout\n";
442
      timeout_str += "Timeout without proxy: ";
443
      if (timeout_direct)
444
        timeout_str += StringifyInt(timeout_direct) + "s\n";
445
      else
446
        timeout_str += "no timeout\n";
447
      talk_mgr->Answer(con_fd, timeout_str);
448
    } else if (line.substr(0, 11) == "timeout set") {
449
      if (line.length() < 13) {
450
        talk_mgr->Answer(con_fd, "Usage: timeout set <proxy> <direct>\n");
451
      } else {
452
        uint64_t timeout;
453
        uint64_t timeout_direct;
454
        String2Uint64Pair(line.substr(12), &timeout, &timeout_direct);
455
        mount_point->download_mgr()->SetTimeout(timeout, timeout_direct);
456
        talk_mgr->Answer(con_fd, "OK\n");
457
      }
458
    } else if (line == "open catalogs") {
459
      talk_mgr->Answer(con_fd, mount_point->catalog_mgr()->PrintHierarchy());
460
    } else if (line == "internal affairs") {
461
      int current;
462
      int highwater;
463
      string result;
464
465
      result += "Inode Generation:\n  " + cvmfs::PrintInodeGeneration();
466
467
      // Manually setting the values of the ShortString counters
468
      mount_point->statistics()->Lookup("pathstring.n_instances")->
469
          Set(PathString::num_instances());
470
      mount_point->statistics()->Lookup("pathstring.n_overflows")->
471
          Set(PathString::num_overflows());
472
      mount_point->statistics()->Lookup("namestring.n_instances")->
473
          Set(NameString::num_instances());
474
      mount_point->statistics()->Lookup("namestring.n_overflows")->
475
          Set(NameString::num_overflows());
476
      mount_point->statistics()->Lookup("linkstring.n_instances")->
477
          Set(LinkString::num_instances());
478
      mount_point->statistics()->Lookup("linkstring.n_overflows")->
479
          Set(LinkString::num_overflows());
480
481
      // Manually setting the inode tracker numbers
482
      glue::InodeTracker::Statistics inode_stats =
483
        mount_point->inode_tracker()->GetStatistics();
484
      mount_point->statistics()->Lookup("inode_tracker.n_insert")->Set(
485
        atomic_read64(&inode_stats.num_inserts));
486
      mount_point->statistics()->Lookup("inode_tracker.n_remove")->Set(
487
        atomic_read64(&inode_stats.num_removes));
488
      mount_point->statistics()->Lookup("inode_tracker.no_reference")->Set(
489
        atomic_read64(&inode_stats.num_references));
490
      mount_point->statistics()->Lookup("inode_tracker.n_hit_inode")->Set(
491
        atomic_read64(&inode_stats.num_hits_inode));
492
      mount_point->statistics()->Lookup("inode_tracker.n_hit_path")->Set(
493
        atomic_read64(&inode_stats.num_hits_path));
494
      mount_point->statistics()->Lookup("inode_tracker.n_miss_path")->Set(
495
        atomic_read64(&inode_stats.num_misses_path));
496
497
      if (file_system->cache_mgr()->id() == kPosixCacheManager) {
498
        PosixCacheManager *cache_mgr =
499
          reinterpret_cast<PosixCacheManager *>(
500
            file_system->cache_mgr());
501
        result += "\nCache Mode: ";
502
        switch (cache_mgr->cache_mode()) {
503
          case PosixCacheManager::kCacheReadWrite:
504
            result += "read-write";
505
            break;
506
          case PosixCacheManager::kCacheReadOnly:
507
            result += "read-only";
508
            break;
509
          default:
510
            result += "unknown";
511
        }
512
      }
513
      bool drainout_mode;
514
      bool maintenance_mode;
515
      cvmfs::GetReloadStatus(&drainout_mode, &maintenance_mode);
516
      result += "\nDrainout Mode: " + StringifyBool(drainout_mode) + "\n";
517
      result += "Maintenance Mode: " + StringifyBool(maintenance_mode) + "\n";
518
519
      if (file_system->IsNfsSource()) {
520
        result += "\nNFS Map Statistics:\n";
521
        result += file_system->nfs_maps()->GetStatistics();
522
      }
523
524
      result += "SQlite Statistics:\n";
525
      sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &current, &highwater, 0);
526
      result += "  Number of allocations " + StringifyInt(current) + "\n";
527
528
      sqlite3_status(SQLITE_STATUS_MEMORY_USED, &current, &highwater, 0);
529
      result += "  General purpose allocator " +StringifyInt(current/1024) +
530
                " KB / " + StringifyInt(highwater/1024) + " KB\n";
531
532
      sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &current, &highwater, 0);
533
      result += "  Largest malloc " + StringifyInt(highwater) + " Bytes\n";
534
535
      sqlite3_status(SQLITE_STATUS_PAGECACHE_USED, &current, &highwater, 0);
536
      result += "  Page cache allocations " + StringifyInt(current) + " / " +
537
                StringifyInt(highwater) + "\n";
538
539
      sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW,
540
                     &current, &highwater, 0);
541
      result += "  Page cache overflows " + StringifyInt(current/1024) +
542
                " KB / " + StringifyInt(highwater/1024) + " KB\n";
543
544
      sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &current, &highwater, 0);
545
      result += "  Largest page cache allocation " + StringifyInt(highwater) +
546
                " Bytes\n";
547
548
      sqlite3_status(SQLITE_STATUS_SCRATCH_USED, &current, &highwater, 0);
549
      result += "  Scratch allocations " + StringifyInt(current) + " / " +
550
                StringifyInt(highwater) + "\n";
551
552
      sqlite3_status(SQLITE_STATUS_SCRATCH_OVERFLOW, &current, &highwater, 0);
553
      result += "  Scratch overflows " + StringifyInt(current) + " / " +
554
                StringifyInt(highwater) + "\n";
555
556
      sqlite3_status(SQLITE_STATUS_SCRATCH_SIZE, &current, &highwater, 0);
557
      result += "  Largest scratch allocation " + StringifyInt(highwater/1024)
558
                + " KB\n";
559
560
      result += "\nPer-Connection Memory Statistics:\n" +
561
                mount_point->catalog_mgr()->PrintAllMemStatistics();
562
563
      result += "\nRaw Counters:\n" +
564
        mount_point->statistics()->PrintList(perf::Statistics::kPrintHeader);
565
566
      talk_mgr->Answer(con_fd, result);
567
    } else if (line == "reset error counters") {
568
      file_system->ResetErrorCounters();
569
      talk_mgr->Answer(con_fd, "OK\n");
570
    } else if (line == "pid") {
571
      const string pid_str = StringifyInt(cvmfs::pid_) + "\n";
572
      talk_mgr->Answer(con_fd, pid_str);
573
    } else if (line == "pid cachemgr") {
574
      const string pid_str =
575
        StringifyInt(file_system->cache_mgr()->quota_mgr()->GetPid()) + "\n";
576
      talk_mgr->Answer(con_fd, pid_str);
577
    } else if (line == "pid watchdog") {
578
      const string pid_str = StringifyInt(Watchdog::GetPid()) + "\n";
579
      talk_mgr->Answer(con_fd, pid_str);
580
    } else if (line == "parameters") {
581
      talk_mgr->Answer(con_fd, file_system->options_mgr()->Dump());
582
    } else if (line == "hotpatch history") {
583
      string history_str =
584
        StringifyTime(cvmfs::loader_exports_->boot_time, true) +
585
        "    (start of CernVM-FS loader " +
586
        cvmfs::loader_exports_->loader_version + ")\n";
587
      for (loader::EventList::const_iterator i =
588
           cvmfs::loader_exports_->history.begin(),
589
           iEnd = cvmfs::loader_exports_->history.end(); i != iEnd; ++i)
590
      {
591
        history_str += StringifyTime((*i)->timestamp, true) +
592
          "    (loaded CernVM-FS Fuse Module " +
593
          (*i)->so_version + ")\n";
594
      }
595
      talk_mgr->Answer(con_fd, history_str);
596
    } else if (line == "vfs inodes") {
597
      string result;
598
      glue::InodeTracker::Cursor cursor(
599
        mount_point->inode_tracker()->BeginEnumerate());
600
      uint64_t inode;
601
      while (mount_point->inode_tracker()->NextInode(&cursor, &inode)) {
602
        result += StringifyInt(inode) + "\n";
603
      }
604
      mount_point->inode_tracker()->EndEnumerate(&cursor);
605
      talk_mgr->Answer(con_fd, result);
606
    } else if (line == "vfs entries") {
607
      string result;
608
      glue::InodeTracker::Cursor cursor(
609
        mount_point->inode_tracker()->BeginEnumerate());
610
      uint64_t inode_parent;
611
      NameString name;
612
      while (mount_point->inode_tracker()->NextEntry(
613
        &cursor, &inode_parent, &name))
614
      {
615
        result += "<" + StringifyInt(inode_parent) + ">/" + name.ToString() +
616
                  "\n";
617
      }
618
      mount_point->inode_tracker()->EndEnumerate(&cursor);
619
      talk_mgr->Answer(con_fd, result);
620
    } else if (line == "version") {
621
      string version_str = string(VERSION) + " (CernVM-FS Fuse Module)\n" +
622
        cvmfs::loader_exports_->loader_version + " (Loader)\n";
623
      talk_mgr->Answer(con_fd, version_str);
624
    } else if (line == "version patchlevel") {
625
      talk_mgr->Answer(con_fd, string(CVMFS_PATCH_LEVEL) + "\n");
626
    } else if (line == "tear down to read-only") {
627
      if (file_system->cache_mgr()->id() != kPosixCacheManager) {
628
        talk_mgr->Answer(con_fd, "not supported\n");
629
      } else {
630
        // hack
631
        cvmfs::UnregisterQuotaListener();
632
        file_system->TearDown2ReadOnly();
633
        talk_mgr->Answer(con_fd, "In read-only mode\n");
634
      }
635
    } else {
636
      talk_mgr->Answer(con_fd, "unknown command\n");
637
    }
638
  }
639
640
  return NULL;
641
}
642
643
644
TalkManager::TalkManager(
645
  const string &socket_path,
646
  MountPoint *mount_point,
647
  FuseRemounter *remounter)
648
  : socket_path_(socket_path)
649
  , socket_fd_(-1)
650
  , mount_point_(mount_point)
651
  , remounter_(remounter)
652
  , spawned_(false)
653
{
654
  memset(&thread_talk_, 0, sizeof(thread_talk_));
655
}
656
657
658
TalkManager::~TalkManager() {
659
  if (!socket_path_.empty()) {
660
    int retval = unlink(socket_path_.c_str());
661
    if (retval != 0) {
662
      LogCvmfs(kLogTalk, kLogSyslogWarn,
663
               "Could not remove cvmfs_io socket from cache directory (%d)",
664
               errno);
665
    }
666
  }
667
668
  if (socket_fd_ >= 0) {
669
    shutdown(socket_fd_, SHUT_RDWR);
670
    close(socket_fd_);
671
  }
672
673
  if (spawned_) {
674
    pthread_join(thread_talk_, NULL);
675
    LogCvmfs(kLogTalk, kLogDebug, "talk thread stopped");
676
  }
677
}
678
679
680
void TalkManager::Spawn() {
681
  int retval = pthread_create(&thread_talk_, NULL, MainResponder, this);
682
  assert(retval == 0);
683
  spawned_ = true;
684
}