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/pointer.h" |
18 |
|
|
#include "util/posix.h" |
19 |
|
|
#include "util/string.h" |
20 |
|
|
|
21 |
|
|
|
22 |
|
|
struct InstanceInfo { |
23 |
|
✗ |
bool IsDefined() { |
24 |
|
✗ |
return !socket_path.empty() || !instance_name.empty(); |
25 |
|
|
} |
26 |
|
|
|
27 |
|
|
// Called at most once by DeterminePath |
28 |
|
✗ |
static std::string GetDefaultDomain() { |
29 |
|
✗ |
std::string result; |
30 |
|
✗ |
BashOptionsManager options_mgr; |
31 |
|
✗ |
options_mgr.ParseDefault(""); |
32 |
|
✗ |
bool retval = options_mgr.GetValue("CVMFS_DEFAULT_DOMAIN", &result); |
33 |
|
✗ |
if (!retval) { |
34 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, |
35 |
|
|
"Error: could not determine CVMFS_DEFAULT_DOMAIN"); |
36 |
|
|
} |
37 |
|
✗ |
return result; |
38 |
|
|
} |
39 |
|
|
|
40 |
|
✗ |
bool DeterminePaths() { |
41 |
|
✗ |
std::string fqrn = instance_name; |
42 |
|
✗ |
if (fqrn.find('.') == std::string::npos) { |
43 |
|
✗ |
static std::string default_domain = GetDefaultDomain(); |
44 |
|
✗ |
fqrn = fqrn + "." + default_domain; |
45 |
|
|
} |
46 |
|
|
|
47 |
|
✗ |
BashOptionsManager options_mgr; |
48 |
|
✗ |
options_mgr.ParseDefault(fqrn); |
49 |
|
✗ |
if (!options_mgr.GetValue("CVMFS_WORKSPACE", &workspace)) { |
50 |
|
✗ |
if (!options_mgr.GetValue("CVMFS_CACHE_DIR", &workspace)) { |
51 |
|
✗ |
bool retval = options_mgr.GetValue("CVMFS_CACHE_BASE", &workspace); |
52 |
|
✗ |
if (!retval) { |
53 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, |
54 |
|
|
"CVMFS_WORKSPACE, CVMFS_CACHE_DIR, and CVMFS_CACHE_BASE " |
55 |
|
|
"missing"); |
56 |
|
✗ |
return false; |
57 |
|
|
} |
58 |
|
|
|
59 |
|
✗ |
std::string optarg; |
60 |
|
✗ |
if (options_mgr.GetValue("CVMFS_SHARED_CACHE", &optarg) && |
61 |
|
✗ |
options_mgr.IsOn(optarg)) |
62 |
|
|
{ |
63 |
|
✗ |
workspace += "/shared"; |
64 |
|
|
} else { |
65 |
|
✗ |
workspace += "/" + fqrn; |
66 |
|
|
} |
67 |
|
|
} |
68 |
|
|
} |
69 |
|
|
|
70 |
|
✗ |
socket_path = workspace + "/cvmfs_io." + fqrn; |
71 |
|
✗ |
return true; |
72 |
|
|
} |
73 |
|
|
|
74 |
|
✗ |
bool CompleteInfo() { |
75 |
|
✗ |
assert(IsDefined()); |
76 |
|
|
|
77 |
|
✗ |
if (socket_path.empty()) { |
78 |
|
✗ |
bool retval = DeterminePaths(); |
79 |
|
✗ |
if (!retval) |
80 |
|
✗ |
return false; |
81 |
|
✗ |
identifier = "instance '" + instance_name + "' active in " + workspace; |
82 |
|
|
} else { |
83 |
|
✗ |
workspace = GetParentPath(socket_path); |
84 |
|
✗ |
identifier = "instance listening at " + socket_path; |
85 |
|
|
} |
86 |
|
✗ |
return true; |
87 |
|
|
} |
88 |
|
|
|
89 |
|
|
std::string socket_path; |
90 |
|
|
std::string instance_name; |
91 |
|
|
std::string workspace; |
92 |
|
|
std::string identifier; |
93 |
|
|
}; |
94 |
|
|
|
95 |
|
|
|
96 |
|
✗ |
static bool ReadResponse(int fd) { |
97 |
|
✗ |
std::string line; |
98 |
|
|
char buf; |
99 |
|
|
int retval; |
100 |
|
✗ |
while ((retval = read(fd, &buf, 1)) == 1) { |
101 |
|
✗ |
if (buf == '\n') { |
102 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStdout, "%s", line.c_str()); |
103 |
|
✗ |
line.clear(); |
104 |
|
✗ |
continue; |
105 |
|
|
} |
106 |
|
✗ |
line.push_back(buf); |
107 |
|
|
} |
108 |
|
✗ |
return retval == 0; |
109 |
|
|
} |
110 |
|
|
|
111 |
|
|
|
112 |
|
✗ |
bool SendCommand(const std::string &command, InstanceInfo instance_info) { |
113 |
|
✗ |
bool retval = instance_info.CompleteInfo(); |
114 |
|
✗ |
if (!retval) return false; |
115 |
|
|
|
116 |
|
✗ |
int fd = ConnectSocket(instance_info.socket_path); |
117 |
|
✗ |
if (fd < 0) { |
118 |
|
✗ |
if (errno == ENOENT) { |
119 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, |
120 |
|
|
"Seems like CernVM-FS is not running in %s (not found: %s)", |
121 |
|
|
instance_info.workspace.c_str(), |
122 |
|
|
instance_info.socket_path.c_str()); |
123 |
|
|
} else { |
124 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, "Could not access %s (%d - %s)", |
125 |
|
|
instance_info.identifier.c_str(), errno, strerror(errno)); |
126 |
|
|
} |
127 |
|
✗ |
return false; |
128 |
|
|
} |
129 |
|
|
|
130 |
|
✗ |
WritePipe(fd, command.data(), command.size()); |
131 |
|
✗ |
retval = ReadResponse(fd); |
132 |
|
✗ |
close(fd); |
133 |
|
|
|
134 |
|
✗ |
if (!retval) { |
135 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, "Broken connection to %s (%d - %s)", |
136 |
|
|
instance_info.identifier.c_str(), errno, strerror(errno)); |
137 |
|
✗ |
return false; |
138 |
|
|
} |
139 |
|
✗ |
return true; |
140 |
|
|
} |
141 |
|
|
|
142 |
|
|
|
143 |
|
✗ |
static void Usage(const std::string &exe) { |
144 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStdout, |
145 |
|
|
"Usage: %s [-i instance | -p socket] <command> \n" |
146 |
|
|
" By default, iterate through all instances defined in \n" |
147 |
|
|
" CVMFS_REPOSITORIES \n" |
148 |
|
|
"\n" |
149 |
|
|
"Example: \n" |
150 |
|
|
" %s -i atlas.cern.ch pid \n" |
151 |
|
|
"\n" |
152 |
|
|
"Commands: \n" |
153 |
|
|
" tracebuffer flush flushes the trace buffer to disk \n" |
154 |
|
|
" cache instance describes the active cache manager \n" |
155 |
|
|
" cache size gets current size of file cache \n" |
156 |
|
|
" cache limit get gets current size limit of the file cache\n" |
157 |
|
|
" cache limit set <MB> sets the max size limit of the file cache\n" |
158 |
|
|
" cache list gets files in cache \n" |
159 |
|
|
" cache list pinned gets pinned file catalogs in cache \n" |
160 |
|
|
" cache list catalogs gets all file catalogs in cache \n" |
161 |
|
|
" cleanup <MB> cleans file cache until size <= <MB> \n" |
162 |
|
|
" cleanup rate <period> n.o. cleanups in the last <period> min \n" |
163 |
|
|
" evict <path> removes <path> from the cache \n" |
164 |
|
|
" pin <path> pins <path> in the cache \n" |
165 |
|
|
" mountpoint returns the mount point \n" |
166 |
|
|
" device id returns major:minor virtual device id \n" |
167 |
|
|
" on Linux and 0:0 on macOS \n" |
168 |
|
|
" remount [sync] look for new catalogs \n" |
169 |
|
|
" revision gets the repository revision \n" |
170 |
|
|
" max ttl info gets the maximum ttl \n" |
171 |
|
|
" max ttl set <minutes> sets the maximum ttl \n" |
172 |
|
|
" nameserver get get the DNS server \n" |
173 |
|
|
" nameserver set <host> sets a DNS server \n" |
174 |
|
|
" host info get host chain and their rtt, \n" |
175 |
|
|
" if already probed \n" |
176 |
|
|
" host probe orders the host chain according to rtt \n" |
177 |
|
|
" host probe geo let Stratum 1s order the host chain and \n" |
178 |
|
|
" fallback proxies using the Geo-API \n" |
179 |
|
|
" host switch switches to the next host in the chain \n" |
180 |
|
|
" host set <host list> sets a new host chain \n" |
181 |
|
|
" proxy info gets load-balance proxy groups \n" |
182 |
|
|
" proxy rebalance randomly selects a new proxy server \n" |
183 |
|
|
" from the current load-balance group \n" |
184 |
|
|
" proxy group switch switches to the next load-balance \n" |
185 |
|
|
" proxy group in the chain \n" |
186 |
|
|
" proxy set <proxy list> sets a new chain of load-balance proxy \n" |
187 |
|
|
" groups (not including fallback proxies) \n" |
188 |
|
|
" proxy fallback <list> sets a new list of fallback proxies \n" |
189 |
|
|
" external host info gets info about external host chain \n" |
190 |
|
|
" external host switch switches to the next external host \n" |
191 |
|
|
" external host set \n" |
192 |
|
|
" <host list> sets external host chain \n" |
193 |
|
|
" external proxy info gets info about external proxy groups \n" |
194 |
|
|
" external proxy set \n" |
195 |
|
|
" <proxy list> sets chain of external proxy groups \n" |
196 |
|
|
" timeout info gets the network timeouts \n" |
197 |
|
|
" timeout set \n" |
198 |
|
|
" <proxy> <direct> sets the network timeouts in seconds \n" |
199 |
|
|
" pid gets the pid \n" |
200 |
|
|
" pid cachemgr gets the pid of the shared cache manager \n" |
201 |
|
|
" pid watchdog gets the pid of the crash handler process\n" |
202 |
|
|
" parameters dumps the effective parameters \n" |
203 |
|
|
" reset error counters resets the counter for I/O errors \n" |
204 |
|
|
" hotpatch history shows timestamps and version info of \n" |
205 |
|
|
" loaded (hotpatched) Fuse modules \n" |
206 |
|
|
" version gets cvmfs version \n" |
207 |
|
|
" version patchlevel gets cvmfs patchlevel \n" |
208 |
|
|
" open catalogs shows information about currently \n" |
209 |
|
|
" loaded catalogs (_not_ all cached ones) \n" |
210 |
|
|
" latency show the latencies of different fuse \n" |
211 |
|
|
" calls (requires CVMFS_INSTRUMENT_FUSE) \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 |
|
✗ |
bool retval = SendCommand(command, instance_info); |
268 |
|
✗ |
if (!retval) retcode = 1; |
269 |
|
|
} |
270 |
|
✗ |
if (is_empty_repo_list) { |
271 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStdout, |
272 |
|
|
"Warning: no instance was specified. In this case, the command " |
273 |
|
|
"is executed for all instances defined in CVMFS_REPOSITORIES but" |
274 |
|
|
" this list is empty. Did you mean running \n\n" |
275 |
|
|
" cvmfs_talk -i <repository name> <command>\n"); |
276 |
|
|
} |
277 |
|
✗ |
} else { |
278 |
|
✗ |
bool retval = SendCommand(command, instance_info); |
279 |
|
✗ |
if (!retval) retcode = 1; |
280 |
|
|
} |
281 |
|
✗ |
return retcode; |
282 |
|
|
} |
283 |
|
|
|