GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/cvmfs_talk.cc
Date: 2026-10-04 02:40:33
Exec Total Coverage
Lines: 0 115 0.0%
Branches: 0 70 0.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * cvmfs_talk runs query-response cycles against a running cvmfs instance.
5 */
6
7 #include <errno.h>
8 #include <unistd.h>
9
10 #include <cassert>
11 #include <cstring>
12 #include <string>
13 #include <vector>
14
15 #include "options.h"
16 #include "util/logging.h"
17 #include "util/posix.h"
18 #include "util/string.h"
19
20
21 struct InstanceInfo {
22 ✗ bool IsDefined() { return !socket_path.empty() || !instance_name.empty(); }
23
24 // Called at most once by DeterminePath
25 ✗ static std::string GetDefaultDomain() {
26 ✗ std::string result;
27 ✗ BashOptionsManager options_mgr;
28 ✗ options_mgr.ParseDefault("");
29 ✗ const bool retval = options_mgr.GetValue("CVMFS_DEFAULT_DOMAIN", &result);
30 ✗ if (!retval) {
31 ✗ LogCvmfs(kLogCvmfs, kLogStderr,
32 "Error: could not determine CVMFS_DEFAULT_DOMAIN");
33 }
34 ✗ return result;
35 }
36
37 ✗ bool DeterminePaths() {
38 ✗ std::string fqrn = instance_name;
39 ✗ if (fqrn.find('.') == std::string::npos) {
40 ✗ static const std::string default_domain = GetDefaultDomain();
41 ✗ fqrn = fqrn + "." + default_domain;
42 }
43
44 ✗ BashOptionsManager options_mgr;
45 ✗ options_mgr.ParseDefault(fqrn);
46 ✗ if (!options_mgr.GetValue("CVMFS_WORKSPACE", &workspace)) {
47 ✗ if (!options_mgr.GetValue("CVMFS_CACHE_DIR", &workspace)) {
48 ✗ const bool retval = options_mgr.GetValue("CVMFS_CACHE_BASE",
49 &workspace);
50 ✗ if (!retval) {
51 ✗ LogCvmfs(kLogCvmfs, kLogStderr,
52 "CVMFS_WORKSPACE, CVMFS_CACHE_DIR, and CVMFS_CACHE_BASE "
53 "missing");
54 ✗ return false;
55 }
56
57 ✗ std::string optarg;
58 ✗ if (options_mgr.GetValue("CVMFS_SHARED_CACHE", &optarg)
59 ✗ && options_mgr.IsOn(optarg)) {
60 ✗ workspace += "/shared";
61 } else {
62 ✗ workspace += "/" + fqrn;
63 }
64 }
65 }
66
67 ✗ socket_path = workspace + "/cvmfs_io." + fqrn;
68 ✗ return true;
69 }
70
71 ✗ bool CompleteInfo() {
72 ✗ assert(IsDefined());
73
74 ✗ if (socket_path.empty()) {
75 ✗ const bool retval = DeterminePaths();
76 ✗ if (!retval)
77 ✗ return false;
78 ✗ identifier = "instance '" + instance_name + "' active in " + workspace;
79 } else {
80 ✗ workspace = GetParentPath(socket_path);
81 ✗ identifier = "instance listening at " + socket_path;
82 }
83 ✗ return true;
84 }
85
86 std::string socket_path;
87 std::string instance_name;
88 std::string workspace;
89 std::string identifier;
90 };
91
92
93 ✗ static bool ReadResponse(int fd) {
94 ✗ std::string line;
95 char buf;
96 int retval;
97 ✗ while ((retval = read(fd, &buf, 1)) == 1) {
98 ✗ if (buf == '\n') {
99 ✗ LogCvmfs(kLogCvmfs, kLogStdout, "%s", line.c_str());
100 ✗ line.clear();
101 ✗ continue;
102 }
103 ✗ line.push_back(buf);
104 }
105 ✗ return retval == 0;
106 }
107
108
109 ✗ bool SendCommand(const std::string &command, InstanceInfo instance_info) {
110 ✗ bool retval = instance_info.CompleteInfo();
111 ✗ if (!retval)
112 ✗ return false;
113
114 ✗ const int fd = ConnectSocket(instance_info.socket_path);
115 ✗ if (fd < 0) {
116 ✗ if (errno == ENOENT) {
117 ✗ LogCvmfs(kLogCvmfs, kLogStderr,
118 "Seems like CernVM-FS is not running in %s (not found: %s)",
119 instance_info.workspace.c_str(),
120 instance_info.socket_path.c_str());
121 } else {
122 ✗ LogCvmfs(kLogCvmfs, kLogStderr, "Could not access %s (%d - %s)",
123 instance_info.identifier.c_str(), errno, strerror(errno));
124 }
125 ✗ return false;
126 }
127
128 ✗ WritePipe(fd, command.data(), command.size());
129 ✗ retval = ReadResponse(fd);
130 ✗ close(fd);
131
132 ✗ if (!retval) {
133 ✗ LogCvmfs(kLogCvmfs, kLogStderr, "Broken connection to %s (%d - %s)",
134 instance_info.identifier.c_str(), errno, strerror(errno));
135 ✗ return false;
136 }
137 ✗ return true;
138 }
139
140
141 ✗ static void Usage(const std::string &exe) {
142 ✗ LogCvmfs(
143 kLogCvmfs, kLogStdout,
144 "Usage: %s [-i instance | -p socket] <command> \n"
145 " By default, iterate through all instances defined in \n"
146 " CVMFS_REPOSITORIES \n"
147 "\n"
148 "Example: \n"
149 " %s -i atlas.cern.ch pid \n"
150 "\n"
151 "Commands: \n"
152 " tracebuffer flush flushes the trace buffer to disk \n"
153 " cache instance describes the active cache manager \n"
154 " cache size gets current size of file cache \n"
155 " cache limit get gets current size limit of the file cache\n"
156 " cache limit set <MB> sets the max size limit of the file cache\n"
157 " cache list gets files in cache \n"
158 " cache list pinned gets pinned file catalogs in cache \n"
159 " cache list catalogs gets all file catalogs in cache \n"
160 " cleanup <MB> cleans file cache until size <= <MB> \n"
161 " cleanup rate <period> n.o. cleanups in the last <period> min \n"
162 " evict <path> removes <path> from the cache \n"
163 " pin <path> pins <path> in the cache \n"
164 " mountpoint returns the mount point \n"
165 " device id returns major:minor virtual device id \n"
166 " on Linux and 0:0 on macOS \n"
167 " remount [sync] look for new catalogs \n"
168 " revision gets the repository revision \n"
169 " max ttl info gets the maximum ttl \n"
170 " max ttl set <minutes> sets the maximum ttl \n"
171 " nameserver get get the DNS server \n"
172 " nameserver set <host> sets a DNS server \n"
173 " host info get host chain and their rtt, \n"
174 " if already probed \n"
175 " host probe orders the host chain according to rtt \n"
176 " host probe geo let Stratum 1s order the host chain and \n"
177 " fallback proxies using the Geo-API \n"
178 " host switch switches to the next host in the chain \n"
179 " host set <host list> sets a new host chain \n"
180 " proxy info gets load-balance proxy groups \n"
181 " proxy rebalance randomly selects a new proxy server \n"
182 " from the current load-balance group \n"
183 " proxy group switch switches to the next load-balance \n"
184 " proxy group in the chain \n"
185 " proxy set <proxy list> sets a new chain of load-balance proxy \n"
186 " groups (not including fallback proxies) \n"
187 " proxy fallback <list> sets a new list of fallback proxies \n"
188 " external host info gets info about external host chain \n"
189 " external host switch switches to the next external host \n"
190 " external host set \n"
191 " <host list> sets external host chain \n"
192 " external proxy info gets info about external proxy groups \n"
193 " external proxy set \n"
194 " <proxy list> sets chain of external proxy groups \n"
195 " timeout info gets the network timeouts \n"
196 " timeout set \n"
197 " <proxy> <direct> sets the network timeouts in seconds \n"
198 " pid gets the pid \n"
199 " pid cachemgr gets the pid of the shared cache manager \n"
200 " pid watchdog gets the pid of the crash handler process\n"
201 " parameters dumps the effective parameters \n"
202 " reset error counters resets the counter for I/O errors \n"
203 " hotpatch history shows timestamps and version info of \n"
204 " loaded (hotpatched) Fuse modules \n"
205 " version gets cvmfs version \n"
206 " version patchlevel gets cvmfs patchlevel \n"
207 " open catalogs shows information about currently \n"
208 " loaded catalogs (_not_ all cached ones) \n"
209 " latency show the latencies of different fuse \n"
210 " calls (requires CVMFS_INSTRUMENT_FUSE) \n"
211 " metrics prometheus outputs all metrics in Prometheus format \n"
212 "\n",
213 exe.c_str(), exe.c_str());
214 }
215
216
217 ✗ int main(int argc, char *argv[]) {
218 ✗ InstanceInfo instance_info;
219 ✗ std::string command;
220
221 int c;
222 // 's' for socket would have been a better option letter but we keep 'p'
223 // for backwards compatibility. The '+' at the beginning of the option string
224 // prevents permutation of the option and non-option arguments.
225 ✗ while ((c = getopt(argc, argv, "+hi:p:")) != -1) {
226 ✗ switch (c) {
227 ✗ case 'h':
228 ✗ Usage(argv[0]);
229 ✗ return 0;
230 ✗ case 'p':
231 ✗ instance_info.socket_path = optarg;
232 ✗ break;
233 ✗ case 'i':
234 ✗ instance_info.instance_name = optarg;
235 ✗ break;
236 ✗ case '?':
237 default:
238 ✗ Usage(argv[0]);
239 ✗ return 1;
240 }
241 }
242
243 ✗ for (; optind < argc; ++optind) {
244 ✗ command += argv[optind];
245 ✗ if (optind < (argc - 1))
246 ✗ command.push_back(' ');
247 }
248 ✗ if (command.empty()) {
249 ✗ Usage(argv[0]);
250 ✗ return 1;
251 }
252
253 ✗ int retcode = 0;
254 ✗ if (!instance_info.IsDefined()) {
255 ✗ BashOptionsManager options_mgr;
256 ✗ options_mgr.ParseDefault("");
257 ✗ std::string opt_repos;
258 ✗ options_mgr.GetValue("CVMFS_REPOSITORIES", &opt_repos);
259 ✗ std::vector<std::string> repos = SplitString(opt_repos, ',');
260 ✗ bool is_empty_repo_list = true;
261 ✗ for (unsigned i = 0; i < repos.size(); ++i) {
262 ✗ if (repos[i].empty())
263 ✗ continue;
264 ✗ is_empty_repo_list = false;
265 ✗ instance_info.instance_name = repos[i];
266 ✗ LogCvmfs(kLogCvmfs, kLogStdout, "%s:", repos[i].c_str());
267 ✗ const bool retval = SendCommand(command, instance_info);
268 ✗ if (!retval)
269 ✗ retcode = 1;
270 }
271 ✗ if (is_empty_repo_list) {
272 ✗ LogCvmfs(kLogCvmfs, kLogStdout,
273 "Warning: no instance was specified. In this case, the command "
274 "is executed for all instances defined in CVMFS_REPOSITORIES but"
275 " this list is empty. Did you mean running \n\n"
276 " cvmfs_talk -i <repository name> <command>\n");
277 }
278 ✗ } else {
279 ✗ const bool retval = SendCommand(command, instance_info);
280 ✗ if (!retval)
281 ✗ retcode = 1;
282 }
283 ✗ return retcode;
284 }
285