| 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() { return !socket_path.empty() || !instance_name.empty(); } |
| 24 |
|
|
|
| 25 |
|
|
// Called at most once by DeterminePath |
| 26 |
|
✗ |
static std::string GetDefaultDomain() { |
| 27 |
|
✗ |
std::string result; |
| 28 |
|
✗ |
BashOptionsManager options_mgr; |
| 29 |
|
✗ |
options_mgr.ParseDefault(""); |
| 30 |
|
✗ |
const bool retval = options_mgr.GetValue("CVMFS_DEFAULT_DOMAIN", &result); |
| 31 |
|
✗ |
if (!retval) { |
| 32 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, |
| 33 |
|
|
"Error: could not determine CVMFS_DEFAULT_DOMAIN"); |
| 34 |
|
|
} |
| 35 |
|
✗ |
return result; |
| 36 |
|
|
} |
| 37 |
|
|
|
| 38 |
|
✗ |
bool DeterminePaths() { |
| 39 |
|
✗ |
std::string fqrn = instance_name; |
| 40 |
|
✗ |
if (fqrn.find('.') == std::string::npos) { |
| 41 |
|
✗ |
static const std::string default_domain = GetDefaultDomain(); |
| 42 |
|
✗ |
fqrn = fqrn + "." + default_domain; |
| 43 |
|
|
} |
| 44 |
|
|
|
| 45 |
|
✗ |
BashOptionsManager options_mgr; |
| 46 |
|
✗ |
options_mgr.ParseDefault(fqrn); |
| 47 |
|
✗ |
if (!options_mgr.GetValue("CVMFS_WORKSPACE", &workspace)) { |
| 48 |
|
✗ |
if (!options_mgr.GetValue("CVMFS_CACHE_DIR", &workspace)) { |
| 49 |
|
✗ |
const bool retval = options_mgr.GetValue("CVMFS_CACHE_BASE", |
| 50 |
|
|
&workspace); |
| 51 |
|
✗ |
if (!retval) { |
| 52 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, |
| 53 |
|
|
"CVMFS_WORKSPACE, CVMFS_CACHE_DIR, and CVMFS_CACHE_BASE " |
| 54 |
|
|
"missing"); |
| 55 |
|
✗ |
return false; |
| 56 |
|
|
} |
| 57 |
|
|
|
| 58 |
|
✗ |
std::string optarg; |
| 59 |
|
✗ |
if (options_mgr.GetValue("CVMFS_SHARED_CACHE", &optarg) |
| 60 |
|
✗ |
&& options_mgr.IsOn(optarg)) { |
| 61 |
|
✗ |
workspace += "/shared"; |
| 62 |
|
|
} else { |
| 63 |
|
✗ |
workspace += "/" + fqrn; |
| 64 |
|
|
} |
| 65 |
|
|
} |
| 66 |
|
|
} |
| 67 |
|
|
|
| 68 |
|
✗ |
socket_path = workspace + "/cvmfs_io." + fqrn; |
| 69 |
|
✗ |
return true; |
| 70 |
|
|
} |
| 71 |
|
|
|
| 72 |
|
✗ |
bool CompleteInfo() { |
| 73 |
|
✗ |
assert(IsDefined()); |
| 74 |
|
|
|
| 75 |
|
✗ |
if (socket_path.empty()) { |
| 76 |
|
✗ |
const bool retval = DeterminePaths(); |
| 77 |
|
✗ |
if (!retval) |
| 78 |
|
✗ |
return false; |
| 79 |
|
✗ |
identifier = "instance '" + instance_name + "' active in " + workspace; |
| 80 |
|
|
} else { |
| 81 |
|
✗ |
workspace = GetParentPath(socket_path); |
| 82 |
|
✗ |
identifier = "instance listening at " + socket_path; |
| 83 |
|
|
} |
| 84 |
|
✗ |
return true; |
| 85 |
|
|
} |
| 86 |
|
|
|
| 87 |
|
|
std::string socket_path; |
| 88 |
|
|
std::string instance_name; |
| 89 |
|
|
std::string workspace; |
| 90 |
|
|
std::string identifier; |
| 91 |
|
|
}; |
| 92 |
|
|
|
| 93 |
|
|
|
| 94 |
|
✗ |
static bool ReadResponse(int fd) { |
| 95 |
|
✗ |
std::string line; |
| 96 |
|
|
char buf; |
| 97 |
|
|
int retval; |
| 98 |
|
✗ |
while ((retval = read(fd, &buf, 1)) == 1) { |
| 99 |
|
✗ |
if (buf == '\n') { |
| 100 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStdout, "%s", line.c_str()); |
| 101 |
|
✗ |
line.clear(); |
| 102 |
|
✗ |
continue; |
| 103 |
|
|
} |
| 104 |
|
✗ |
line.push_back(buf); |
| 105 |
|
|
} |
| 106 |
|
✗ |
return retval == 0; |
| 107 |
|
|
} |
| 108 |
|
|
|
| 109 |
|
|
|
| 110 |
|
✗ |
bool SendCommand(const std::string &command, InstanceInfo instance_info) { |
| 111 |
|
✗ |
bool retval = instance_info.CompleteInfo(); |
| 112 |
|
✗ |
if (!retval) |
| 113 |
|
✗ |
return false; |
| 114 |
|
|
|
| 115 |
|
✗ |
const int fd = ConnectSocket(instance_info.socket_path); |
| 116 |
|
✗ |
if (fd < 0) { |
| 117 |
|
✗ |
if (errno == ENOENT) { |
| 118 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, |
| 119 |
|
|
"Seems like CernVM-FS is not running in %s (not found: %s)", |
| 120 |
|
|
instance_info.workspace.c_str(), |
| 121 |
|
|
instance_info.socket_path.c_str()); |
| 122 |
|
|
} else { |
| 123 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, "Could not access %s (%d - %s)", |
| 124 |
|
|
instance_info.identifier.c_str(), errno, strerror(errno)); |
| 125 |
|
|
} |
| 126 |
|
✗ |
return false; |
| 127 |
|
|
} |
| 128 |
|
|
|
| 129 |
|
✗ |
WritePipe(fd, command.data(), command.size()); |
| 130 |
|
✗ |
retval = ReadResponse(fd); |
| 131 |
|
✗ |
close(fd); |
| 132 |
|
|
|
| 133 |
|
✗ |
if (!retval) { |
| 134 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStderr, "Broken connection to %s (%d - %s)", |
| 135 |
|
|
instance_info.identifier.c_str(), errno, strerror(errno)); |
| 136 |
|
✗ |
return false; |
| 137 |
|
|
} |
| 138 |
|
✗ |
return true; |
| 139 |
|
|
} |
| 140 |
|
|
|
| 141 |
|
|
|
| 142 |
|
✗ |
static void Usage(const std::string &exe) { |
| 143 |
|
✗ |
LogCvmfs( |
| 144 |
|
|
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 |
|
|
" metrics prometheus outputs all metrics in Prometheus format \n" |
| 213 |
|
|
"\n", |
| 214 |
|
|
exe.c_str(), exe.c_str()); |
| 215 |
|
|
} |
| 216 |
|
|
|
| 217 |
|
|
|
| 218 |
|
✗ |
int main(int argc, char *argv[]) { |
| 219 |
|
✗ |
InstanceInfo instance_info; |
| 220 |
|
✗ |
std::string command; |
| 221 |
|
|
|
| 222 |
|
|
int c; |
| 223 |
|
|
// 's' for socket would have been a better option letter but we keep 'p' |
| 224 |
|
|
// for backwards compatibility. The '+' at the beginning of the option string |
| 225 |
|
|
// prevents permutation of the option and non-option arguments. |
| 226 |
|
✗ |
while ((c = getopt(argc, argv, "+hi:p:")) != -1) { |
| 227 |
|
✗ |
switch (c) { |
| 228 |
|
✗ |
case 'h': |
| 229 |
|
✗ |
Usage(argv[0]); |
| 230 |
|
✗ |
return 0; |
| 231 |
|
✗ |
case 'p': |
| 232 |
|
✗ |
instance_info.socket_path = optarg; |
| 233 |
|
✗ |
break; |
| 234 |
|
✗ |
case 'i': |
| 235 |
|
✗ |
instance_info.instance_name = optarg; |
| 236 |
|
✗ |
break; |
| 237 |
|
✗ |
case '?': |
| 238 |
|
|
default: |
| 239 |
|
✗ |
Usage(argv[0]); |
| 240 |
|
✗ |
return 1; |
| 241 |
|
|
} |
| 242 |
|
|
} |
| 243 |
|
|
|
| 244 |
|
✗ |
for (; optind < argc; ++optind) { |
| 245 |
|
✗ |
command += argv[optind]; |
| 246 |
|
✗ |
if (optind < (argc - 1)) |
| 247 |
|
✗ |
command.push_back(' '); |
| 248 |
|
|
} |
| 249 |
|
✗ |
if (command.empty()) { |
| 250 |
|
✗ |
Usage(argv[0]); |
| 251 |
|
✗ |
return 1; |
| 252 |
|
|
} |
| 253 |
|
|
|
| 254 |
|
✗ |
int retcode = 0; |
| 255 |
|
✗ |
if (!instance_info.IsDefined()) { |
| 256 |
|
✗ |
BashOptionsManager options_mgr; |
| 257 |
|
✗ |
options_mgr.ParseDefault(""); |
| 258 |
|
✗ |
std::string opt_repos; |
| 259 |
|
✗ |
options_mgr.GetValue("CVMFS_REPOSITORIES", &opt_repos); |
| 260 |
|
✗ |
std::vector<std::string> repos = SplitString(opt_repos, ','); |
| 261 |
|
✗ |
bool is_empty_repo_list = true; |
| 262 |
|
✗ |
for (unsigned i = 0; i < repos.size(); ++i) { |
| 263 |
|
✗ |
if (repos[i].empty()) |
| 264 |
|
✗ |
continue; |
| 265 |
|
✗ |
is_empty_repo_list = false; |
| 266 |
|
✗ |
instance_info.instance_name = repos[i]; |
| 267 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStdout, "%s:", repos[i].c_str()); |
| 268 |
|
✗ |
const bool retval = SendCommand(command, instance_info); |
| 269 |
|
✗ |
if (!retval) |
| 270 |
|
✗ |
retcode = 1; |
| 271 |
|
|
} |
| 272 |
|
✗ |
if (is_empty_repo_list) { |
| 273 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStdout, |
| 274 |
|
|
"Warning: no instance was specified. In this case, the command " |
| 275 |
|
|
"is executed for all instances defined in CVMFS_REPOSITORIES but" |
| 276 |
|
|
" this list is empty. Did you mean running \n\n" |
| 277 |
|
|
" cvmfs_talk -i <repository name> <command>\n"); |
| 278 |
|
|
} |
| 279 |
|
✗ |
} else { |
| 280 |
|
✗ |
const bool retval = SendCommand(command, instance_info); |
| 281 |
|
✗ |
if (!retval) |
| 282 |
|
✗ |
retcode = 1; |
| 283 |
|
|
} |
| 284 |
|
✗ |
return retcode; |
| 285 |
|
|
} |
| 286 |
|
|
|